Chapters – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Wed, 06 Oct 2021 20:58:51 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 Chapters – CSS-Tricks https://css-tricks.com 32 32 45537868 Full Book Raw https://css-tricks.com/books/greatest-css-tricks/full-book-raw/ Mon, 03 May 2021 23:27:31 +0000 https://css-tricks.com/?post_type=chapters&p=339863


Full Book Raw originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

Full Book Raw originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
339863
Scroll Animation https://css-tricks.com/books/greatest-css-tricks/scroll-animation/ Mon, 22 Jun 2020 13:29:21 +0000 https://css-tricks.com/?post_type=chapters&p=313469 There are some scroll animations that are possible in CSS without any JavaScript at all. Just look at the chapter on the Scroll Indicator, which is clearly CSS magic. But we can do a lot of scroll animation work directly in CSS with just one little bit of information provided by JavaScript: how far the page has scrolled.


Scroll Animation originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
So let’s get that out of the way. With a JavaScript one-liner, we can set a CSS custom property that knows the percentage of the page scrolled:

window.addEventListener('scroll', () => {
  document.body.style.setProperty('--scroll', window.pageYOffset / (document.body.offsetHeight - window.innerHeight));
}, false);

Now we have --scroll as a value we can use in the CSS.

This trick comes by way of Scott Kellum who is quite the CSS trickery master!

Let’s set up an animation without using that value at first. This is a simple spinning animation for an SVG element that will spin and spin forever:

svg {
  display: inline-block;
  animation: rotate 1s linear infinite;
}

@keyframes rotate {
  to {
    transform: rotate(360deg);
  }
}

Here comes the trick! Now let’s pause this animation. Rather than animate it over a time period, we’ll animate it via the scroll position by adjusting the animation-delay as the page scrolls. If the animation-duration is 1s, that means scrolling the whole length of the page. is one iteration of the animation.

svg {
  position: fixed; /* make sure it stays put so we can see it! */

  animation: rotate 1s linear infinite;
  animation-play-state: paused;
  animation-delay: calc(var(--scroll) * -1s);
}

Try changing the animation-duration to 0.5s. That allows for two complete animation cycles as the page is scrolled down with the animation-delay math.

Scott noted in his original demo that also setting…

animation-iteration-count: 1;
animation-fill-mode: both;

… accounted for some “overshoot” weirdness and I can attest that I’ve seen it too, particularly on short viewports, so it’s worth setting these too.

Scott also set the scroll-related animation properties on the :root {} itself, meaning that it could control all the animations on the page at once. Here’s his demo that controls three animations simultaneously:


Scroll Animation originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
313469
Pin Scrolling to Bottom https://css-tricks.com/books/greatest-css-tricks/pin-scrolling-to-bottom/ Thu, 04 Jun 2020 18:17:32 +0000 https://css-tricks.com/?post_type=chapters&p=311167 In perhaps in the top 5 most annoying things a website can do is this. You're trying to read something (or click something!) and all the sudden the page shift underneath you (or you mis-click!). There is a CSS property that can help fix that. Plus, we can exploit it to do something that was previously exclusively in the realm of JavaScript.


Pin Scrolling to Bottom originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The overflow-anchor property in CSS is relatively new, first debuting in 2017 with Chrome¹, Firefox in 2019, and now Edge picking it up with the Chrome transition in 2020. Fortunately, its use is largely an enhancement. The idea is that browsers really try to not to allow position shifting by default. Then if you don’t like how it’s handling that, you can turn it off with overflow-anchor. So generally, you never touch it.

But as you might suspect, we can exploit this little beauty for a little CSS trickery. We can force a scrolling element to stay pinned down to the bottom even as we append new content.

Chat is a classic example of pin-to-bottom scrolling.

We expect this behavior in a UI like Slack, where if we’re scrolled down to the most recent messages in a channel, when new messages arrive, they are visible at the bottom immediately, we don’t have to manually re-scroll down to see them.

This feature comes by way of Ryan Hunt who also credits Nicolas Chevobbe.

As Ryan says:

Have you ever tried implementing a scrollable element where new content is being added and you want to pin the user to the bottom? It’s not trivial to do correctly.

At a minimum, you’ll need to detect when new content is added with JavaScript and force the scrolling element to the bottom. Here’s a technique using MutationObserver in JavaScript to watch for new content and forcing the scrolling:

const scrollingElement = document.getElementById("scroller");

const config = { childList: true };

const callback = function (mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      window.scrollTo(0, document.body.scrollHeight);
    }
  }
};

const observer = new MutationObserver(callback);
observer.observe(scrollingElement, config);

Here’s a demo of that.

But I find a CSS-only solution far more enticing! The version above has some UX pitfalls we’ll cover later.

The trick here is that browsers already do scroll anchoring by default. But what browsers are trying to do is not shift the page on you. So when new content is added that might shift the visual position of the page, they try to prevent that from happening. In this unusual circumstance, we sort of want the opposite. We want the page to be stuck at the bottom of the page and have the visual page visually move, because it is forced to to remain stuck to the bottom.

Here’s how the trick works. First some HTML within a scrolling parent element:

<div id="scroller">
  <!-- new content dynamically inserted here -->
  <div id="anchor"></div>
