Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Wed, 14 Aug 2024 17:25:38 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 225069128 Relative Color Syntax — Basic Use Cases https://frontendmasters.com/blog/relative-color-syntax-basic-use-cases/ https://frontendmasters.com/blog/relative-color-syntax-basic-use-cases/#comments Mon, 12 Aug 2024 18:38:35 +0000 https://frontendmasters.com/blog/?p=3448 As of last month, Firefox 128’s support of the relative color syntax means we’ve now got support across the board. I’m excited about that as it’s an extremely powerful way to manipulate colors in CSS. Plus it was part of Interop this year so that is further proof that is trucking along nicely.

The syntax with generic names looks like this:

color-function(from origin-color channel1 channel2 channel3 / alpha)

Here’s how it works in my head:

Add Opacity to a Color you Already Have

It’s common to have CSS custom properties set up for colors on a project.

html {
  --color-yellow: oklch(80% 0.15 94);
  --color-green:  oklch(70% 0.25 140);

  ...
}

Now you want to use that yellow, but at about 50% opacity. How do you do that? There are actually a couple of ways to add transparency to an existing color, but in my opinion the relative color syntax is the nicest.

In the past, I’ve split out the color values like this:

html {
  --color-yellow-lch: 80% 0.15 94;
  --color-yellow: oklch(var(--color-yellow-lch);

  ...
}

That way I could either use the color all together, or use the split out values to apply opacity:

.box {
  background: var(--color-yellow);
  border-color: oklch(var(--color-yellow-lch) / 50%);
}

But that can get out of hand! You could also split each color into L, C, and H, the combine those, giving you five variables for every color. Too much.

With the relative color syntax, breaking down colors isn’t necessary. You apply alpha (and other transformations) on demand, leaving the original single color as the only variable (token) you need.

.box {
  background: var(--color-yellow);
  border-color: oklch(from var(--color-yellow) l c h / 50%); 
}

I much prefer the idea of keeping the main colors tokenized as custom properties, then tweaking them as needed on demand.

Darken a Color you Already Have

In the above example, we had --color-yellow and I ended by saying I prefer doing one-off tweaks on demand rather than making a whole new variable. If you have a ton of usage of a slightly-darker version of a color, then sure, make a new variable and stay consistent. But if it’s more of a one-off, relative color syntax is awesome:

.box {
  background: var(--gray-5);

  h2 {
    color: var(--color-yellow);
    /* Darkened version of yellow */
    border-bottom: 2px solid oklch(from var(--color-yellow) calc(l - 0.4) c h);
  }
}

Lighten a Color you Already Have

Same deal here. I’m using OKLCH because I like it, particularly the “uniform brightness” characteristic. Meaning when doing this darkening and lightening across different colors, it feels like it lightens/darkens the same amount. Which feels weird to write, but it’s true. Other color spaces do not lighten and darken consistently.

.box {
  background: var(--gray-5);

  h2 {
    color: var(--color-orange);
    /* Darkened version of orange */
    border-bottom: 2px solid oklch(from var(--color-orange) calc(l + 0.4) c h);
  }
}

Easy Variations

Avoiding making too many variables is a nice consequence of the relative color syntax, but you can still use the relative color syntax to make variables if it’s useful to have them.

I like the idea of starting with a base color, perhaps a slightly tinted gray, and then making the official variations with the relative color syntax.

html {
  --base-gray: oklch(12.94% 0.02 159);
  
  --gray-1: var(--base-gray);
  --gray-2: oklch(from var(--base-gray) calc(l + 0.1) c h);
  --gray-3: oklch(from var(--base-gray) calc(l + 0.2) c h);
  --gray-4: oklch(from var(--base-gray) calc(l + 0.3) c h);
  --gray-5: oklch(from var(--base-gray) calc(l + 0.4) c h);
  --gray-6: oklch(from var(--base-gray) calc(l + 0.5) c h);
  --gray-7: oklch(from var(--base-gray) calc(l + 0.6) c h);
  --gray-8: oklch(from var(--base-gray) calc(l + 0.7) c h);
}

The fact that you can start with any color, use any color function, and manipulate any part of the color is incredibly powerful. The above use cases are pretty basic. I’m sure more talented designers or developers who deeply know color will be able to do much more interesting things!

]]>
https://frontendmasters.com/blog/relative-color-syntax-basic-use-cases/feed/ 1 3448
It’s Time To Talk About “CSS5” https://frontendmasters.com/blog/its-time-to-talk-about-css5/ https://frontendmasters.com/blog/its-time-to-talk-about-css5/#respond Thu, 08 Aug 2024 14:04:03 +0000 https://frontendmasters.com/blog/?p=3397 Brecht De Ruyte has a good rundown of what’s up with future named versions of CSS. You might remember “CSS3” and how hot of a buzzword that is. JavaScript still has successful “ES202X” naming that groups features into useful buckets. But CSS hasn’t benefited from named groups since CSS3.

I’ve written:

