Tag: Headers

Sticky Headers: 5 Ways to Make Them Better

Page Laubheimer says that if you’re going to do a sticky header…

  1. Keep it small.
  2. Visually contrast it with the rest of the page.
  3. If it’s going to move, keep it minimal. (I’d say, respect prefers-reduced-motion.)
  4. Consider “partially persistent headers.” (Jemima Abu calls it a Smart Navbar.)
  5. Actually, maybe don’t even do it.

I generally like the term “sticky” header, because it implies you should use position: sticky for them, which I think you should. It used to be done with position: fixed, but that was trickier to pull off since the header would move in-and-out of flow of the document. Using sticky positioning helps reserve that space automatically without JavaScript or magic numbers.

Direct Link to ArticlePermalink


The post Sticky Headers: 5 Ways to Make Them Better appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,

How to Use CSS Grid for Sticky Headers and Footers

CSS Grid is a collection of properties designed to make layout easier than it’s ever been. Like anything, there’s a bit of a learning curve, but Grid is honestly fun to work with once you get the hang of it. One area where it shines is dealing with headers and footers. With a little adjustment in our thinking, we can pull off headers and footers that behave like they are fixed, or have that “sticky” treatment (not position: sticky, but the kind of footer that hugs the bottom of the screen even if there isn’t enough content to push it there, and is pushed away with more content). 

Hopefully this sparks further interest in modern layouts, and if it does, I can’t recommend Rachel Andrew’s book The New CSS Layout strongly enough: it covers both of the major modern layout techniques, grid and flexbox.

What we’re making

Let’s implement a fairly classic HTML layout that consist of a header, main content and footer.

We’ll make a truly fixed footer, one that stays at the bottom of the viewport where the main content scrolls within itself, as needed, then later update the footer to be a more traditional sticky footer that starts at the bottom of the viewport, even if the main content is small, but gets pushed down as needed. Further, to broaden our exposure to grid, let’s design our main content holder so that it can either span the whole width of the viewport, or take up a nicely centered strip down the middle.

A fixed footer is slightly unusual. Footers are commonly designed to start at the bottom of the viewport, and get pushed down by main content as needed. But a persistent footer isn’t unheard of. Charles Schwab does it on their homepage. Either way, it’ll be fun to implement!

But before we move on, feel free to actually peek at the fixed footer implemented on the Charles Schwab site. Unsurprisingly, it uses fixed positioning, which means it has a hard-coded size. In fact, if we crack open DevTools, we see that right off the bat:

body #qq0 {   border-top: 4px solid #133568;   background-color: #eee;   left: 0;   right: 0;   bottom: 0;   height: 40px!important; }

Not only that, but there’s the balance of making sure the main content doesn’t get hidden behind that fixed footer, which it does by setting hard-coded paddings (including 15px on the bottom of the <footer> element), margins (including 20px on <ul> in the footer), and even line breaks.

Let’s try to pull this off without any of these restrictions.  

Our baseline styles

Let’s sketch out a bare minimum UI to get us started, then enhance our grid to match our goals. There’s a CodeSandbox below, plus additional ones for the subsequent steps that get us to the end result.

First, let’s do some prep work. We’ll make sure we’re using the whole height of the viewport, so when we add our grid, it’ll be easy to put the footer at the bottom (and keep it there).  There’s only going to be one element inside the document’s <body> with an ID of #app, which will hold the <header, <main> and <footer> elements.

body {   margin: 0; /* prevents scrollbars */ } 
 #app {   height: 100vh; }

Next, let’s set up our header, main, and footer sections, as well as the grid they’ll all sit in. To be clear, this will not work the way we want right out of the gate. It’s just to get us started, with a base to build from.