</div>

All elements naturally have a overflow-anchor: auto; on them which means they attempt to prevent that page shifting when they are on the screen, but we can turn that off with overflow-anchor: none;. So the trick is to target all that dynamically inserted content and turn it off:

#scroller * {
  overflow-anchor: none;
}

Then force only that anchor element to have the scroll anchoring, nothing else:

#anchor {
  overflow-anchor: auto;
  height: 1px;
}

Now once that anchor is visible on the page, the browser will be forced to pin that scroll position to it, and since it is the very last thing in that scrolling element, remain pinned to the bottom.

Here we go!

There are two little caveats here…

  1. Notice the anchor must have some size. We’re using height here to make sure it’s not a collapsed/empty element with no size, which would prevent this from working.
  2. In order for scroll anchoring to work, the page must be scrolled at least once to begin with.

That second one is harder. One option is to not deal with it at all, you could just wait for the user to scroll to the bottom, and the effect works from there on out. It’s kind of nice actually, because if they scroll away from the bottom, the effect stops working, which is what you want. In the JavaScript version above, note how it forces you to the bottom even when you try to scroll up, that’s what Ryan meant by this not being trivial to do correctly.

If you need the effect kicked off immediately, the trick is to have the scrolling element be scrollable immediately, for example:

body {
  height: 100.001vh;
}

And then trigger a very slight scroll right away:

document.scrollingElement.scroll(0, 1);

And that should do the trick. Those lines are available in the demo above to uncomment and try.

  1. Speaking of Chrome, Google takes this layout shifting stuff very seriously. One of the Web Core Vitals is Cumulative Layout Shift (CLS), which measures visual stability. Get a bad CLS score, and it literally impacts your SEO.


Pin Scrolling to Bottom originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
311167
Squigglevision https://css-tricks.com/books/greatest-css-tricks/squigglevision/ Fri, 15 May 2020 16:20:37 +0000 https://css-tricks.com/?post_type=chapters&p=311126 Funny word, right? It's a visual effect that makes an element kinda wiggle squiggle jiggle in place giving it a sort of uneasy, but extremely unique feel.


Squigglevision originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Squigglevision a (real!) term for animation where the lines appear to squirm around, even when the object/scene is at rest. It’s part of the iconic look of shows like Dr. Katz, remember that?

Quite a unique look! It’s even patented. But the patent talks about five edited images and displaying them in “rapid succession”. Wikipedia:

In order to create the line oscillation effects that characterize Squigglevision, Tom Snyder Productions’ animators loop five slightly different drawings in a sequence called a flic.

On the web, if we had to animate five (or more) images in rapid success, we’d probably do it with a step()-based @keyframes animation and a sprite sheet. Here’s a great example of that by simuari that shows exactly how it works, with the sprite sheet on top (10 images combined into 1) and the animation below.

But that’s a lot of work! There is a way we can get the wiggle jiggle squiggle on any element without having to hand-craft a bunch of individual images and make bespoke keyframes of specialized sizes to make it work.

The trick?

Rapidly iterated SVG turbulence filters

Whaaaat? Yep, it’s so cool.

I learned this trick from David Khourshid who made a wonderful demo, Alex the CSS Husky (look below), where the squiggling wasn’t even the main feature of the demo! David says he got the trick from Lucas Bebber in another demo I’ll embed below.

(That’s my forked version for a tiny Firefox fix: you can’t display: none; the SVG in Firefox.)

Here’s how a single SVG turbulence filter works. First, you declare it with some inline <svg>:

<svg display="none">
  <defs>
    <filter id="turb">
      <feTurbulence baseFrequency="0.3" numOctaves="2" />
      <feDisplacementMap in="SourceGraphic" scale="20" />
    </filter>
  </defs>
</svg>

Then you can apply it to any HTML element like this:

.filter {
  filter: url("#turb");
}

Here’s a before/after:

That’s a pretty extreme amount of turbulence. Try cranking it down to baseFrequency="0.003" and see a way more subtle version. Hmmm—almost looks like a very slight squiggle, doesn’t it?

The trick is to use just a smidge, make several different ones, then animate between them.

Here are five different turbulence filters, all slightly different from each other, with different ID’s:

<svg>
  <filter id="turbulence-1">
    <feTurbulence type="fractalNoise" baseFrequency="0.001" numOctaves="2" data-filterId="3" />
    <feDisplacementMap xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" scale="25" />
  </filter>

  <filter id="turbulence-2">
    <feTurbulence type="fractalNoise" baseFrequency="0.0015" numOctaves="2" data-filterId="3" />
    <feDisplacementMap xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" scale="25" />
  </filter>

  <filter id="turbulence-3">
    <feTurbulence type="fractalNoise" baseFrequency="0.002" numOctaves="2" data-filterId="3" />
    <feDisplacementMap xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" scale="25" />
  </filter>

  <filter id="turbulence-4">
    <feTurbulence type="fractalNoise" baseFrequency="0.0025" numOctaves="2" data-filterId="3" />
    <feDisplacementMap xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" scale="25" />
  </filter>

  <filter id="turbulence-5">
    <feTurbulence type="fractalNoise" baseFrequency="0.003" numOctaves="2" data-filterId="3" />
    <feDisplacementMap xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" scale="25" />
  </filter>