CSS3, and perhaps to a larger degree, “HTML5”, became (almost) household names. It was so successful, it’s leaving us wanting to pull that lever again. It was successful on a ton of levels:

  • It pushed browser technology forward, particularly on technologies that had been stale for too long.
  • It got site owners to think, “hey maybe it’s a good time to update our website.
  • It got educators to think, “hey maybe it’s a good time to update our curriculums.

Then it felt like a pipedream. Now it’s real! You can see how the official community group is grouping CSS features and comment on the RFC (Request for Comments). I take no issues with the groups. Ship it.

]]>
https://frontendmasters.com/blog/its-time-to-talk-about-css5/feed/ 0 3397
What if you used Container Units for… everything? https://frontendmasters.com/blog/what-if-you-used-container-units-for-everything/ https://frontendmasters.com/blog/what-if-you-used-container-units-for-everything/#respond Fri, 02 Aug 2024 16:49:11 +0000 https://frontendmasters.com/blog/?p=3108 I said to myself I said what if I used container units for every single unit in a design? I was wondering, partially because I thought the answer might be well, everything will probably scale really nicely then. Container units, in case you haven’t heard of them, are unit (like px or rem, but more closely related to viewport units like vw or vi) that are sized relatively to the container that they are in.

Turns out, surprise surprise, that it’s not that easy. There are plenty of things that container queries are awkward at or just not the right unit for.

I had a play with a pretty simple grid of cards of various sizes. Don’t consider this demo in good shape, it’s just what I used to have a plan.

Potential Problem: You Can’t Style The Element You Query

This is a fairly known thing with container queries, but the weirdness with it compounds a bit with container units, as the desire to use those units right away on a container is strong. To be clear, the container units will “work”, they’ll just be based on the next-higher-up container, which if there isn’t a declared one will be the document.

.card-wrap {
  container: cardWrap / inline-size;

  padding: 2cqi;
  border-radius: 4cqi;

  .card {
    border-radius: 4cqi;
  }
}

Above, the border-radius will be different despite looking like it will be the same, because the container the units reference are different.

See how the outer border radius’ match, but the inner card are different and look off on the larger card.

Potential Solution: Style Nothing on the Container

Be vigilant! It will save headaches if you are hardfast when you set a container you do no other size-based styling. If that means adding an extra otherwise meaningless <div> wrapper, well, that’s not ultra ideal as DOM weight does matter, but it’s probably fine.

Potential Problem: Too Small & Too Big

If you only use container units for something like font-size, it’s pretty easy to get into a situation where text, and text-based elements, end up either too big or too small.

Here the text on the larger card feels a bit too big but the tags are OK. The text on the smaller card feels OK, but those tags are too small.

Either is annoying, but too small is also an accessibility failure.

Using container units (or viewport units) alone is a bad practice for text sizing. It’s fixable though.

Potential Solution: Clamp

Making sure text doesn’t get too small or too big is solved with a CSS clamp() function. And while we’re at it, we can sprinkle in a relative unit to make sure that users font sizing preferences are honored.

You can still use container units, but set those limits and use a bit of relative mixed in.

.tag {
  /* Don't */
  font-size: 4cqi;

  /* Do */
  font-size: clamp(16px, 4cqi + 0.5rem, 24px);
}

Potential Problem: Rows vs Columns

One strong use case for a container query is shifting layout at container based breakpoints. If that container goes from wide-and-short to narrow-and-tall, then the width-based (probably the most common) container units will be super different.

Say the container here is the card itself, and you size some icons to work with that context when they are horizontal. But then if you shift the layout to a narrow column for the icons, as the card gets bigger, the sizing doesn’t work in that context now.

Bonus problem: you can’t use container units to set the grid-template-columns as they can never be based on an element that is reliably the same width as the grid.

Instead, if we make the element around the icons the container, and thus it changes width when the layout changes, the change in our icon size can be too dramatic.

Potential Solution: Use a Different Unit

And here’s the rub. You just don’t have to use container units for everything. I doubt anyone ever intended for them to be used that way. It’s just a fun exercise, especially as strong scalability is a great effect.

In this case maybe something like cqmax units would be workable, so the unit is based on the containers longest edge.

.actions {
  container: actions / inline-size;

  svg {
    display: block;
    width: 4cqmax;
    min-width: 24px;
    max-width: 100%;
    aspect-ratio: 1;
  }
}

But… nah. It’s too weird. I’d say just use relative units or pixels or something here.

In the end, if container units are helpful for a scaling effect based on the size of an element you want to achieve, go for it! The support is good. But don’t force it.

If you’re up for a challenge, have a play with it. Try something like converting every unit in a more complex layout like this into containers with container units.

]]>
https://frontendmasters.com/blog/what-if-you-used-container-units-for-everything/feed/ 0 3108
So you think you know box shadows? https://frontendmasters.com/blog/so-you-think-you-know-box-shadows/ https://frontendmasters.com/blog/so-you-think-you-know-box-shadows/#respond Thu, 01 Aug 2024 14:35:32 +0000 https://frontendmasters.com/blog/?p=3322 David Gerrells has a bunch of fun rendering far too many CSS box-shadows for things that box-shadow was never meant to do.