body {   margin: 0; } 
 #app {   height: 100vh;      /* grid container settings */   display: grid;   grid-template-columns: 1fr;   grid-template-rows: auto 1fr auto;   grid-template-areas:      'header'     'main'     'footer'; } 
 #app > header {   grid-area: header; } 
 #app > main {   grid-area: main;   padding: 15px 5px 10px 5px; } 
 #app > footer {   grid-area: footer; }

We’ve created a simple one-column layout, with a width of 1fr. If that 1fr is new to you, it essentially means “take the remaining space” which, in this case, is the entire width of the grid container, #app.

We’ve also defined three rows:

#app {   /* etc. */   grid-template-rows: auto 1fr auto;   /* etc. */ }

The first and third rows, which will be our header and footer, respectively, are sized with auto, which means they’ll take up as much space as needed. In other words: no need for hard-coded sizes! This is a super important detail and a perfect example of how we benefit from using CSS Grid.

The middle row is where we’ll put our content. We’ve assigned it a size of 1fr which, again, just means it takes up all of the remaining space that’s left over from the other two rows. If you’re wondering why we aren’t making it auto as well, it’s because the entire grid spans the viewport’s whole height, so we need one section to grow and fill up any unused space. Note that we do not have, nor will we ever need at any point, any fixed heights, margins, paddings — or even line breaks! — to push things into place. Such is the good life when working with grid!

Shall we try some content?

You’ll notice in the Sandbox that I used React to build this demo, but since this isn’t a post about React, I won’t belabor those details; React has absolutely nothing to do with any of the CSS Grid work in this post. I’m only using it as an easy way to navigate between different chunks of markup. If you hate React, that’s fine: hopefully you can ignore it in this post.

We have Header, Main and Footer components that render the expected <header> , <main>  and <footer> elements, respectively. And, of course, this all sits inside our #app container. Yes, in theory, #app should be an <article> element, semantically speaking, but that’s always looked weird to me. I just wanted to covey these details so we’re all one the same page as we plow ahead.

For the actual content, I have Billing and Settings sections that you can navigate between in the header. They both render fake, static content, and are only meant to show our layout in action. The Settings section will be the content that we put in a centered strip on our page, Billing will be the one that spans our whole page.

Here’s the Sandbox with what we have so far.

The Billing section looks good, but the Settings section pushes our footer off screen. Not only that, but if we scroll, the entire page scrolls, causing us to lose our header. That may be desirable in some cases, but we want both the header and footer to stay in view, so let’s fix that.

Fixed header, fixed footer

When we initially set up our grid, we gave it a height of 100vh, which is the entire height of the viewport. We then assigned the rows for the header and footer an auto height, and the main a height of 1fr to take up the remaining space. Unfortunately, when content exceeds the space available, it expanded beyond the viewport bounds, pushing our footer down and out of view.

The fix here is trivial: adding overflow: auto will cause our <main> element to scroll, while keeping our <header> and <footer> elements in place.

#app > main {   grid-area: main;   overflow: auto;   padding: 15px 5px 10px 5px; }

Here’s the updated demo that puts this to use.

Adjustable width main section

We want our <main> element to either span the whole width of the viewport, or be centered in a 600px space. You might think we could simply make <main> a 600px fixed width, with an auto margins on either side. But since this is a post about grid, let’s use moar grid. (Plus, as we’ll see later, a fixed width won’t work anyway).

To achieve our centered 600px element, we’ll actually make the <main> element a grid container. That’s right, a grid within a grid! Nesting grids is a totally legit approach, and will even get easier in the future when subgrid is officially supported across browsers. In this scenario, we’ll make <main> a grid with three column tracks of 1fr 600px 1fr or, stated simply, 600px in the middle, with the remaining space equally divided on the sides.

#app > main {   display: grid;   grid-template-rows: 1fr;   grid-template-columns: 1fr 600px 1fr; }

Now let’s position our the content in the grid. Our different modules all render in a <section> child. Let’s say that by default, content will occupy the middle section, unless it has a .full class, in which case it will span the entire grid width. We won’t use named areas here, and instead specify precise grid coordinates of the form [row-start] / [col-start] / [row-end] / [col-end]:

#app > section {   grid-area: 1 / 2 / 1 / 3; } 
 #app > section.full {   grid-area: 1 / 1 / 1 / 4 }

You might be surprised to see a col-end value of 4, given that there’s only three columns. This is because the column and row values are column and row grid lines. It takes four grid lines to draw three grid columns. 

Our <section> will always be in the first row, which is the only row. By default it’ll span column lines 2 through 3, which is the middle column, unless the section has a full class on it, in which case it’ll span column lines 1 through 4, which is all three columns.

Here’s an updated demo with this code. It’ll probably look good, depending on your CodeSandbox layout, but there’s still a problem. If you shrink the display to smaller than 600px, the content is abruptly truncated. We don’t really want a fixed 600px width in the middle. We want a width of up to 600px. It turns out grid has just the tool for us: the minmax() function. We specify a minimum width and a maximum width, and the grid will compute a value that falls in that range. That’s how we prevent the content from blowing out of the grid.

All we need to do is swap out that 600px value with minmax(0, 600px):

main {   display: grid;   grid-template-rows: 1fr;   grid-template-columns: 1fr minmax(0, 600px) 1fr; }

Here’s the demo for the finished code.

One more approach: The traditional fixed footer

Earlier, we decided to prevent the footer from being pushed off the screen and did that by setting the <main> element’s overflow property to auto.

But, as we briefly called out, that might be a desirable effect. In fact, it’s more of a classic “sticky” footer that solves that annoying issue, and places the footer on the bottom edge of the viewport when the content is super short.

Hey, get back to the bottom!

How could we keep all of our existing work, but allow the footer to get pushed down, instead of fixing itself to the bottom in persistent view?

Right now our content is in a grid with this HTML structure:

<div id="app">   <header />   <main>     <section />   </main>   <footer /> </div>

…where <main> is a grid container nested within the #app grid container, that contains one row and three columns that we use to position our module’s contents, which go in the <section> tag.

 Let’s change it to this:

<div id="app">   <header />   <main>     <section />     <footer />   </main> </div>

…and incorporate <footer> into the <main> element’s grid. We’ll start by updating our parent #app grid so that it now consists of two rows instead of three:

#app {   /* same as before */ 
   grid-template-columns: 1fr;   grid-template-rows: auto 1fr;   grid-template-areas:      'header'     'main'; }

Just two rows, one for the header, and the other for everything else. Now let’s update the grid inside our <main> element:

#app > main {   display: grid;   grid-template-rows: 1fr auto;   grid-template-columns: 1fr minmax(0, 600px) 1fr; }

We’ve introduced a new auto-sized row. That means we now have a 1fr row for our content, that holds our <section>, and an auto row for the footer.

Now we position our <footer> inside this grid, instead of directly in #app:

#app > footer {   grid-area: 2 / 1 / 2 / 4; }

Since <main> is the element that has scrolling, and since this element now has our footer, we’ve achieved the sticky footer we want! This way, if <main> has content that exceeds the viewport, the whole thing will scroll, and that scrolling content will now include our footer, which sits at the very bottom of the screen as we’d expect.

Here’s an updated demo. Note that the footer will be at the bottom of the screen if possible; otherwise it’ll scroll as needed. 

I made a few other small changes, like minor adjustments to paddings here and there; we can’t have any left or right paddings on <main>, because the <footer> would no longer go edge-to-edge.

I also made a last-minute adjustment during final edits to the <section> element—the one we enabled adjustable width content on. Specifically, I set its display to flex, its width to 100%, and its immediate descendant to overflow: auto. I did this so the <section> element’s content can scroll horizontally, within itself, if it exceeds our grid column boundary, but without allowing any vertical scrolling.