</svg>

And a CSS keyframes animation to animate between them:

@keyframes squigglevision {
  0% {
    filter: url("#turbulence-1");
  }
  25% {
    filter: url("#turbulence-2");
  }
  50% {
    filter: url("#turbulence-3");
  }
  75% {
    filter: url("#turbulence-4");
  }
  100% {
    filter: url("#turbulence-5");
  }
}

Which you call on anything you wanna squiggle:

.squiggle {
  animation: squigglevision 0.4s infinite alternate;
}

That’s pretty much exactly what’s happening with Alex the CSS Husky, only the filters are even more chill.

Here’s Lucas’ original demo:


Squigglevision originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
311126
Flexible Grids https://css-tricks.com/books/greatest-css-tricks/flexible-grids/ Fri, 08 May 2020 17:40:20 +0000 https://css-tricks.com/?post_type=chapters&p=306989 Perhaps the greatest trick in all of CSS grid is being able to write a column layout that doesn't explicitly declare the number of rows or columns, but automatically creates them based on somewhat loose instructions and the content you provide.


Flexible Grids originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
CSS Grid has a learning curve, like anything else, but after a minute there is a certain clarity to it. You set up a grid (literally columns and rows), and then place things on those rows. The mental model of it, I’d argue, is simpler than flexbox by a smidge.

Here I’ll set up a grid:

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1rem;
}

And now if I had three children to put onto that grid…

<div class="grid">
   <div></div>
   <div></div>
   <div></div>
</div>

They would fall onto that grid perfectly. That’s wonderfully easy, and offers us a ton of control. That 1fr unit can be adjusted as needed. If the first one was 2fr instead, it would take up twice as much room as the other two. If it was 200px, it would exactly that wide. The gap can be widened and narrowed. There are all kinds of tools for alignment and explicit placement and ordering.

Let’s think about something else though for a moment. Say there were only 2 children. Well, they would automatically land in the 1st and 2nd columns, if we weren’t otherwise explicit about where we wanted them to go. Say there were 5 children. Well, the 4th and 5th would move down onto a new row automatically. Yes, rows! Our grid so far has totally ignored rows, they are just implied. There is something intuitive about that. You don’t have to care about rows, they can be automatically created. You can be explicit about them, but you don’t have to be.

Here’s the CSS trick: we can extend that “don’t have to care” fun to columns in addition to rows.

One way to do that is by…

  1. Not setting any grid-template-columns
  2. Change the auto-flow away from the default rows to grid-auto-flow: column;

Now there will be as many columns as there are child elements! Plus you can still use gap, which is nice.

But what we’ve lost here is wrapping. It would be nice if the number of columns were based on how many elements could fit without breaking the width of the parent, then doing that for the rest of the grid.

That leads us to perhaps the most famous and useful code in all of CSS grid:

.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

There is also an auto-fill keyword and they are a bit different, as Sara Soueidan explains.

If you resize this example, you’ll see how the number of columns adjusts:

Also note that the minimum value used here is 200px for each column. That’s just some number that you’d pick that feels good for your content. If that number was, say, 400px instead, you might consider an alteration that allows it to go smaller if the screen itself is smaller than that wide. I first saw this trick from Evan Minto:

grid-template-columns: repeat(auto-fill, minmax(min(10rem, 100%), 1fr));

That’s saying that if 100% width calculates to less than 10rem (otherwise the minimum), then use that instead, making it safer for small-screen layouts.


Flexible Grids originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
306989
Draggable Elements https://css-tricks.com/books/greatest-css-tricks/draggable-elements/ Thu, 16 Apr 2020 23:06:24 +0000 https://css-tricks.com/?post_type=chapters&p=306795 Dragging an element around the screen is something that is pretty firmly in the territory of JavaScript. You'll want access to DOM events like clicks and mouse movement. But we're here to talk CSS trickery so let's get it done in HTML and CSS alone!


Draggable Elements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Just to be clear, even when we pull this off in HTML and CSS, all we’re getting done is making the element draggable around the screen. If you actually need to do something as a result of that dragging, your back in JavaScript territory.

This trick comes by way of Scott Kellum. Scott has done a number of my absolute favorite CSS tricks over the years, like this super simple @keyframes setup that bounces an element off the viewport edges like an old school screensaver, to an impressive Sass-powered parallax technique.

There really just one CSS thing that can help us with click-and-drag, and that’s the browser UI we get on desktop browsers when we use the resize property. Here’s a <div> where we use it (along with overflow: hidden; which is a prereq for it to work):

If you’re looking at the demo a desktop browser, you’ll be able to grab the bottom right corner of that and drag it around.

Now here’s the real trick.

We can put that resizeable element into another container. That container will grow in height naturally as the resizeable element changes height. It will change it width naturally because of width: min-content;.

Now we have a parent element that resizes along with the resizeable element. That matters because we can put other stuff in that parent element that moves along with it. I’ll drop a big ol’ ✖ in there and position it right on top of the resizer, with pointer-events: none; on it so I can still do the resizing:

Now if we make sure the resizing element is hidden via opacity: 0; it appears as if we’ve made a draggable element out of nowhere! We might need to jiggle the numbers a bit to get things lining up, but it’s doable:


Draggable Elements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
306795
Hard Stop Gradients https://css-tricks.com/books/greatest-css-tricks/hard-stop-gradients/ Tue, 07 Apr 2020 14:10:58 +0000 https://css-tricks.com/?post_type=chapters&p=306104 The word "gradient" implies a transition from one color to another color. That's super useful in web design and can create lovely effects. We're going to skip that whole "transition" part though and see what kind of CSS trickery that unlocks.


Hard Stop Gradients originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Here’s an example of “traditional” gradients where colors slowly fade from one to another.

These are all created with CSS gradients.

There is a concept called color-stops with gradients that allow you to control the position of where colors start transitioning from one to the next. Here’s an example where the first color hangs on for most of the way:

Here’s the trick: the color stops can get closer and closer to each other, and actually be at the same point. Meaning that instead of the color transitioning at all, one color stops and the other color can start at an exact point. Here’s a visual explanation of converging color stops:

The bottom example there almost looks like it’s two separate elements with two separate backgrounds, but nope, it’s a single element with hard-stop gradients splitting the space visually. If you needed to make vertical columns and handle their backgrounds all on one parent element, this is a possibility! In fact, because the background will cover the whole area, you don’t have to worry about the elements “stretching” the full height, which made this a great trick when we needed to make columns based on floats or inline-block elements.

Extending the concept of hard stops, we can make a color-striped bar. Here are variations of it made by moving the background-position.

Speaking of stripes, these hard-stop gradients are great for striped backgrounds of any kind. It gets a little easier with repeating gradients (e.g. repeating-linear-gradient()) as you don’t have to fill 100% of the space, you can use pixels and stop where you need to.

There are other types of gradients as well! We can use hard-stops with radial-gradient and repeating-linear-gradient as well!

Notice in that last example, you still see some color fading stuff going on. A hard-stop gradient doesn’t have to be used exclusively. That one has just one hard stop that repeats.

Conical gradients are another prime canidate for hard stop gradients, as when applied into a circle (e.g. border-radius: 50%) they become instant pie charts!


Hard Stop Gradients originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
306104
Editable Style Blocks https://css-tricks.com/books/greatest-css-tricks/editable-style-blocks/ Tue, 31 Mar 2020 14:27:43 +0000 https://css-tricks.com/?post_type=chapters&p=305930 We know that different HTML elements do different things. That's obvious when we can see them. But some HTML elements operate behind the scenes and sometimes we just don't think about them as being related to elements that are intended to be visual. But let's take a little trip into making some elements visual that aren't by default and how weird (and maybe useful!) it can get.


Editable Style Blocks originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
When you see some HTML like:

<p>I'm going to display this text.</p>

That’s pretty intuitive. Like, the browser is going to display that paragraph element of text.

But that paragraph exists in a larger HTML document, like:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My Website</title>
</head>
<body>
  <p>I'm going to display this text.</p>
</body>
</html>

Why doesn’t “My Website” also display like the paragraph does? What’s so different about <title> and <p>. Well, that’s the nature of any code. Different things do different things. But in this case, we can trace why it doesn’t display pretty easily.

If we open this HTML document in a browser an inspect that <title> element, it will show us that the User Agent Stylesheet sets that element to display: none; Well that makes sense! That’s exactly what we use when we want to hide things entirely from sites.

What’s more, the <title> elements parent element, <head>, is also display: none;.

This is where it gets funny.

User Agent styles are very easy to override! Any value that comes from our CSS will override it. So let’s try this:

<style>
  head, title {
    display: block;
  }
</style>

LOLZ, there it is! Just like a paragraph:

And we have just as much control over it as anything else, meaning we can apply perfect styling to it:

And this can get even weirder… See that <style> block that is also in the <head>, we can’t see that for the exact same reason we couldn’t see the <title>, it’s display: none;. We can change that to make it visible also.

While we’re at it, we can make it look like it does in our code editor too by having it respect whitespace and use a monospace font:

head, title, style {
  display: block;
}
style {
  font-family: monospace;
  white-space: pre;
}

Ha! What the heck!

Now we can get one more step weirder. That style block? It can become editable, because that’s a thing any HTML element can do thanks to the contenteditable attribute.

<style contenteditable>
  ...
</style>

Now that visible <style> block can be edited just like it was a <textarea>, and the CSS applies immediately to the document.

Definitely one of the weirdest things HTML and CSS are capable of.

You might call this a CSS Quine (“a self-referential program that can, without any external access, output its own source.”). Alex Sexton published an example of this in 2013 and credits Anne van Kesteren and Mathias Bynens as prior art. I’ve seen Lea Verou use it to do live coding in conference talks!


Editable Style Blocks originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
305930
Scroll Shadows https://css-tricks.com/books/greatest-css-tricks/scroll-shadows/ Mon, 30 Mar 2020 15:35:54 +0000 https://css-tricks.com/?post_type=chapters&p=305914 Perhaps my favorite CSS trick of all time! This one makes use of four layered background gradients that reveal shadows on the top and bottom of containers that scroll to indicate you can scroll in that direction. It's just good UX, and even moreso now than it was in 2012 when it was invented as scrollbar-less UIs are more and more common.