I found out my m1 can render a stupid number of these bad boys and so I set out to see just how far you can push them and boy did I.

Because box-shadow mimics the shape of the original element, doesn’t have to have any blur at all, and can be colored any color, they can be a way to draw anything you want wherever you want with a single DOM element. Doing faux ray-tracing as David does at the end is not something I thought I’d ever see.

I found it fun looking at the DevTools while the demos were cooking.

]]>
https://frontendmasters.com/blog/so-you-think-you-know-box-shadows/feed/ 0 3322
SVG triangle of compromise https://frontendmasters.com/blog/svg-triangle-of-compromise/ https://frontendmasters.com/blog/svg-triangle-of-compromise/#respond Tue, 30 Jul 2024 15:15:04 +0000 https://frontendmasters.com/blog/?p=3290 tag, it’s cached nicely, but you give up on CSS […]]]> I enjoyed Micah R Ledbetter’s SVG triangle of compromise and generally think it’s a fair analysis of how any-which-way you use SVG on a page, you’re giving up some kind of nice ability it could have. For instance, if you use SVG through an <img> tag, it’s cached nicely, but you give up on CSS reaching in there to style things. If you drop it in as <svg>, you can style, but then it’s not cached well for repeated uses.

Then Scott Jehl chimed in with a way to “have it all”. The crux of it is using the SVG <use> element to reference an SVG file (so you get caching and sizing) and you can set CSS --custom-properties that “pierce” the shadow DOM that <use> creates (that’s right, SVG can have a shadow DOM just like web components) and allow for styling.

This does solve all three angles, the caveats being 1) you can’t cache the SVG (“sprite”, it’s usually called when you combine icons into a single file) on a different domain. 2) it’s a manual pain to set up SVGs to be entirely styled in this way. Scott’s tool might help with 2, but browsers need to help with 1.

]]>
https://frontendmasters.com/blog/svg-triangle-of-compromise/feed/ 0 3290
Blurring https://frontendmasters.com/blog/blurring/ https://frontendmasters.com/blog/blurring/#respond Fri, 26 Jul 2024 19:31:08 +0000 https://frontendmasters.com/blog/?p=3194 , to WebGL. I particularly like the idea of masking an element with a backdrop-filter with a gradient so, for instance, a header can fade out how much […]]]> This 9-minute video from Juxtopposed on blurring is a great watch. There are an awful lot of ways to blur things on the web, from filter, backdrop-filter, <feGaussianBlur>, to WebGL. I particularly like the idea of masking an element with a backdrop-filter with a gradient so, for instance, a header can fade out how much blur is being applied.

]]>
https://frontendmasters.com/blog/blurring/feed/ 0 3194
How to Get the Width/Height of Any Element in Only CSS https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/ https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/#respond Thu, 25 Jul 2024 14:14:28 +0000 https://frontendmasters.com/blog/?p=3119 Getting the dimension of an element using JavaScript is a trivial task. You barely even need to do anything. If you have a reference to an element, you’ve got the dimensions (i.e. el.offsetWidth / el.offsetHeight). But we aren’t so lucky in CSS. While we’re able to react to elements being particular sizes with @container queries, we don’t have access to a straight up number we could use to, for example, display on the screen.

It may sound impossible but it’s doable! There are no simple built-in functions for this, so get ready for some slightly hacky experimentation.

Note: At time of writing, only Chrome (and Edge) have the full support of the features we will be using so consider those browsers to read the article.

Let’s start with a demo:

This demo has a simple layout with elements that will all have different sizes. Each rectangular element displays it’s own width/height. You can resize the browser or adjust the content; the values will update automatically.

Don’t try to find the hidden JavaScript, it’s 100% CSS magic, powered mostly by scroll-driven animations.

Why Scroll-Driven Animations?

Scroll-Driven animations is one of the most popular new CSS features in 2024. It unlocked a lot of possibilities and solved some common problems.

How are these features relevant to this situation of figuring out an element’s dimensions, though?

The terms “scroll” and “animation” tend to bring to mind, uhh, animating stuff on scroll. To be fair, that is the main purpose:

It allows you to animate property values based on a progression along a scroll-based timeline instead of the default time-based document timeline. This means that you can animate an element by scrolling a scrollable element, rather than just by the passing of time.

MDN

But we can think about it differently and achieve more than a simple animation on scroll. If you keep reading the MDN page, it explains there are two types of “scroll-based timelines”. In our case, we will consider the “view progress timeline”.

You progress this timeline based on the change in visibility of an element (known as the subject) inside a scroller. The visibility of the subject inside the scroller is tracked as a percentage of progress.

MDN

With this type of scroll timeline, there are three relevant elements: the scroller which is the container having the scroll, the subject which is an element moving inside the container and the animation that will progress based on the position of the “subject” inside the “scroller”.