Without this change, the work we did would amount to the fixed footer approach we covered earlier. Making section> a flex container forces its immediate child — the <div> that contains the content — to take up all of the available vertical space. And, of course, setting that child div to overflow: auto enables scrolling. If you’re wondering why I didn’t just set the section’s overflow-x to auto, and overflow-y to visible, well, it turns out that’s not possible.

Parting thoughts 

We haven’t done anything revolutionary in this post, and certainly nothing that couldn’t be accomplished before CSS Grid. Our fixed width <main> container could have been a block element with a max-width value of 600px, and auto margins on the left and right. Our fixed footer could have been made with position: fixed (just make sure the main content doesn’t overlap with it). And, of course, there are various ways to get a more traditional “sticky footer.”

But CSS Grid provides a single, uniform layout mechanism to accomplish all of this, and it’s fun to work with — honestly fun. In fact, the idea of moving the footer from fixed to sticky wasn’t even something I planned at first. I threw it in at the last minute because I thought the post was a bit too light without it. It was trivial to accomplish, basically moving grid rows around, not unlike putting lego blocks together. And again, these UIs were trivial. Imagine how brightly grid will shine with more ambitious designs!


The post How to Use CSS Grid for Sticky Headers and Footers appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

On Adding IDs to Headers

Here’s a two-second review. If an element has an ID, you can link to it with natural browser behavior. It’s great if headings have them, because it’s often useful to link directly to a specific section of content.

<h3 id="step-2">Step 2</a>

Should I be so inclined, I could link right to this heading, be it from an URL, like https://my-website.com/#step-2, or an on-page link, like:

<a href="#step-2">Jump to Step 2</a>

So, it’s ideal if all headers have unique IDs.

I find it entirely too much work to manually add IDs to all my headers though. For years and years, I did it like this using jQuery on this very site (sue me):

// Adjust this for targetting the headers important to have IDs const $ headers = $ (".article-content > h3");  $ headers.each((i, el) => {   const $ el = $ (el);    // Probably a flexbox layout style page   if ($ el.has("a").length != 0) {     return;   }    let idToLink = "";    if ($ el.attr("id") === undefined) {     // give it ID     idToLink = "article-header-id-" + i;     $ el.attr("id", idToLink);   } else {     // already has ID     idToLink = $ el.attr("id");   }    const $ headerLink = $ ("<a />", {     html: "#",     class: "article-headline-link",     href: "#" + idToLink   });    $ el.addClass("has-header-link").prepend($ headerLink); });

That script goes one step further than just adding IDs (if it doesn’t already have one) by adding a # link right inside the heading that links to that heading. The point of that is to demonstrate that the headers have IDs, and makes it easy to do stuff like right-click copy-link. Here’s that demo, if you care to see it.

Problem! All the sudden this stopped working.

Not the script itself, that works fine. But the native browser behavior that allows the browser to jump down to the heading when the page loads is what’s busted. I imagine it’s a race condition:

  1. The HTML arrives
  2. The page starts to render
  3. The browser is looking for the ID in the URL to scroll down to
  4. It doesn’t find it…
  5. Oh wait there it is!
  6. Scroll there.

The Oh wait there it is! step is from the script executing and putting that ID on the heading. I really don’t blame browsers for not jumping to dynamically-inserted links. I’m surprised this worked for as long as it did.

It’s much better to have the IDs on the headings by the time the HTML arrives. This site is WordPress, so I knew I could do it with some kind of content filter. Turns out I didn’t even have to bother because, of course, there is a plugin for that: Karolína Vyskočilová‘s Add Anchor Links. Works great for me. It’s technique is that it adds the ID on the anchor link itself, which is also totally fine. I guess that’s another way of avoiding messing with existing IDs.

If I didn’t have WordPress, I would have found some other way to process the HTML server-side to make sure there is some kind of heading link happening somehow. There is always a way. In fact, if it was too weird or cumbersome or whatever to do during the build process or in a server-side filter, I would look at doing it in a service worker. I’ve been having fun playing with Cloudflare’s HTMLRewriter, which is totally capable of this.