Scroll Shadows originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The idea of scroll shadows makes an absolute ton of sense. When a container is scrolled down, you can see a shadow at the top, which makes it clear you can scroll back up. And if it’s possible to scroll down, there is a shadow down there also, unless you’ve scrolled down all the way.

This might just be my favorite CSS trick of all time. The idea comes by way of Roman Komarov, but then Lea Verou came up with the extra fancy CSS trickery and popularized it.

Scroll shadows are such nice UX, it almost makes you wonder why it’s not a native browser feature, or at least easier to pull off in CSS. You could call them an affordance, an obvious visual cue that scrolling is either possible or complete, that doesn’t require any learning.

Here’s a working example:

It’s a bit of a mind-bender understanding how it works, in part because it uses background-attachment: local; which is a rare thing to use, to say the least. Here’s an attempt:

  1. There are two types of shadows at work here:
    1. Regular shadows
    2. Cover shadows
  2. All of the shadows are created with background gradients. For example, a non-repeatingradial-gradient sized and placed at the center top of the element to look like a shadow.
  3. The cover shadows are placed on top of those regular shadows, by way of the stacking order of multiple backgrounds, and capable of entirely hiding them.
  4. The regular shadows use the default value of background-attachment, which is scroll, which you’ll be familiar with because it’s the way backgrounds normally work in that you don’t really think about it. The backgrounds are just there, positioned in the visible portion of the element, and don’t move around as the element scrolls.
  5. The overflow shadows us the unusual background-attachment: local; which places them at the top and bottom edges of the element factoring in the entire scroll height of the element. They move as the elements scroll position moves.

So imagine this scenario: the element overflows vertically, and it is currently all the way scrolled to the top. Both the top shadow and the top shadow cover are at the top of the element. The cover is on top, hiding the shadow like it’s not there at all. Scroll down a little, and the cover sticks to the very top of the element, now hidden by overflow, so you can’t see the cover anymore and the shadow reveals itself. At the bottom, you’ve been able to see the shadow the whole time because the cover is stuck to the very bottom of the element and the shadow is stuck to the bottom of the visible area. Scroll all the way to the bottom and the cover will overlap the bottom shadow, hiding it. That’s a mouthful, but it all works!

The beauty of it is how it’s just a few lines of code you can apply to a single element to get it done.

.scroll-shadows {
  max-height: 200px;
  overflow: auto;

  background:
    /* Shadow Cover TOP */
    linear-gradient(
      white 30%,
      rgba(255, 255, 255, 0)
    ) center top,
    
    /* Shadow Cover BOTTOM */
    linear-gradient(
      rgba(255, 255, 255, 0), 
      white 70%
    ) center bottom,
    
    /* Shadow TOP */
    radial-gradient(
      farthest-side at 50% 0,
      rgba(0, 0, 0, 0.2),
      rgba(0, 0, 0, 0)
    ) center top,
    
    /* Shadow BOTTOM */
    radial-gradient(
      farthest-side at 50% 100%,
      rgba(0, 0, 0, 0.2),
      rgba(0, 0, 0, 0)
    ) center bottom;
  
  background-repeat: no-repeat;
  background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
  background-attachment: local, local, scroll, scroll;
}

It doesn’t have to work on only white backgrounds either, but it does need to be a flat color.

Here’s a version with the colors abstracted into CSS custom properties:

This can be much more than just a UI/UX nicety, it can be vital for indicating when a container has more stuff in it that can be scrolled to in a situation where the container doesn’t have a scrollbar or any other UI to indicate it can be scrolled. Consider the story Perfectly Cropped from Tyler Hall in which shares how confused his family members are about the sharing panel in iOS 13. It’s entirely not obvious that you can scroll down here in this screenshot.

Other Tricks

Perhaps the shadow could be bigger or stronger depending on how much there is to scroll?

Hakim El Hattab once tweeted an example of this that did a great job of demonstrating.

Note that that demo uses a bit of JavaScript to do its thing. Of course, I’m attracted to the CSS-only version, particularly here as it is so easy to apply to a single element. But there are lots of takes of this with JavaScript, like:

  • This one that inserts and fades the shadows in and out as needed
  • This one using React Hooks
  • This one using the Intersection Observer API

Despite my attraction to only doing this in CSS, there is another reason you might want to reach for a JavaScript-powered solution instead: iOS Safari. Back in June 2019, when iOS 13 shipped, the version of Safari in that (and every version since), this technique fails to work on. I’m actually not 100% sure why. It seems like a bug. It broke at the same time clever CSS-powered parallax broke on iOS Safari. It may have something to do with how they “cache” certain painted layers of sites. It sure would be nice if they would fix it.


Scroll Shadows originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
305914
Perfect Font Fallbacks https://css-tricks.com/books/greatest-css-tricks/perfect-font-fallbacks/ Wed, 25 Mar 2020 13:33:56 +0000 https://css-tricks.com/?post_type=chapters&p=305626 Thankfully, we have a lot more control over font loading these days. We no longer have to force our users to wait for a font to load before showing them text, we can swap it out as the font loads. But that swap can be visually rough with lots of abrupt shifts and reflow. We can fix that though!