The three elements are linked with each other. To identify the progress of the animation we need to know the position of the subject inside the scroller and for this, we need to know the dimension of the scroller, the dimension of the subject, and the offset of the subject (the distance between the subject and the edges of the scroller).

So our equation contains four variables:

  1. Dimension of the scroller
  2. Dimension of the subject
  3. Progress of the animation
  4. Offset of the subject

If three variables are known, we can automatically find the missing one. In our case, the missing variable will be the “dimension of scroller” and that’s how we are going to find the width/height of any element (an element that will a be scroller).

How Does it Work?

Let’s dive into the theory and get to how scroll-driven animations are actually used to do this. It won’t be long and boring, I promise! I’ll be using width as the dimension being measured, but height would use the same logic just on the other axis.

Consider the following figure:

We have a container (the scroller) and an element inside it (the subject) placed on the left. There are two special positions within the container. The 0% position is when the element is at the right (inside the container) and the 100% position is when the element has exited the container from the left (outside the container).

The movement of the subject between 0% and 100% will define the percentage of the progression but our element will not move so the percentage will be fixed. Let’s call it P. We also know the width of the subject and we need to find the width of the scroller.

Remember the variables we talked about. Considering this configuration, we already know three of them: “the width of the subject”, “the offset of the subject” (fixed to the left edge), and the “progress of the animation” (since the subject is fixed). To make things easier, let’s consider that the width of the scroller is a multiplier of the width of the subject:

W = N * S.

The goal is to find the N or more precisely, we need to find the relation between the P and N. I said the P is fixed, but in reality it’s only fixed when the scroller width is fixed which is logical. But if the width of the scroller changes, the progress will also change, that’s why we need to find the formula between the progress and the width.

Let’s start with the case where the width of the scroller is equal to twice the width of the subject, we get the following:

The subject is in the middle between 0% and 100% so the progress in this case is 50%. For N = 2 we get P = 50%.

Let’s try for N = 3:

Now we have two extra slots in addition to the 0% and 100%. If we suppose that the subject can only be placed inside one of the 4 slots, we can have the following progress: 0%33.33%66.67%100%. But the subject is always placed at the before-the-last slot so the progress in this case is equal to 66.67% or, seen differently, it’s equal to 100% - 100%/3 (100%/3 is the progression step).

Are you seeing the pattern? If the width of the scroller is equal to N times the width of the subject we will have N+1 slots (including 0% and 100%) so the step between each slot is equal to 100%/N and the subject is located at the before-the-last slot so the progress is equal to 100% - 100%/N.

We have our equation: P = 100% - 100%/N so N = 100%/(100% - P).

If we convert the percentage to values between 0 and 1 we get N = 1/(1 - P) and the width we are looking for is equal to W = N * S = S/(1 - P).

Now If we consider a width for the subject equal to 1px, we get W = 1px/(1 - P) and without the unit, we have W = 1/(1 - P).

Let’s Write Some Code

Enough theory! Let’s transform all this into code. We start with this structure:

<div class="container"></div>
.container {
  overflow: auto;
  position: relative;
}
.container:before {
  content: "";
  position: absolute;
  left: 0;
  width: 1px;
}

The scroller element is the container and the subject element is a pseudo-element. I am using position: absolute so the subject doesn’t affect the width of the container (the value we need to calculate). Like described in the previous section, it’s placed at the left of the container with 1px of width.

Next, we define a named timeline linked to the pseudo-element (the subject)

.container {
  timeline-scope: --cx;
}
.container:before {
  view-timeline: --cx inline
}

The MDN description of the property:

The view-timeline CSS shorthand property is used to define a named view progress timeline, which is progressed through based on the change in visibility of an element (known as the subject) inside a scrollable element (scroller). view-timeline is set on the subject.

We consider the inline (horizontal) axis. We need to also use timeline-scope to give the container access to the view progress. By default, a named timeline is scoped to the element where it’s defined (and its descendants) but we can change this to make it available at any level.

Why not define the scope at the html level, then?

Enlarging the scope to all the elements may sound like a good idea, but it’s not. We may need to use the same code for different elements so limiting the scope allows us to reuse the same code and keep the same naming.

I won’t spend too much time detailing the scope feature but don’t forget about it. If the code doesn’t work as intended, it’s probably a scoping issue.

Now let’s define the animation:

@property --x {
  syntax: "<number>";
  inherits: true;
  initial-value: 0; 
}
.container {
  animation: x linear;
  animation-timeline: --cx;
  animation-range: entry 100% exit 100%; 
}
@keyframes x {
  0%   { --x: 0; }
  100% { --x: 1; }
}

We define a keyframes that animates a variable from 0 to 1. We have to register that variable with a number type to be able to animate it. We run the animation on the container with a linear easing and define the timeline using animation-timeline.