The post On Adding IDs to Headers appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]

Adding CSS to a Page via HTTP Headers

Only Firefox supports it, but if you return a request with a header like this:

Header add Link "<style.css>;rel=stylesheet;media=all"

…that will link to that stylesheet without you having to do it in the HTML. Louis Lazaris digs into it:

[…] the only thing I can think of that could justify use for this in production is as a way to include some Firefox-only CSS, which Eric Meyer mentions as a possibility in an old post on this subject. But it’s not guaranteed to always only work in Firefox, so that’s still a problem.

Do with this what you like, but it’s extremely unlikely that this will have any use in a real project.

I appreciate some classic CSS trickery.

Direct Link to ArticlePermalink

The post Adding CSS to a Page via HTTP Headers appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Rotated Table Column Headers… Now With Fewer Magic Numbers!

Rotated <table> column headers is something that’s been covered before right here on CSS-Tricks, so shout-out to that for getting me started and helping me achieve this effect. As the article points out, if you aren’t using trigonometry to calculate your table styles, you’ll have to rely on magic numbers and your table will be brittle and any dreams of responsiveness crushed. 

Fortunately, in this case, we can take the trigonometry out and replace it with some careful geometry and our magic numbers all turn into 0 (a truly magical number).

For those in a hurry, here is the CSS (it’s very similar to the styles in the other article). Below is a thorough walk-through.

<th class="rotate"><div><span>Column Header 1</span></div></th>
table {  border-collapse: collapse;  --table-border-width: 1px; } th.rotate {   white-space: nowrap;   position: relative; 
} th.rotate > div {   /* place div at bottom left of the th parent */   position: absolute;   bottom: 0;   left: 0;   /* Make sure short labels still meet the corner of the parent otherwise you'll get a gap */   text-align: left;   /* Move the top left corner of the span's bottom-border to line up with the top left corner of the td's border-right border so that the border corners are matched    * Rotate 315 (-45) degrees about matched border corners */   transform:      translate(calc(100% - var(--table-border-width) / 2), var(--table-border-width))     rotate(315deg);   transform-origin: 0% calc(100% - var(--table-border-width));   width: 100%; 
} th.rotate > div > span {   /* make sure the bottom of the span is matched up with the bottom of the parent div */   position: absolute;   bottom: 0;   left: 0;   border-bottom: var(--table-border-width) solid gray; } td {   border-right: var(--table-border-width) solid gray;   /* make sure this is at least as wide as sqrt(2) * height of the tallest letter in your font or the headers will overlap each other*/   min-width: 30px;   padding-top: 2px;   padding-left: 5px;   text-align: right; }

Let’s unpack this table and see what’s going on. The magic starts with that funny chain of HTML tags. We’re putting a <span> inside of a <div> inside of our <th>. Is this all really necessary? Between how borders behave, the positioning flexibility we need, and what determines the width of a table column… yes, they each have a purpose and are necessary.

Let’s see what happens if we rotate the <th> directly:

<th class="rotate">Column header 1</th>
table {   border-collapse: collapse; } th.rotate {   border-bottom: 1px solid gray;   transform: rotate(315deg);   white-space: nowrap; } td {   border-right: 1px solid gray;   min-width: 30px;   padding-top: 2px;   padding-left: 5px;   text-align: right; }

Ignoring the fact that we haven’t corrected position, there are two big issues here: 

  1. The column width is still calculated from the header length which is what we were trying to avoid.
  2. Our border didn’t come with us in the rotation, because it is actually part of the table.

These problems aren’t so difficult to fix. We know that if the <th> has a child element with a border, the browser won’t treat that border as part of the table. Further, we know that absolutely-positioned elements are taken out of the document flow and won’t affect the parent’s width. Enter <div> tag, stage left…and right, I guess.