Perfect Font Fallbacks originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
When you load custom fonts on the web, a responsible way to do that is to make sure the @font-face declaration uses the property font-display set to a value like swap or optional.

@font-face {
  font-family: 'MyWebFont'; /* Define the custom font name */
  src:  url('myfont.woff2') format('woff2'); /* Define where the font can be downloaded */
  font-display: swap; /* Define how the browser behaves during download */
}

With that in place, there will be no delay for a user loading your page before they see text. That’s great for performance. But it comes with a design tradeoff, the user will see FOUT or “Flash of Unstyled Text”. Meaning they’ll see the page load with one font, the font will load, then the page will flip out that font for the new one, causing a bit of visual disruption and likely a bit of reflow.

This trick is about minimizing that disruption and reflow!

This trick comes by way of Glen Maddern who published a screencast about this at Front End Center who uses Monica Dinculescu’s Font style matcher combined with Bram Stein’s Font Face Observer library.

Let’s say you load up a font from Google Fonts. Here I’ll use Rubik in two weights:

@import url("https://fonts.googleapis.com/css2?family=Rubik:wght@400;900&display=swap");

At the end of that URL, by default, you’ll see &display=swap which is how they make sure font-display: swap; is in the @font-face declaration.

On a slow connection, this is how a simple page with text will load:

First you’ll see the fallback typography, then the custom fonts will load and you’ll see the typography swap to use those.

See the swap? Remember that’s good, in a way, because at least the text is visible to begin with. But the swap is pretty heavy-handed. It feels visually disruptive.

Let’s fix that.

Using Font style matcher tool, we can lay the two fonts on top of each other and see how different Rubik and the fallback font are.

Note I’m using system-ui as the fallback font here. You’ll want to use a classic “web-safe” font for a fallback, like Georgia, Times New Roman, Arial, Tahoma, Verdana, etc. The vast majority of computers have those installed by default so they are safe fallbacks.

In our case, these two fonts have a pretty much identical “x-height” already (note the height of the red and black lowercase letters above). If they didn’t, we’d end up having to tweak the font-size and line-height to match. But thankfully for us, just a tweak to letter-spacing will get them very close.

Adjusting the callback to using letter-spacing: 0.55px; gets them sizing very close!

Now the trick is to give ourselves the ability apply this styling only before the font loads. So let’s make it the default style, then have a body class that tells us the font is loaded and remove the alterations:

body {
  font-family: "Rubik", system-ui, sans-serif;
  letter-spacing: 0.55px;
}
body.font-loaded {
  letter-spacing: 0px;
}

But how do you get that font-loaded class? The Font Face Observer library makes it very easy and cross-browser friendly. With that library in place, it’s a few lines of JavaScript to adjust the class:

const font = new FontFaceObserver("Rubik", {
  weight: 400
});

font.load().then(function() {
  document.body.classList.add("font-loaded");
});

Now see how much smoother and less disruptive the font loading experience is:

That’s a really great trick!

Here’s that minimal demo:

When testing, if you can’t see the swap happen at all, check and make sure you don’t have Rubik installed on your machine already. Or your internet might be just too fast! DevTools can help throttle your connection to be slower for testing:

This can get more intricate as you use multiple fonts and multiple weights of fonts. You can watch for the loading of each one and adjust different classes as they load in, adjusting styles to make sure things reflow as little as possible.


Perfect Font Fallbacks originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
305626
Scroll Indicator https://css-tricks.com/books/greatest-css-tricks/scroll-indicator/ Sun, 15 Mar 2020 14:03:20 +0000 https://css-tricks.com/?post_type=chapters&p=304986 There is a way you can build a progress bar displaying how far a user has scrolled down the page (like a "reading progress indicator") without any JavaScript at all. Just some clever usage of gradients and positioning.


Scroll Indicator originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There is a built-in browser feature for indicating your scroll position. Get this: it’s the scrollbar, and it does a great job. There is even a standardized way to style scrollbars these days.

body {
  --scrollbarBG: #CFD8DC;
  --thumbBG: #90A4AE;

  scrollbar-width: thin;
  scrollbar-color: var(--thumbBG) var(--scrollbarBG);
}

You might want to combine those with -webkit- styles for the best browser support. For example:

But let’s say you weren’t as interested in styling the scrollbar as you were building your own indicator to show the user how far down they’ve scrolled. Like a progress bar that fills up as you approach the end of reading an article.

Mike Riethmuller found a way to do it that is extraordinarily clever!

It’s not only clever but is done with remarkably little code. To understand, let’s remove the white backgrounds on the header and the pseudo-element on the body revealing the linear-gradient used.

Ah ha! A diagonal gradient drawn with a hard stop. We can already see what’s happening here. As the page is scrolled down, the bit of this gradient that you can see becomes more and more filled with blue. The trick then becomes hiding everything but a small strip of this gradient, hence the solid backgrounds on the header and the pseudo-element, placed a few pixels apart.

Perhaps the most clever bit is how the gradient background is sized. You might think it just covers the entire background, but no. If you did that, the scrollbar would never complete because it is at the top of the page and the gradient completes at the bottom of the page. Because of this demos mid-page placement, the gradient needs to complete almost a full viewport-height short of the bottom. That would looke like:

background-size: 100% calc(100% - 100vh);

Except the fixed header size factors in a well, so that needs to be subtracted. In the end, the code appears as if it has quite a few magic numbers in it. But they aren’t quite magical as most of them are related to each other genetically. Here’s a fork that turns them all into custom properties so you can see that.

Why do this?

  • It’s kinda fun.
  • Some browsers don’t have scrollbars at all (think mobile, and “Scroll scrollbars only when scrolling” setting on macOS).

If you want to do something really fancy, like display the percentage of how far you’ve scrolled through the page, or even fancier like showing an estimated reading time that is programmatically calculated, well, that’s all doable, but you’re in JavaScript territory.


Scroll Indicator originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
304986
Yellow Flash https://css-tricks.com/books/greatest-css-tricks/the-yellow-flash/ Mon, 17 Feb 2020 18:26:39 +0000 https://css-tricks.com/?post_type=chapters&p=303153 When a hash-link is used on a website, the page scrolls to the position where the element being linked to is visible. But is that entirely clear? Sometimes it's jarring and confusing. The Yellow Flash is an idea to highlight the element being linked to briefly to guide the eye to it and make sense of a confusing situation.


Yellow Flash originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’ll get to the Yellow Flash, but it all has to do with on-page scroll position and understanding where you are. These days, CSS alone can animate the scroll position on a page. It’s a one-liner!

html {
  scroll-behavior: smooth;
}

To some degree, this is an aesthetic choice. When a page scrolls from one position to the next (because of, say, a “jump link” to another ID on the page), rather than instantly jumping there the page smoothly scrolls there. Beyond aesthetics, the point of using this might be to help a user understand context. Oh I see, the page scrolled from here to here, and where it stopped represents where I linked to.

But a smooth scroll isn’t the only way to emphasize that the context of linking elsewhere on the same page. We can also do with a visual changed to the targetted area, and I’d argue it’s most clear that way.

Fortunately, there is a CSS pseudo-selector that is perfectly suited to the job of styling elements when they’ve been on-page linked to. The :target selector, which can be used on any element:

section:target {
  background: yellow;
}

What that means is: When the #hash in the URL matches the #id of this element, this selector matches and style it like this.

So if there is an element like…

<section id="footnotes">

</section>

And the URL happens to be:

https://website.com/#footnotes

That selector matches and we’ll see it have a yellow background.

That URL would occur when a link is clicked like:

<a href="/#footnotes">Jump to Footnotes</a>

There is a trick here though, of course. A yellow background alone (or any other static styling) doesn’t exactly scream you just linked here! A little animation can go a long way here. If you don’t just set the background of the linked-to element to yellow but instead flash a background of yellow just temporarily, it’s enough to draw the eye and make the action very clear.

Here is an example and demo. Say you have a link to a footnote at the bottom of an article. This is especially interesting because a link to the bottom of a page is especially hard to draw attention to (there may not be enough scrolling room to get the footnote to the top of the page).

<p>
  Lorem ipsum<sup class="footnote" id="footnote-top-1">
    <a href="#footnote-bottom-1">1</a></sup>
  dolor sit amet consectetur adipisicing elit.
</p>

Then at the bottom of the page, the actual footnote you’re linking to:

<div id="footnotes" class="footnotes">
  <ol>
    <li id="footnote-bottom-1">Lorem ipsum is Greek. 
      <a href="#footnote-top-1">
        <span class="screen-reader-text">Back to reference</span>
        ↥
      </a></li>
  </ol>
</div>

Note that both of the anchor links here are using jump links, footnote-bottom-1 and footnote-top-1 to those respective IDs.

We can make the footnote itself flash as you arrive at it with a @keyframesanimation:

.footnotes :target {
  animation: yellowflash-bg 2s;
}
@keyframes yellowflash-bg {
  from { background: yellow; }
  to   { background: transparent; }
}

In this case, it flashes to yellow immediately, then fades back out to a transparent background over 2 seconds.

Here’s an interactive example:

That’s the Yellow Flash! Of course, it doesn’t have to be yellow and it doesn’t even have to flash. The point is doing something to visually signify what is being linked to for clarity.

That demo above is paired with smooth scrolling, but you might not want to do that, as you can’t control the timing of the smooth scrolling so there is a risk the yellow flash is done by the time you get there.

Hey, make a shake might be fun too.


Yellow Flash originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
303153
A Striped Barberpole Animation https://css-tricks.com/books/fundamental-css-tactics/striped-barberpole-animation/ Fri, 23 Jun 2017 14:05:18 +0000 http://css-tricks.com/?post_type=chapters&p=255868 You can make background stripes in CSS by using linear-gradient. We often think of a gradient as a fade from one color to another, but the trick to stripes is to not have any fade at all. Instead, we …


A Striped Barberpole Animation originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You can make background stripes in CSS by using linear-gradient. We often think of a gradient as a fade from one color to another, but the trick to stripes is to not have any fade at all. Instead, we can specify “color stops” at the same location, such that the color changes instantly from one to the next.

Then the trick to make this even easier is using repeating-linear-gradient so we can just describe the first few stripes and it will naturally repeat:

.element {
  background: repeating-linear-gradient(
    45deg,
    #606dbc,
    #606dbc 10px,
    #465298 10px,
    #465298 20px
  );
}

To animate the stripes in a barberpole fashion, it’s a matter of animating the background-position. This is also just a smidge tricky. If the size of your element doesn’t match the size of the repeating stripes, moving the background-position might result in some awkward stripes like these:

Rather than trying to match up these sizes perfectly, it’s easier to blow up the background-position to 200% and then animate its position by 100%.

div {
  background-image: 
    repeating-linear-gradient(
      -45deg, 
      transparent, 
      transparent 1rem,
      #ccc 1rem,
      #ccc 2rem
    );
  background-size: 200% 200%;
  animation: barberpole 10s linear infinite;
}

@keyframes barberpole {
  100% {
    background-position: 100% 100%;
  }
}

A Striped Barberpole Animation originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
255868
Full Book https://css-tricks.com/books/greatest-css-tricks/full/ Sat, 10 Jun 2017 15:34:20 +0000 http://css-tricks.com/?post_type=chapters&p=255771


Full Book originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

Full Book originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
255771
Shape Morphing https://css-tricks.com/books/greatest-css-tricks/shape-morphing/ Fri, 09 Jun 2017 12:18:36 +0000 http://css-tricks.com/?post_type=chapters&p=255511 There are a number of ways we can animate or transition one shape to another on the web. I'm not talking about rotating an arrow or enlarging a checkmark, I'm talking about watching a shape move the very points it is made from to new places.


Shape Morphing originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There are lots of motion possibilities on the web. You can animate any element’s opacity, color, and transform properties (like translate, scale, and rotate), to name a few, all pretty easily. For example:

.kitchen-sink {
  opacity: 0.5;
  background-color: orange;
  transform: translateX(-100px) scale(1.2) rotate(1deg);
}
.kitchen-sink:hover {
  opacity: 1;
  background-color: black;
  transform: translateX(0) scale(0) rotate(0);
}

By the way, animating the transform and opacity properties are ideal because the browser can do it “cheaply” as they say. It means that the browser has much less work to do to make the movement happen and can take advantage of “hardware acceleration”).

Lesser known is the fact that you can animate the actual shape of elements! I’m not just talking about animating border-radius or moving some pseudo-elements around (although that can certainly be useful to), I mean quite literally morphing the vector shape of an element.

For our first trick, let’s create the vector shape by way of clip-path. We can cut away parts of an element at % coordinates like this:

.moving-arrow {
  width: 200px;
  height: 200px;
  background: red;
  clip-path: polygon(100% 0%, 75% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
}

Clippy is an incredible tool for generating polygon() shape data. Firefox DevTools also has pretty good built-in tooling for manipulating it once it has been applied.

Then we can change that clip-path on some kind of state change. It could be the change of a class, but let’s use :hover here. While we’re at it, let’s add a transition so we can see the shape change!

.moving-arrow {
  ...
  transition: clip-path 0.2s;
  clip-path: polygon(100% 0%, 75% 50%, 100% 100%, 25% 100%, 0% 50%, 25% 0%);
}
.moving-arrow:hover {
  clip-path: polygon(75% 0%, 100% 50%, 75% 100%, 0% 100%, 25% 50%, 0% 0%);
}

Because the polygon() has the exact same number of coordinates, the transition works.

Using clip-path to draw vector shapes is fine, but it’s perhaps not the perfect tool for the job of drawing vector shapes. Drawing vector shapes is really the parlance of SVG. SVG’s elements have attributes designed for drawing. The powerhouse is the element that has its own special syntax for drawing.

You might see a path like this:

<svg viewBox="0 0 20 20">
  <path d="
     M 8 0
     L 12 0
     L 12 8
     L 20 8
     L 20 12
     L 12 12
     L 12 20
     L 8 20
     L 8 12
     L 0 12
     L 0 8
     L 8 8
     L 8 0
  "></path>
</svg>

Which draws a “+” shape.

That value for the d attribute may look like gibberish, but it really just commands the movement of a virtual pen. Move the pen here, draw a line this far in this direction, etc.

In the example above, there are only two commands in use, which you can see from the letters that precede each line:

  • M: Move the pen to these exact coordinates (without drawing)
  • L: Draw a line from the pen’s current coordinates to these exact coordinates

SVG itself has a language for altering those coordinates if we wanted to, including animation. It’s called SMIL, but the big problem with it is that it’s old and was never particularly well supported.

The good news is that some browsers support some of what SMIL can do right in CSS. For example, we can alter the path on :hover in CSS like this:

svg:hover path {
    d: path("
      M 10 0 
      L 10 0
      L 13 7
      L 20 10
      L 20 10
      L 13 13
      L 10 20
      L 10 20
      L 7 13
      L 0 10
      L 0 10
      L 7 7
      L 10 0
    ");
}
path {
  transition: 0.2s;
}

That turns our plus shape into a throwing star shape, and the transition is possible because it’s the same number of points.

If you’re seriously into the idea of morphing shape and want an extremely powerful tool for helping do it, check out Greensock’s MorphSVG plugin. It allows for a ton of control over how the shape morphs and isn’t limited to same-number-of-points transitions.


Shape Morphing originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
255511