At this step, we told the browser to consider the named timeline defined on the pseudo-element (the subject) as the reference for the animation progress. And that progress will be stored in the --x variable. At 50%, we have --x: 0.5, at 70%, we have --x: 0.7, and so on.

The last step is to add the formula we identified earlier:

@property --w {
  syntax: "<integer>";
  inherits: true;
  initial-value: 0; 
}
.container {
  --w: calc(1/(1 - var(--x)));
}

The --w variable will contain the width in pixel of the container as a unitless value. It’s important to notice the “unitless” part. It gives us a lot of flexibility as we can integrate it within any formula. If you are a CSS hacker like me, you know what I mean!

What about that animation-range: entry 100% exit 100%;?

In addition to using a named timeline to define which element control the progress, we can also control the range of the animation. In other words, we can explicitly define where the 0% and 100% progress are located within the timeline.

Let’s get back to the first figure where I am showing the 0% and 100% progress.

The 0% is when the subject has completely entered the scroller from the right. We can express this using animation-range-start: entry 100%.

The 100% is when the subject has completely exited the scroller from the left. We can express this using animation-range-end: exit 100%.

Or using the shorthand:

animation-range: entry 100% exit 100%;

If you are new to scroll-driven animations, this part is not easy to grasp, so don’t worry if you don’t fully understand it. It requires some practice to build a mental model for it. Here is a good online tool that can help you visualize the different values.

Now, we do the same for the height and we are done. Here is the first demo again so you can inspect the full code.

Notice that I am using another pseudo-element to show the values. Let’s consider this as our first use case. Being able to get the width/height of any element and show them using only CSS is super cool!

.size::after {
  content: counter(w) "x" counter(h);
  counter-reset: w var(--w) h var(--h);
}

Are There Any Drawbacks?

Even if it seems to work fine, I still consider this as a “hack” to be used with caution. I am pretty sure it will fail in many situations so don’t consider this as a robust solution.

I also said “any element” in the title but in reality not all of them. It’s mandatory to be able to have a child element (the subject) so we cannot apply this trick to elements like <img> for example.

You also need to add overflow: auto (or hidden) to the container to make it the scroller for the subject. If you plan to have overflowing content then this solution will give you some trouble.

The value you will get using this method will include the padding but not the border! Pay attention to this part and compare the values you get with the ones of the Dev tools. You may need to perform another calculation to get the real dimension of the element by adding or subtracting specific amounts.

Another drawback is related to the use of 1px as our unit. We assumed that the size is a multiplier of 1px (which is true in most cases) but if your element is having a size like 185.15px, this trick won’t work. We can overcome this by using a smaller width for the subject (something like 0.01px) but I don’t think it is worth making this hack more complex.

A Few Use Cases

The first use case we saw is to show the dimension of the element which is a cool feature and can be a good one for debugging purposes. Let’s dig into more use cases.

Getting the Screen Dimension

We already have the viewport units vh and vw that works fine but this method can give us the unitless pixel values. You may ask how to do this since the viewport is not a real element. The solution is to rely on position: fixed applied to any element on the page. A fixed element is positioned relative to the viewport so its scroller will the viewport.

If you check the code, you will see that I am relying on the HTML pseudo-element for the subject and I don’t need to define any overflow or position on the HTML element. Plus the values are available globally since they are defined inside the HTML element!

For this particular case, I also have another CSS trick to get the screen dimension with an easier method:

Calculating the Scrollbar Width

There is a slight difference between the two screen width calculating methods above. The first demo will not include the scrollbar width if the page has a lot of content but the second one will. This means that If we combine both methods we can get the width of scrollbar!

Cool right? In addition to the screen dimension, you can also have the width of the scrollbar. Both values are available at root level so you can use them anywhere on the page.

Counting Stuff

All the calculations we did were based on the 1px size of the subject. If we change this to something else we can do some interesting counting. For example, if we consider 1lh (the height of the line box) we can count the number of lines inside a text.

Here is the version where you can edit the content. The number of lines will adjust based on the content you will enter.

Note how I am playing with the scope in this example. I am making the variable available at a higher level to be able to show the count inside a different element. Not only we can count the numbers of lines but we can also show the result anywhere on the page.

Can you think about something else to count? Share your example in the comment section.

Transferring Sizes

Being able to control the scope means that we can transfer the size of an element to another one on the page.

Here is an example where resizing the left element will also resize the right one!

Another important part of this trick is being able to get the width/height values as integer. This allows us to use them within any formula and append any unit to them.

Here is an example, where resizing the left element will rotate/scale the right one.

I have mapped the width with the rotation and the height with the scaling. Cool right? We can get the width/height of an element, have them as an integer, and transfer them to another element to do whatever we want. CSS is magic!

Conclusion

I hope you enjoyed this funny experiment. I still insist on the fact that it’s a hacky workaround to do something that was not possible using CSS. Use it for fun, use it to experiment with more CSS-only ideas but think twice before including this into a real project. Using one line of JavaScript code to get the dimension of an element is safer. Not all CSS-only tricks are a good replacement for JavaScript.