<th class="rotate"><div>Column header 1</div></th>
table {   border-collapse: collapse; } th.rotate {   white-space: nowrap;   position: relative; } th.rotate > div {   position: absolute;   transform: rotate(315deg);   border-bottom: 1px solid gray; } td {   border-right: 1px solid gray;   min-width: 30px;   text-align: right;   padding-top: 2px;   padding-left: 5px; }
Now our headers don’t influence the column width and the borders are rotated. We just need to line things up.

It’s easier to tell in the image with the rotated <th> elements, but that rotation is happening around the center of the element (that’s the default behavior of transform-origin). It is only another transform in x and y to get it to the right spot, but this is where we’d need trigonometry to figure out just how much x and y to line it up with the column borders. If we instead carefully choose the point to rotate the header about, and use transform-origin to select it, then we can end up with distances that are more straightforward than magic numbers.

The animation below helps illustrate what we’re going to do to avoid complicated math. The black dot in the top left of the blue border needs to match the red dot on the right border of the table column and rotate about it. Then there won’t be any gaps between the two borders.

It’s not helpful to start going somewhere if you don’t know where you are. The absolute positioning is going to help us out with this. By specifying bottom: 0; left: 0; on the <div>, it ends up at the bottom left of the parent <th>. This means the <div> border’s bottom-left corner is sitting on top of the left column border and halfway through it. From here, it’s apparent we need to move down one border width and over one cell width, but how are we going to get that responsively? It’s at this very moment you may recall that we haven’t added the <span> yet — we’re going to need it!

We’ll use the <div> to “figure out” how big the table cells are and the <span> to actually hold the text and position it absolutely as well to overflow the parent.

<th class="rotate"><div><span>Column header 1</span></div></th>
th.rotate{   white-space: nowrap;   position: relative; } th.rotate > div {   position: absolute;   bottom: 0;   left: 0;   width: 100%;  /* <- now the div parent is as wide as the columns */ } th.rotate > div > span {   position: absolute;   bottom: 0;   left: 0;   border-bottom: 1px solid gray; }

Great! When we set the width of the <div> to 100%, it holds the information for how big the column is regardless of what the content is in the table cells. With this in place, we can easily translate things over by the width of the <div> — but don’t forget that we need to shave off a half border width. Our translation becomes:

transform: translate( calc( 100% - var(--table-border-width)/2), var(--table-border-width));

The <div> is now in the right spot to rotate, but we have to make sure to pick the correct transform-origin. We want it to be on the top-left corner of the border, which will be on the left and up one border’s width from the bottom of our <div> element:

transform-origin: 0%, calc(100% - var(--table-border-width));

This brings us to our final style for the table header.

table {   border-collapse: collapse;   --table-border-width: 1px; } th.rotate{   white-space: nowrap;   position: relative; } th.rotate > div {   position: absolute;   bottom: 0;   left: 0;   width: 100%;   transform:     translate( calc( 100% - var(--table-border-width)/2), var(--table-border-width));     rotate(315deg);   transform-origin: 0%, calc(100% - var(--table-border-width)); } th.rotate > div > span {   position: absolute;   bottom: 0;   left: 0;   border-bottom: var(--table-border-width) solid gray; }

Note that transformations happen after everything is placed. That means the rotated headers will overflow onto everything as best they can. You will need to wrap the whole table in something to compensate for the unexpected height. I put the title and table together in a flexbox <div> and set the flex-basis of the title to a value large enough to compensate for the tall headers.

#div-with-table {   display: flex;   flex-direction: column;   justify-content: space-around; } #title {   flex-basis: 140px; }

The post Rotated Table Column Headers… Now With Fewer Magic Numbers! appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Fixed Headers and Jump Links? The Solution is scroll-margin-top

The problem: you click a jump link like <a href="#header-3">Jump</a> which links to something like <h3 id="header-3">Header</h3>. That’s totally fine, until you have a position: fixed; header at the top of the page obscuring the header you’re trying to link to!