This said, if you find an interesting use case or you have another CSS-only experimentation where this trick can be useful, share it in the comment section.

I will end this article with a last demo where I am transforming the native progress element into a circular one. Can you figure out how it works? I am using the same technique. This time, I know both the width of the scroller and the subject and the missing variable is the progress. Try to dissect the code as homework 😜.

]]>
https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/feed/ 0 3119
Clip Pathing Color Changes https://frontendmasters.com/blog/clip-pathing-color-changes/ https://frontendmasters.com/blog/clip-pathing-color-changes/#respond Tue, 23 Jul 2024 17:29:41 +0000 https://frontendmasters.com/blog/?p=3103 This is a nice post from Emil Kowalski on usage of the clip-path property in CSS. I’ve always liked clip-path. Maybe it’s because it’s such a sharp knife. When you clip an element, it’s clipped, yo. There isn’t a lot of nuance to it, it does what it does. But moreso, I think I like the connection to SVG (oh, hey, by the way, I just made my old book Practical SVG entirely free). The value that you give clip-path is stuff like circle(), polygon(), path(), etc — the primitive shapes of SVG.

In Emil’s post, my favorite example is a navigation bar where a “pill” shape animates from one navigation item to another when they are clicked. The pill is a different background color, and so the text color also changes. (If you’re over 100 years old like me, we used to call this kind of thing “lava lamp” navigation 👴).

I would guess most people would assume what is happening here is an extra element set behind the links that moves position to underneath the newly active links. You could do it that way, but there is a minor aesthetic issue with it. Because the background-color is changing here, the text also needs to change appropriately (from dark to light here). You could change that color instantly, but that will look weird like it’s changing too early. You could set a transition on it, but you’ll never get the fade to look quite right, especially as it has to go through an awkward gray color.

Essentially, you’ll never get a state like this:

This ain’t gonna happen with an underlay element alone.

See how the text is half-light and half-dark mid-animation when the highlight blue pill moves from one to another? That’s a lovely effect that makes this feel very polished and smooth. This idea first came from a tweet by Paco. Like Emil says:

You might say that not everyone is going to notice the difference, but I truly believe that small details like this add up and make the experience feel more polished. Even if they go unnoticed.

Agreed.

In Emil’s post, it’s done with React. That’s totally fine, but I figured I’d make a vanilla one for y’all here:

Here’s how this works:

  1. There is one set of semantic HTML navigation.
  2. If JavaScript executes, it duplicates the nav (we’ll need two) but ensures the duplicate is hidden for screen readers.
  3. The duplicate is placed exactly on top of the original (it’s the “blue” one) and can’t directly be clicked (i.e. pointer-events: none;)
  4. A clip-path is set that highlights one of the navigation items in particular by clipping the entire duplicate except one link.
  5. As links are clicked, the clip-path is changed using positional math, highlighting the new one. Also high-five for the round keyword that can be used with inset() for rounded corners on inset rectangles.
  6. The clip-path animates, thanks to a basic CSS transition.

I think it’s cool as heck that it all comes together that cleanly.

It’s also a nice touch that the new clip-path positions are calculated based on their page position, meaning that there are really no magic numbers here. If we add navigation items or change them, this code will be resilient and it will all still work. And if none of this JavaScript runs at all, no big deal.

]]>
https://frontendmasters.com/blog/clip-pathing-color-changes/feed/ 0 3103
Notes On “Microfeatures I Love in Blogs and Personal Websites” https://frontendmasters.com/blog/notes-on-microfeatures-i-love-in-blogs-and-personal-websites/ https://frontendmasters.com/blog/notes-on-microfeatures-i-love-in-blogs-and-personal-websites/#respond Mon, 15 Jul 2024 23:30:33 +0000 https://frontendmasters.com/blog/?p=3017 I enjoyed Danila Fedorin’s post Microfeatures I Love in Blogs and Personal Websites. Here’s some stuff I think it cool is a great style of post that I wish more people did, especially since I was just poking at that. Maybe I’ll do my own one of these days, but I had so many thoughts while reading Danila’s, I figured I could turn that into a post.

I’m going to go through each feature with what goes through my brain. It is clearly noted “[these features] need not be applied indiscriminately” which I agree and want to underscore. These are mostly just nice ideas when appropriate.

Sidenotes

Danila mentions examples like this.

Love it. Very classy. My thinking is:

  1. Start as semantic HTML footnotes. Put the footnotes at the bottom of the article, use numbered jump links down to them, then link back.
  2. Progressively enhance to a popover, probably as a drawer.
  3. If on a large enough screen, enhance to Nuclear Anchored Sidenotes. CSS’ upcoming anchor positioning API is going to be a godsend for this.

Easily Linkable Headings

The idea here is instead of something like this:

<h2>
  <a href="#particular-header">
    #
    <span class="visually-hidden">Jump Link to Particular Header</a>
  </a>
  Particular Header
</h2>