Fixed headers have a nasty habit of hiding the element you’re trying to link to.

There used to be all kinds of wild hacks to get around this problem. In fact, in the design of CSS-Tricks as I write, I was like, “Screw it, I’ll just have a big generous padding-top on my in-article headers because I don’t mind that look anyway.”

But there is actually a really straightforward way of handling this in CSS now.

h3 {   scroll-margin-top: 5rem; /* whatever is a nice number that gets you past the header */ }

We have an Almanac article on it, which includes browser support, which is essentially everywhere. It’s often talked about in conjunction with scroll snapping, but I find this use case even more practical.

Here’s a simple demo:

In a related vein, that weird (but cool) “text fragments” link that Chrome shipped takes you to the middle of the page instead, which I think is nice.

The post Fixed Headers and Jump Links? The Solution is scroll-margin-top appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Position Sticky and Table Headers

You can’t position: sticky; a <thead>. Nor a <tr>. But you can sticky a <th>, which means you can make sticky headers inside a regular ol’ <table>. This is tricky stuff, because if you didn’t know this weird quirk, it would be hard to blame you. It makes way more sense to sticky a parent element like the table header rather than each individiaul element in a row.

The issue boils down to the fact that stickiness requires position: relative to work and that doesn’t apply to <thead> and <tr> in the CSS 2.1 spec.

There are two very extreme reactions to this, should you need to implement sticky table headers and not be aware of the <th> workaround.

  • Don’t use table markup at all. Instead, use different elements (<div>s and whatnot) and other CSS layout methods to replicate the style of a table, but not locked out of using position: relative and creating position: sticky parent elements.
  • Use table elements, but totally remove all their styling defaults with new display values.

The first is dangerous because you aren’t using semantic and accessible elements for the content to be read and navigated. The second is almost the same. You can go that route, but need to be really careful to re-apply semantic roles.

Anyway, none of that matters if you just stick (get it?!) to using a sticky value on those <th> elements.

See the Pen
Sticky Table Headers with CSS
by Chris Coyier (@chriscoyier)
on CodePen.

It’s probably a bit weird to have table headers as a row in the middle of a table, but it’s just illustrating the idea. I was imagining colored header bars separating players on different sports teams or something.

Anytime I think about data tables, I also think about how tricky it can be to make them responsive. Fortunately, there are a variety of ways, all depending on the best way to group and explore the data in them.

The post Position Sticky and Table Headers appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Fixed Headers, On-Page Links, and Overlapping Content, Oh My!

Let’s take a basic on-page link:

<a href="#section-two">Section Two</a>

When clicked, the browser will scroll itself to the element with that ID: <section id="section-two"></section>. A browser feature as old as browsers themselves, just about.

But as soon as we position: fixed; came into play, it became a bit of an issue. The browser will still jump to bring the newly targeted element into view, but that element may be obscured by a fixed position element, which is pretty bad UX.

I called this “headbutting the browswer window” nearly 10 years ago, and went over some possible solutions. Nicolas Gallager documented five different techniques. I’m even using a fixed position header here in v17 of CSS-Tricks, and I don’t particularly love any of those techniques. I sort of punted on it and added top padding to all my <h3> elements, which is big enough for the header to fit there.

There is a new way though! Finally!

Šime Vidas documented this in Web Platform News. There are a bunch of CSS properties that go together as part of CSS scroll snapping, but it turns out that scroll-padding and scroll-margin can be used outside of a scroll snapping container.

body {   scroll-padding-top: 70px; /* height of sticky header */ }

This only works in Chromium browsers:

See the Pen
Scroll Padding on Fixed Postion Headers
by Chris Coyier (@chriscoyier)
on CodePen.

This is such a useful thing we shoot hoot and holler for WebKit and Firefox to do it.

The post Fixed Headers, On-Page Links, and Overlapping Content, Oh My! appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]