You just make the header itself a link like:

<a href="#particular-header">
  <h2 id="particular-header">Particular Header</h2>
</a>

Huh! I just never thought to do it that way because it feels like… I dunno they aren’t really links so it feels weird linking the whole thing. But the more I think about it the more I don’t hate it. Danila is doing it and even tosses in a little yellow fade technique for good measure I see.

To me, it’s more about an authoring experience that doesn’t make you think about it at all. All headers should be linkable, automatically. I’ve long been a fan of this itty bitty WordPress plugin that does it. Whatever you use to produce HTML from written content, automate it!

GitHub does it like this, after the header.
The current design of this site does it like this, before the header.

Table of Contents

I actually moved “Easily Linkable Headlines” up a few spots so that it would come before this section. My thinking is that once you have all headers linked properly, producing a table of contents is “easy”. Loop over the headers, display. The more complicated (but optional) thing could could do is nesting the headers. Meaning h4’s are nested under the preceding h3, which is nested under the preceding h2, etc.

I would think any major CMS will have some automated way of producing these things. I hand-wrote the PHP on this site to create the ones you can see in our sidebar on all posts (that have headings).

This site’s current Table of Contents design, which is in the sidebar and has position: sticky so it hangs around as you scroll down a longer article.

Showing Page Progress

I actually disagree on this one. I think those horizontal bars that fill up as you scroll down the page are cheezy, unnecessary, and unhelpful. They do make for a pretty good demo on using Scroll-Driven Animations though!

But we just talked about Table of Contents and Danila mentions a Table of Contents that highlights where you are, and that is actually pretty rad. Agreed on that one! Maybe I can implement that to our Table of Contents one day.

Grouping Series of Posts

It’s actually silly to write a series of posts and then not clearly link them together. Definitely do that! As many ways as you kind. That’s just good wayfinding for users. Nobody is going to be mad at you for helping you find your way around.

I built this kind of widget here on Boost for that:

The Article Series block on this site’s design at the time of writing.

As I mentioned, this is a WordPress site, so I used Advanced Custom Fields (very broadly useful) and the Post Object Field Type applied to a custom Block, so I can plop one of these little “Article Series” blocks where ever I want. Then I make the block a “Pattern”, so that I can re-use the exact same version of the Block all over the series. Update one, they all update, which makes it easy as you’re publishing over time. I realize that’s pretty WordPress-specific, but it’s worth building out something for this if you publish series!

Dialogs

Heck yeah! +1 to interesting post formats. We all use all sorts of messaging services, so having that available to use as a design element with posts is a great idea. We’ve done limited versions of it sort of replicating a Discord conversation a few times, but it doesn’t yet have that back-and-forth conversational feel, it’s just a list of posts.

I’d probably make a “left” custom design and a “right” custom design so I could just pick and choose them however makes the conversation look best. Oh and it would be a great use-case for text-wrap: balance so that the actual “text bubbles” would feel rather sized to their content nicely.

Generally: art direct those articles! Make them interesting!

Code Blocks with Origin

The idea here is showing off the name of the file that you’re showing off a code block of.

I mean, sure. Why not. I could see that being an interesting bit of metadata you might want to have available sometimes. I’ve just written one zillion code blocks in posts in my life and have rarely wanted it. I usually don’t care what you name your file, it’s just a concept. In Danila’s case, it was based on user feedback about a pretty complex series of posts about a very technical subject, so point taken.

Here’s my list for both authors and users of code blocks:

  1. Syntax highlighting, server side. Subset of languages I care about.
  2. I don’t want to have to escape special characters myself.
  3. Nice design. Distinct but not distracting.
  4. A copy and paste button.
  5. Line numbers that I can turn on or off.
  6. Ability to highlight any lines.

I do like the idea of allowing for clickable links within code blocks. This is a good one for the list because I think it would be extra tricky to pull off and quite a nice touch. You either have to give up on having the code auto-escaped (so the HTML within could stay actual HTML) or do something really clever, like not escaping HTML within comments?? or something??

.el {
  /* Try the <a href="https://scroll-driven-animations.style/tools/view-timeline/ranges/">View Timeline Visualizer</a> */
  animation: reveal linear both;
  animation-timeline: view(block);
  animation-range: cover 0% cover 100%;
}

Markers for External Links

I get the idea. The little box-with-arrow icon sorta like [⤴].

I can’t get behind it though. I’d say it’s just personal preference (I don’t really care if a link is “internal” or “external”), but I’ve also never seen data on if users find it helpful or heard any particularly strong or compelling opinions about it over my years. I do like it when links that are weird/surprising are indicated though, like:

  1. Email links (e.g. mailto:)
  2. Links to PDFs
  3. Links to Media files (e.g. .mp3)

Those have way different behavior than just “go to new website” so a heads up is nice. And since they are all <a> links (probably), CSS can help:

a[href$=".pdf"]::after {
  content: " (PDF)";
}
a[href^="mailto"]::after {
  content: " (Email)";
}
/* etc. */

Danila’s idea of different markers for different destinations, while I’m personally not that into it, can be pulled off in CSS with a little indie web service.

Link Preview

I’d have a real light touch with this! It’s kind of unexpected behavior if you do something like make it a hover effect for a link.

Remember when posted about Standalone Web Components, I linked up David Darnes’ <link-peak> component which could help with this.

RSS Feeds

Yes! Old man shakes fist at internet!

If you write and publish your work on your own site for free, give me that sweet sweet RSS feed. Do it just for me. I’ll subscribe to it. I carefully curate my feeds and I love it. It’s simple technology designed to connect us.

Danila almost mentions linking up other people’s sites. Sure! Go for it! Have fun with it! You could call it a “blogroll”, that’s kind of the classic term for it. Or go even older-school with a “webring”, those are coming back a smidge. If you want to show the latest posts from other sites, that ups the difficultly and has performance implications, but it’s doable and you could have fun with that.

]]>
https://frontendmasters.com/blog/notes-on-microfeatures-i-love-in-blogs-and-personal-websites/feed/ 0 3017
Style Queries are Almost Like Mixins (But Mixins Would Be Better) https://frontendmasters.com/blog/css-does-need-mixins/ https://frontendmasters.com/blog/css-does-need-mixins/#comments Fri, 12 Jul 2024 18:41:35 +0000 https://frontendmasters.com/blog/?p=3002 I was styling a menu thing the other day, and it had some decently nested selectors. Normally I’m a pretty big fan of putting a class right on the thing you want to style and keeping CSS selectors pretty “flat” in that they are just that class alone. But for menus and the semantic HTML within, a little nesting seemed reasonable. For instance this HTML:

<nav class="site-nav">
  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">Contact</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">History</a></li>
  </ul>
</nav>

Can lead to this kind of CSS:

.site-nav {
  > ul {
    > li {
      > a {
        &:hover, 
        &:focus {
        }
      }
    }
  }
}

I can see how that turns some people off, but honestly it doesn’t bother me that much. The structure is reliable here and I’d rather this setup in CSS than a class on every one of those links in the HTML.

If we get into sub-menu territory though, it gets gnarlier:

.site-nav {
  > ul {
    > li {
      > ul { /* sub menu */
        > li {
          > a {
            &:hover,
            &:focus {
              
            }
          }
        }
      }
    }
  }
}

I’m willing to admit this is probably a bit too far in nesting town 😬. Particularly because at each of those nested levels there will be a bunch of styling and it will become hard to reason about quite quickly.

It occurred to me that the (newfangled, not production-ready) CSS style queries might be able to jump in and help here, because they behave a bit like a mixin.

Mixin?

Yeah! That’s what Sass called the concept, anyway. A mixin allows us to name a block of styles, then call them as needed. So…

@mixin linkHovered {
  background: red;
  color: white; 
}

.site-nav {
  > ul {
    > li {
      > a {
        &:hover, 
        &:focus {
          @include linkHovered;
        }
      }
    }
  }
}

So now we’ve kind of flattened out the styles a bit. We have this re-usable chunk of styles that we can just call rather than nest the styles so deeply.

What I wanted to try here was using Style Queries (and not Sass), but unfortunately it’s not quite as clean as I’d like. After using Sass for so long, this is what I wanted to work:

/* Invalid! Doesn't select anything */
@container style(--linkHovered) {
  background: red;
}

.site-nav {
  > ul {
    > li {
      > a {
        &:hover, &:focus {
          --linkHovered: true;
        }
      }
    }
  }
}

But that’s a no-go. The @container style query either needs to be nested within the other styles so that it has an implied selector (which defeats the “flatten the styles” purpose) or it needs an explicit selector inside it. So it needs to be like this:

@container style(--hasLinkHovered) {
  a {
    background: red;
  }
}

.site-nav {
  > ul {
    > li {
      &:has(> a:hover, > a:focus) {
        --hasLinkHovered: true;
      }
    }
  }
}

That works (where supported):

I just don’t love it. You have to set the Custom Property higher up in the nesting because container styles can’t style the thing they query. Plus now the true selector is a combination of the nesting and what’s in the container style query which is an awful brainbuster to keep track of.

This is not to say that Style Queries aren’t useful. They totally are, and I’m sure we’ll uncover lots of cool use cases in the coming years. I’m just saying that shoehorning them to behave exactly like mixins isn’t great.

It sure would be nice if CSS got native mixins!

It would be yet another Sass feature making it’s way into the platform. In the case of mixins, it would be a great win, because the CSS would be more efficient than the way Sass had to express the mixin concept back in CSS. If you used a @mixin 10 times under different selectors, those styles blocks would be barfed out 10 duplicate times in the CSS. Perhaps not the worlds biggest deal thanks to file compression, but certainly not as efficient as the language itself just referring to a single block of styles.

]]>
https://frontendmasters.com/blog/css-does-need-mixins/feed/ 1 3002