Developer News

Help choose the syntax for CSS Nesting

Css Tricks - Tue, 12/20/2022 - 6:04am

CSS Nesting is making the rounds yet again. Remember earlier this year when Adam and Miriam put three syntax options up for a vote? Those results were tallied and it wasn’t even even close.

Now there’s another chance to speak into the future of nesting, this time over at the WebKit blog. The results from the Adam and Miriam’s survey sparked further discussion and two more ideas were added to the mix. This new survey lets you choose from all five options.

Jen Simmons has put together a thorough outline of those options, including a refresher on nesting, details on how we arrived at the five options, and tons of examples that show the options in various use cases. Let’s return the favor of all the hard work that’s being done here by taking this quick one-question survey.

To Shared LinkPermalink on CSS-Tricks

Help choose the syntax for CSS Nesting originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

WordPress Playground: Running WordPress in the Browser

Css Tricks - Mon, 12/19/2022 - 6:52am

Being able to quickly spin up a WordPress instance has been the strength of WordPress ever since its famous “five-minute install”. Upload a few files, configure a few settings, and you’re off.

The friction of uploading files has gotten a lot easier, thanks to plenty of “one-click” install options many hosts offer (including DigitalOcean and Cloudways).

Some companies have tried to abstract the process even more, using the multi-site features of WordPress to fire up disposable instances for testing and demos. WordPress Sandbox and WP Sandbox come to mind. Scaling can be an issue here, as instances run on the same install adding lag to the entire network. I worked on a headless WordPress project that did this in the background for users, and I recall the incredibly long wait it would take users to create a new account as the number of sites in the network piled up.

Enter WordPress Playground. It runs entirely in the browser which is mindblowing to me as a long-time WordPress user. If you’re having a hard time wrapping your head around how it all works like I did, that link to the overview spells it out nicely:

Dang, that’s cool. The move to SQLite is especially interesting, as it could bring huge performance gains to many sites that might not need the full heft of WordPress — a “WordPress Lite” as Chris recently described it in a different context. In fact, that work is already happening in the experimental WordPress performance plugin.

The evolution to a light, frictionless WordPress is a fun space to watch. I imagine there’s a good chunk of existing WordPress sites that stand to benefit from a slimmed-down CMS. The demo offers a glimpse at what an onboarding experience for that sort of thing could look like.

Select a theme, choose your features, and go!

WordPress Playground: Running WordPress in the Browser originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS Infinite 3D Sliders

Css Tricks - Fri, 12/16/2022 - 4:58am

In this series, we’ve been making image sliders with nothing but HTML and CSS. The idea is that we can use the same markup but different CSS to get wildly different results, no matter how many images we toss in. We started with a circular slider that rotates infinitely, sort of like a fidget spinner that holds images. Then we made one that flips through a stack of photos.

This time around, we’re diving into the third dimension. It’s going to look tough at first, but lots of the code we’re looking at is exactly what we used in the first two articles in this series, with some modifications. So, if you’re just now getting into the series, I’d suggest checking out the others for context on the concepts we’re using here.

CSS Sliders series

This is what we’re aiming for:

CodePen Embed Fallback

At first glance, it looks like we have a rotating cube with four images. But in reality, we’re dealing with six images in total. Here is the slider from a different angle:

Now that we have a good visual for how the images are arranged, let’s dissect the code to see how we get there.

The basic setup

Same HTML as the rest of the sliders we’ve used for the other sliders:

<div class="gallery"> <img src="" alt=""> <img src="" alt=""> <img src="" alt=""> <img src="" alt=""> <img src="" alt=""> </div>

And once again, we’re using CSS Grid to place the images in a stack, one on top of another:

.gallery { display: grid; } .gallery > img { grid-area: 1 / 1; width: 160px; aspect-ratio: 1; object-fit: cover; } The animation

The logic for this slider is very similar to the circular slider from the first article. In fact, if you check the video above again, you can see that the images are placed in a way that creates a polygon. After a full rotation, it returns to the first image.

We relied on the CSS transform-origin and animation-delay properties for that first slider. The same animation is applied to all of the image elements, which rotate around the same point. Then, by using different delays, we correctly place all the images around a big circle.

The implementation will be a bit different for our 3D slider. Using transform-origin won’t work here because we’re working in 3D, so we will use transform instead to correctly place all the images, then rotate the container.

We’re reaching for Sass again so we can loop through the number of images and apply our transforms:

@for $i from 1 to ($n + 1) { .gallery > img:nth-child(#{$i}) { transform: rotate(#{360*($i - 1) / $n}deg) /* 1 */ translateY(50% / math.tan(180deg / $n)) /* 2 */ rotateX(90deg); /* 3 */ } }

You might be wondering why we’re jumping straight into Sass. We started with a fixed number of images using vanilla CSS in the other articles before generalizing the code with Sass to account for any number (N) of images. Well, I think you get the idea now and we can cut out all that discovery work to get to the real implementation.

The transform property is taking three values, which I’ve illustrated here:

We first rotate all the images above each other. The angle of rotation depends on the number of images. For N images, we have an increment equal to 360deg/N. Then we translate all of the images by the same amount in a way that makes their center points meet on the sides.

There’s some boring geometry that helps explain how all this works, but the distance is equal to 50%/tan(180deg/N). We dealt with a similar equation when making the circular slider ( transform-origin: 50% 50%/sin(180deg/N) ).

Finally, we rotate the images around the x-axis by 90deg to get the arrangement we want. Here is a video that illustrates what the last rotation is doing:

Now all we have to do is to rotate the whole container to create our infinite slider.

.gallery { transform-style: preserve-3d; --_t: perspective(280px) rotateX(-90deg); animation: r 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite; } @keyframes r { 0%, 3% {transform: var(--_t) rotate(0deg); } @for $i from 1 to $n { #{($i/$n)*100 - 2}%, #{($i/$n)*100 + 3}% { transform: var(--_t) rotate(#{($i / $n) * -360}deg); } } 98%, 100% { transform: var(--_t) rotate(-360deg); } }

That code might be hard to understand, so let’s actually step back a moment and revisit the animation we made for the circular slider. This is what we wrote in that first article:

.gallery { animation: m 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite; } @keyframes m { 0%, 3% { transform: rotate(0); } @for $i from 1 to $n { #{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}% { transform: rotate(#{($i / $n) * -360}deg); } } 98%, 100% { transform: rotate(-360deg); } }

The keyframes are almost identical. We have the same percentage values, the same loop, and the same rotation.

Why are both the same? Because their logic is the same. In both cases, the images are arranged around a circular shape and we need to rotate the whole thing to show each image. That’s how I was able to copy the keyframes from the circular slider and use that same code for our 3D slider. The only difference is that we need to rotate the container by -90deg along the x-axis to see the images since we have already rotated them by 90deg on the same axis. Then we add a touch of perspective to get the 3D effect.

That’s it! Our slider is done. Here is the full demo again. All you have to do is to add as many images as you want and update one variable to get it going.

CodePen Embed Fallback Vertical 3D slider

Since we are playing in the 3D space, why not make a vertical version of the previous slider? The last one rotates along the z-axis, but we can also move along the x-axis if we want.

CodePen Embed Fallback

If you compare the code for both versions of this slider, you might not immediately spot the difference because it’s only one character! I replaced rotate() with rotateX() inside the keyframes and the image transform. That’s it!

It should be noted that rotate() is equivalent to rotateZ(), so by changing the axis from Z to X we transform the slider from the horizontal version into the vertical one.

Cube slider

We cannot talk about 3D in CSS without talking about cubes. And yes, that means we are going to make another version of the slider.

The idea behind this version of the slider is to create an actual cube shape with the images and rotate the full thing in around the different axis. Since it’s a cube, we’re dealing with six faces. We’ll use six images, one for each face of the cube. So, no Sass but back to vanilla CSS.

CodePen Embed Fallback

That animation is a little overwhelming, right? Where do you even start?

We have six faces, so we need to perform at least six rotations so that each image gets a turn. Well, actually, we need five rotations — the last one brings us back to the first image face. If you go grab a Rubik’s Cube — or some other cube-shaped object like dice — and rotate it with your hand, you’ll have a good idea of what we’re doing.

.gallery { --s: 250px; /* the size */ transform-style: preserve-3d; --_p: perspective(calc(2.5*var(--s))); animation: r 9s infinite cubic-bezier(.5, -0.5, .5, 1.5); } @keyframes r { 0%, 3% { transform: var(--_p); } 14%, 19% { transform: var(--_p) rotateX(90deg); } 31%, 36% { transform: var(--_p) rotateX(90deg) rotateZ(90deg); } 47%, 52% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); } 64%, 69% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg); } 81%, 86% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg); } 97%, 100%{ transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); } }

The transform property starts with zero rotations and, on each state, we append a new rotation on a specific axis until we reach six rotations. Then we are back to the first image.

Let’s not forget the placement of our images. Each one is applied to a face of the cube using transform:

.gallery img { grid-area: 1 / 1; width: var(--s); aspect-ratio: 1; object-fit: cover; transform: var(--_t,) translateZ(calc(var(--s) / 2)); } .gallery img:nth-child(2) { --_t: rotateX(-90deg); } .gallery img:nth-child(3) { --_t: rotateY( 90deg) rotate(-90deg); } .gallery img:nth-child(4) { --_t: rotateX(180deg) rotate( 90deg); } .gallery img:nth-child(5) { --_t: rotateX( 90deg) rotate( 90deg); } .gallery img:nth-child(6) { --_t: rotateY(-90deg); }

You are probably thinking there is weird complex logic behind the values I’m using there, right? Well, no. All I did was open DevTools and play with different rotation values for each image until I got it right. It may sound stupid but, hey, it works — especially since we have a fixed number of images and we are not looking for something that supports N images.

In fact, forget the values I’m using and try to do the placement on your own as an exercise. Start with all the images stacked on top of each other, open the DevTools, and go! You will probably end up with different code and that’s totally fine. There can be different ways to position the images.

What’s the trick with the comma inside the var()? Is it a typo?

It’s not a typo so don’t remove it! If you do remove it, you will notice that it affects the placement of the first image. You can see that in my code I defined --_t for all the images except the first one because I only need a translation for it. That comma makes the variable fall back to a null value. Without the comma, we won’t have a fallback and the whole value will be invalid.

From the specification:

Note: That is, var(--a,) is a valid function, specifying that if the --a custom property is invalid or missing, the var()` should be replaced with nothing.

Random cube slider

A little bit of randomness can be a nice enhancement for this sort of animation. So, rather than rotate the cube in sequential order, we can roll the dice so to speak, and let the cube roll however it will.

CodePen Embed Fallback

Cool right? I don’t know about you, but I like this version better! It’s more interesting and the transitions are satisfying to watch. And guess what? You can play with the values to create your own random cube slider!

The logic is actual not random at all — it just appears that way. You define a transform on each keyframe that allows you to show one face and… well, that’s really it! You can pick any order you want.

@keyframes r { 0%, 3% { transform: var(--_p) rotate3d( 0, 0, 0, 0deg); } 14%,19% { transform: var(--_p) rotate3d(-1, 1, 0,180deg); } 31%,36% { transform: var(--_p) rotate3d( 0,-1, 0, 90deg); } 47%,52% { transform: var(--_p) rotate3d( 1, 0, 0, 90deg); } 64%,69% { transform: var(--_p) rotate3d( 1, 0, 0,-90deg); } 81%,86% { transform: var(--_p) rotate3d( 0, 1, 0, 90deg); } 97%,100% { transform: var(--_p) rotate3d( 0, 0, 0, 0deg); } }

I am using rotate3d() this time but am still relying on DevTools to find the values that feel “right” to me. Don’t try to find a relationship between the keyframes because there simply isn’t one. I’m defining separate transforms and then watching the “random” result. Make sure the first image is the first and last frames, respectively, and show a different image on each of the other frames.

You are not obligated to use a rotate3d() transform as I did. You can also chain different rotations like we did in the previous example. Play around and see what you can come up with! I will be waiting for you to share your version with me in the comments section!

Wrapping up

I hope you enjoyed this little series. We built some fun (and funny) sliders while learning a lot about all kinds of CSS concepts along the way — from grid placement and stacking order, to animation delays and transforms. We even got to play with a dash of Sass to loop through an array of elements.

And we did it all with the exact same HTML for each and every slider we made. How cool is that? CSS is dang powerful and capable of accomplishing so much without the aid of JavaScript.

CSS Infinite 3D Sliders originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

So, you’d like to animate the display property

Css Tricks - Thu, 12/15/2022 - 5:41am

The CSS Working Group gave that a thumbs-up a couple weeks ago. The super-duper conceptual proposal being that we can animate or transition from, say, display: block to display: none.

It’s a bit of a brain-twister to reason about because setting display: none on an element cancels animations. And adding it restarts animations. Per the spec:

Setting the display property to none will terminate any running animation applied to the element and its descendants. If an element has a display of none, updating display to a value other than none will start all animations applied to the element by the animation-name property, as well as all animations applied to descendants with display other than none.

That circular behavior is what makes the concept seemingly dead on arrival. But if @keyframes supported any display value other than none, then there’s no way for none to cancel or restart things. That gives non-none values priority, allowing none to do its thing only after the animation or transition has completed.

Miriam’s toot (this is what we’re really calling these, right?) explains how this might work:

We’re not exactly interpolating between, say, block and none, but allowing block to stay intact until the time things stop moving and it’s safe to apply none. These are keywords, so there are no explicit values between the two. As such, this remains a discrete animation. We’re toggling between two values once that animation is complete.

This is the Robert Flack’s example pulled straight from the discussion:

@keyframes slideaway { from { display: block; } to { transform: translateY(40px); opacity: 0;} } .hide { animation: slideaway 200ms; display: none; }

This is a helpful example because it shows how the first frame sets the element to display: block, which is given priority over the underlying display: none as a non-none value. That allows the animation to run and finish without none cancelling or resetting it in the process since it only resolves after the animation.

This is the example Miriam referenced on Mastodon:

.hide { transition: opacity 200ms, display 200ms; display: none; opacity: 0; }

We’re dealing with a transition this time. The underlying display value is set to none before anything happens, so it’s completely out of the document flow. Now, if we were to transition this on hover, maybe like this:

.hide:hover { display: block; opacity: 1; }

…then the element should theoretically fade in at 200ms. Again, we’re toggling between display values, but block is given priority so the transition isn’t cancelled up front and is actually applied after opacity finishes its transition.

At least that’s how my mind is reading into it. I’m glad there are super smart people thinking these things through because I imagine there’s a ton to sort out. Like, what happens if multiple animations are assigned to an element — will none reset or cancel any of those? I’m sure everything from infinite animations, reversed directions, and all sorts of other things will be addressed in time.

But what a super cool first step!

So, you’d like to animate the display property originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Some Links on AI-Related Stuff

Css Tricks - Wed, 12/14/2022 - 11:34am

Every so often, I find that the links I save to read later fall into natural groups or patterns that reveal common threads of interest. The past couple of weeks have produced a lot of thoughts about ChatGPT, an AI-powered interface that responds to requests in a chat-like exchange. Sorta like a “Hey Siri” request, but in a Discord channel.

ChatGPT is just one of several AI-flavored tech, including GitHub’s CoPilot (writing code) and Dall-E (generative images and art).

Is it the end of human development? A new and exciting way to produce art? Just cocktail party conversation fodder? There are lots of opinions…

  • A Conversation With ChatGPT (Matthias Ott) — Matthias has a conversation with ChatGPT about typography that delves into deeply theoretical thoughts on design process. My favorite is in response to whether designers should learn to code: “Ultimately, whether or not designers should learn to code is a decision that each individual designer must make for themselves, based on their own goals and circumstances. Some designers may benefit from learning to code, while others may be better served by focusing on design principles and concepts.”
  • They were supposed to replace the creative jobs last (Dave Rupert)“As interesting a future this creates, I’m a member of an old caste of people that still believes massive gains don’t come without realized costs; or more explicitly, electricity isn’t the only cost. What if the cost we’re paying is our perception of reality itself? It’s increasingly likely that the next thing you read or watch is the product of a content extruder.”
  • I just used ChatGPT to help with a complicated equation. — A reddit user used ChatGPT to write a complex equation in Notion. There were a couple hiccups, but it worked in the end.
  • ChatGPT Creates a Working WordPress Plugin – On the First Try (WP Tavern) — Sarah Gooding reporting on a ChatGPT experiment where Johnathon Williams was able to spit out a fully-functional WordPress plugin with a simple chat command. This is the sort of thing that both terrifies me but also blows my mind-hole.
  • ChatGPT Is a Smart Computer’s Impression of a Know-It-All (Pixel Envy) — Nick Heer points to an article on The Atlantic about ChatGPT that opens with three paragraphs written by ChatGPT. It’s crazy that it comes off as naturally as it does, even if it smells slightly fishy at first.
  • Use of ChatGPT1 generated text for content on Stack Overflow is temporarily banned. (Stack Overflow) — A mild dose of #HotDrama as far as Stack Overflow users posting ChatGPT-produced code as answers.
  • Midjourney vs. human illustrators: has AI already won? (Evil Martians) — I love the experiment in this post because it’s a clear example that AI doesn’t *just* work. In its current state, at best, AI is a junior designer when put to the task of creating an image: “After two and a half hours of back and forth with the AI, I was completely exhausted and decided to just upscale the most promising result.” A bonus is that the post concludes with a list of situations where AI might realistically help the team with future work — and it ain’t an entire person’s job.
  • Quick Thoughts on AI (Collaborative Fund) — Ha! Crazy to see a chart comparing how fast ChatGPT reached one million users to other popular services. It took Facebook 10 months, but only five days for ChatGPT.

Dall-E, I want a photo of a developer sitting at a desk with his head exploding while having a chat conversation on a desktop computer with an artificial intelligence algorithm.

Not bad, not bad.

Some Links on AI-Related Stuff originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Unchain My Inaccessibly-Labelled Heart

Css Tricks - Wed, 12/14/2022 - 4:08am

Suzy Naschansky from the HTMHell Advent Calendar:

<h2 id="article1-heading">All About Dragons</h2> <p>I like dragons. Blah blah blah blah blah.</p> <p> <a id="article1-read-more" aria-labelledby="article1-read-more article1-heading">Read more</a> </p>

See that aria-labelledby attribute? It chains two IDs from the markup, one for the heading (#article1-heading) and one for the link (#article1-read-more). What happens there is a screenreader will replace the existing semantic label between the link tags and use the content from both elements and announce them together as a single string of text:

Read more All About Dragons

I’m always sheepish when realizing there’s something I think I should know but don’t. This is definitely one of those cases and I’m thankful as all heck that Suzy shared it.

I was actually in a situation just recently where I could’ve should’ve done this. I always try to avoid a bunch of “Read more” links on the same page but coming up with different flavors of the same thing is tough when you’re working with something like a loop of 15 posts (even though there are resources to help). And if we need to keep labels short for aesthetic reasons — design requirements and whatnot — it’s even more challenging. The aria-labelledby attribute gives me exactly what I want: consistent visual labels and more contextual announcements for assistive tech.

And this is only a thing when the text you want to use for the accessible label already exists on the page. Otherwise, you’d want to go with aria-label and with the caveat that it’s purely for interactive elements that are unable to label things accessibly with semantic HTML.

If you are working in a CMS like WordPress (which I am), you might need to do a little extra work. Like when I drop a Button block on the page, these are the options I have to work with:

Some nice options in there, but nothing to do with accessible labelling. If you’re wondering what’s buried in that Advanced panel:

Close, but no cigar.

Instead, you’ll need to edit the button in HTML mode:

But before doing that, you gotta add an ID to the heading you want to use. The Heading block has the same Advanced panel setting for adding an anchor, which’ll inject an ID on the element:

Then you can go edit the Button block in HTML mode and add the accessible-labels ID as well as an ID for the button itself. This is an example of the edited markup:

<div class="wp-block-buttons"> <!-- wp:button --> <div class="wp-block-button"> <a id="read-more-button" aria-labelledby="read-more-button heading-accessible-labels" class="wp-block-button__link wp-element-button" href=""> Read Report </a> </div> <!-- /wp:button --> </div>

Great! But WordPress just ain’t cool with that:

You can try to resolve the issue:

Le sigh. The Button block has to be converted to a Custom HTML block. Kinda defeats the whole visual editing thing that WordPress is so good at. I did a super quick search for a plugin that might add ARIA labelling options to certain blocks, but came up short. Seems like a ripe opportunity to make one or submit PRs for the blocks that could use those options.

Unchain My Inaccessibly-Labelled Heart originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Holiday Snowtacular 2022

Css Tricks - Tue, 12/13/2022 - 1:03pm

We’ve got ourselves a real holiday treat! Join host Alex Trost from the Frontend Horse community for the Holiday Snowtacular 2022 this Friday, December 16.

There’s a lineup of 12 awesome speakers — including Chris Coyier, Cassidy Williams, Kevin Powell, and Angie Jones — each discussing various front-end and web dev topics. It’s like the 12 days of Christmas, but wrapped up in a four-hour session for web nerds like us.

It’s a real good cause, too. The event is free, but includes fundraising Doctors Without Borders with a goal of reaching $20,000. You can donate here any time and anything you give will be matched by the event’s sponors. So, come for the front-end fun and help a great cause in the process.

To Shared LinkPermalink on CSS-Tricks

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

A Few Times Container Size Queries Would Have Helped Me Out

Css Tricks - Tue, 12/13/2022 - 3:53am

CSS Container Queries are still gaining traction and many of us are getting our hands wet with them, even if it’s for little experiments or whatnot. They’ve got great, but not quite full, browser support — enough to justify using them in some projects, but maybe not to the extent where we might be tempted to start replacing media queries from past projects with shiny new container size queries.

They sure are handy though! In fact, I’ve already run into a few situations where I really wanted to reach for them but just couldn’t overcome the support requirements. If I had been able to use them, this is how it would have looked in those situations.

All of the following demos will be best viewed in Chrome or Safari at the time of this writing. Firefox plans to ship support in Version 109.

Case 1: Card grid

You kind of had to expect this one, right? It’s such a common pattern that all of us seem to run into it at some point. But the fact is that container size queries would have been a huge time-saver for me with a better outcome had I been able to use them over standard media queries.

Let’s say you’ve been tasked with building this card grid with the requirement that each card needs to keep it’s 1:1 aspect ratio:

It’s tougher than it looks! The problem is that sizing a component’s contents on the viewport’s width leaves you at the mercy of how the component responds to the viewport — as well the way any other ancestor containers respond to it. If, for example, you want the font size of a card heading to reduce when the card hits a certain inline size there’s no reliable way to do it.

You could set the font size in vw units, I suppose, but the component is still tied to the browser’s viewport width. And that can cause problems when the card grid is used other in contexts that may not have the same breakpoints.

In my real-world project, I landed on a JavaScript approach that would:

  1. Listen for a resize event.
  2. Calculate the width of each card.
  3. Add an inline font size to each card based on its width.
  4. Style everything inside using em units.

Seems like a lot of work, right? But it is a stable solution to get the required scaling across different screen sizes in different contexts.

Container queries would have been so much better because they provide us with container query units, such as the cqw unit. You probably already get it, but 1cqw is equal to 1% of a container’s width. We also have the cqi unit that’s a measure of a container’s inline width, and cqb for a container’s block width. So, if we have a card container that is 500px wide, a 50cqw value computes to 250px.

If I had been able to use container queries in my card grid, I could have set up the .card component as a container:

.card { container: card / size; }

Then I could have set an inner wrapper with padding that scales at 10% of the .card‘s width using the cqw unit:

.card__inner { padding: 10cqw; }

That’s a nice way to scale the spacing between the card’s edges and its contents consistently no matter where the card is used at any given viewport width. No media queries required!

Another idea? Use cqw units for the font size of the inner contents, then apply padding in em units:

.card__inner { font-size: 5cqw; padding: 2em; }

5cqw is an arbitrary value — just one that I settled on. That padding is still equal to 10cqw since the em unit is relative to the .card__inner font size!

Did you catch that? The 2em is relative to the 5cqw font size that is set on the same container. Containers work different than what we’re used to, as em units are relative to the same element’s font-size value. But what I quickly noticed is that container query units relate to the nearest parent that is also a container.

For example, 5cqw does not scale based on the .card element’s width in this example:

.card { container: card / size; container-name: card; font-size: 5cqw; }

Rather, it scales to whatever the nearest parent that’s defined as a container. That’s why I set up a .card__inner wrapper.

CodePen Embed Fallback Case 2: Alternating layout

I needed yet another card component in a different project. This time, I needed the card to transition from a landscape layout to a portrait layout… then back to landscape, and back to portrait again as the screen gets smaller.

I did the dirty work of making this component go to portrait at those two specific viewport ranges (shout out to the new media query range syntax!), but again, the problem is that it is then locked to the media queries set on it, its parent, and anything else that might respond to the viewport’s width. We want something that works in any condition without worrying about wondering where the content is going to break!

Container queries would have made this a breeze, thanks to the @container rule:

.info-card { container-type: inline-size; container-name: info-card; } @container info-card (max-width: 500px) { .info-card__inner { flex-direction: column; } }

One query, infinite fluidity:

CodePen Embed Fallback

But hold on! There’s something you might want to watch out for. Specifically, it could be difficult to use a container query like this within a prop-based design system. For example, this .info-card component could contain child components that rely on props to change their appearance.

Why’s that a big deal? The card’s portrait layout might require the alternate styling but you can’t change JavaScript props with CSS. As such, you risk duplicating the required styles. I actually touched on this and how to work around it in another article. If you need to use container queries for a significant amount of your styling, then you may need to base your entire design system around them rather than trying to shoehorn them into an existing design system that’s heavy on media queries.

Case 3: SVG strokes

Here’s another super common pattern I’ve recently used where container size queries would have resulted in a more polished product. Say you have an icon locked up with a heading:

<h2> <svg> <!-- SVG stuff --> </svg> Heading </h2>

It’s pretty straightforward to scale the icon with the title’s size, even without media queries. The problem, though, is that the SVG’s stroke-width might get too thin to be noticed all that well at a smaller size, and perhaps catch too much attention with a super thick stroke at a larger size.

I’ve had to create and apply classes to each icon instance to determine its size and stroke width. That’s OK if the icon is next to a heading that’s styled with a fixed font size, I guess, but it’s not so great when working with fluid type that constantly changes.

The heading’s font size might be based on the viewport’s width, so the SVG icon needs to adjust accordingly where its stroke works at any size. You could make the stroke width relative to the heading’s font-size by setting it in em units. But if you have a specific set of stroke sizes that you need to stick to, then this wouldn’t work because it otherwise scales linearly — there’s no way to adjust it to a specific stroke-width value at certain points without resorting to media queries on the viewport width.

But here’s what I would have done if I had the luxury of container queries at that time:

.icon { container: icon / size; width: 1em; height: 1em; } .icon svg { width: 100%; height: 100%; fill: none; stroke: #ccc; stroke-width: 0.8; } @container icon (max-width: 70px) { .icon svg { stroke-width: 1.5; } } @container icon (max-width: 35px) { .icon svg { stroke-width: 3; } }

Compare the implementations and see how the container query version snaps the SVG’s stroke to the specific widths I want based on the container’s width.

CodePen Embed Fallback Bonus: Other types of container size queries

OK, so I haven’t actually run into this on a real project. But as I was combing through information on container queries, I noticed that there are additional things we can query on a container that are related to the container’s size or physical dimensions.

Most examples I’ve seen query the width, max-width, and min-width, height, block-size, and inline-size as I’ve been doing throughout this article.

@container info-card (max-width: 500px) { .info-card__inner { flex-direction: column; } }

But MDN outlines two more things we can query against. One is orientation which makes perfect sense because we use it all the time in media queries. It’s no different with container queries:

@media screen (orientation: landscape) { .info-card__inner { /* Style away! */ } } @container info-card (orientation: landscape) { .info-card__inner { /* Style away! */ } }

The other? It’s aspect-ratio, believe it or not:

@container info-card (aspect-ratio: 3/2) { .info-card__inner { /* Style away! */ } }

Here’s an editable demo to play around with both examples:

CodePen Embed Fallback

I haven’t really found a good use case for either of these yet. If you have any ideas or feel like it could’ve helped you in your projects, let me know in the comments!

A Few Times Container Size Queries Would Have Helped Me Out originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Setting up a screen reader testing environment on your computer

Css Tricks - Mon, 12/12/2022 - 10:56am

Sara Soueidan with everything you need, from what screen reading options are out there all the way to setting up virtual machines for them, installing them, and confguring keyboard options. It’s truly a one-stop reference that pulls together disparate tips for getting the most out of your screen reading accessibility testing.

Thanks, Sara, for putting together this guide, and especially doing so while making no judgments or assumptions about what someone may or may not know about accessibility testing. The guide is just one part of Sara’s forthcoming Practical Accessibility course, which is available for pre-order.

To Shared LinkPermalink on CSS-Tricks

Setting up a screen reader testing environment on your computer originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Saving Settings for a Custom WordPress Block in the Block Editor

Css Tricks - Mon, 12/12/2022 - 4:06am

We’ve accomplished a bunch of stuff in this series! We created a custom WordPress block that fetches data from an external API and renders it on the front end. Then we took that work and extended it so the data also renders directly in the WordPress block editor. After that, we created a settings UI for the block using components from the WordPress InspectorControls package.

There’s one last bit for us to cover and that’s saving the settings options. If we recall from the last article, we’re technically able to “save” our selections in the block settings UI, but those aren’t actually stored anywhere. If we make a few selections, save them, then return to the post, the settings are completely reset.

Let’s close the loop and save those settings so they persist the next time we edit a post that contains our custom block!

Working With External APIs in WordPress Blocks Saving settings attributes

We’re working with an API that provides us with soccer football team ranking and we’re using it to fetch for displaying rankings based on country, league, and season. We can create new attributes for each of those like this:

// index.js attributes: { data: { type: "object", }, settings: { type: "object", default: { country: { type: "string", }, league: { type: "string", }, season: { type: "string", }, }, }, },

Next, we need to set the attributes from LeagueSettings.js. Whenever a ComboboxControl is updated in our settings UI, we need to set the attributes using the setAttributes() method. This was more straightfoward when we were only working with one data endpoint. But now that we have multiple inputs, it’s a little more involved.

This is how I am going to organize it. I am going to create a new object in LeagueSettings.js that follows the structure of the settings attributes and their values.

// LeagueSettings.js let localSettings = { country:, league: attributes.settings.league, season: attributes.settings.season, };

I am also going to change the initial state variables from null to the respective settings variables.

// LeagueSettings.js const [country, setCountry] = useState(; const [league, setLeague] = useState(attributes.settings.league); const [season, setSeason] = useState(attributes.settings.season);

In each of the handle______Change(), I am going to create a setLocalAttributes() that has an argument that clones and overwrites the previous localSettings object with the new country, league, and season values. This is done using the help of the spread operator.

// LeagueSettings.js function handleCountryChange(value) { // Initial code setLocalAttributes({ ...localSettings, country: value }); // Rest of the code } function handleLeagueChange(value) { // Initial code setLocalAttributes({ ...localSettings, league: value }); // Rest of the code } function handleSeasonChange(value) { // Initial code setLocalAttributes({ ...localSettings, season: value }); // Rest of the code }

We can define the setLocalAttributes() like this:

// LeagueSettings.js function setLocalAttributes(value) { let newSettings = Object.assign(localSettings, value); localSettings = { ...newSettings }; setAttributes({ settings: localSettings }); }

So, we’re using Object.assign() to merge the two objects. Then we can clone the newSettings object back to localSettings because we also need to account for each settings attribute when there a new selection is made and a change occurs.

Finally, we can use the setAttributes() as we do normally to set the final object. You can confirm if the above attributes are changing by updating the selections in the UI.

Another way to confirm is to do a console.log() in DevTools to find the attributes.

Look closer at that screenshot. The values are stored in attributes.settings. We are able to see it happen live because React re-renders every time we make a change in the settings, thanks to the useState() hook.

Displaying the values in the blocks settings UI

It isn’t very useful to store the setting values in the control options themselves since each one is dependent on the other setting value (e.g. rankings by league depends on which season is selected). But it is very useful in situations where the settings values are static and where settings are independent of each other.

Without making the current settings complicated, we can create another section inside the settings panel that shows the current attributes. You can choose your way to display the settings values but I am going to import a Tip component from the @wordpress/components package:

// LeagueSettings.js import { Tip } from "@wordpress/components";

While I’m here, I am going to do a conditional check for the values before displaying them inside the Tip component:

<Tip> {country && league && season && ( <> <h2>Current Settings: </h2> <div className="current-settings"> <div className="country"> Country: {} </div> <div className="league"> League: {attributes.settings.league} </div> <div className="season"> Season: {attributes.settings.season} </div> </div> </> )} </Tip>

Here’s how that winds up working in the block editor:

API data is more powerful when live data can be shown without having to manually update them each and every time. We will look into that in the next installment of this series.

Saving Settings for a Custom WordPress Block in the Block Editor originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS Infinite Slider Flipping Through Polaroid Images

Css Tricks - Fri, 12/09/2022 - 4:26am

In the last article, we made a pretty cool little slider (or “carousel” if that’s what you prefer) that rotates in a circular direction. This time we are going to make one that flips through a stack of Polaroid images.

CodePen Embed Fallback

Cool right? Don’t look at the code quite yet because there’s a lot to unravel. Join me, will ya?

CSS Sliders series The basic setup

Most of the HTML and CSS for this slider is similar to the circular one we made last time. In fact, we’re using the exact same markup:

<div class="gallery"> <img src="" alt=""> <img src="" alt=""> <img src="" alt=""> <img src="" alt=""> </div>

And this is the basic CSS that sets our parent .gallery container as a grid where all the images are stacked one on top of one another:

.gallery { display: grid; width: 220px; /* controls the size */ } .gallery > img { grid-area: 1 / 1; width: 100%; aspect-ratio: 1; object-fit: cover; border: 10px solid #f2f2f2; box-shadow: 0 0 4px #0007; }

Nothing complex so far. Even for the Polaroid-like style for the images, all I’m using is some border and box-shadow. You might be able to do it better, so feel free to play around with those decorative styles! We’re going to put most of our focus on the animation, which is the trickiest part.

What’s the trick?

The logic of this slider relies on the stacking order of the images — so yes, we are going to play with z-index. All of the images start with the same z-index value (2) which will logically make the last image on the top of the stack.

We take that last image and slide it to the right until it reveals the next image in the stack. Then we decrease the image’s z-index value then we slide it back into the deck. And since its z-index value is lower than the rest of the images, it becomes the last image in the stack.

Here is a stripped back demo that shows the trick. Hover the image to activate the animation:

CodePen Embed Fallback

Now, imagine the same trick applied to all the images. Here’s the pattern if we’re using the :nth-child() pseudo-selector to differentiate the images:

  • We slide the last image (N). The next image is visible (N - 1).
  • We slide the next image (N - 1). The next image is visible (N - 2)
  • We slide the next image (N - 2). The next image is visible (N - 3)
  • (We continue the same process until we reach the first image)
  • We slide the first image (1). The last image (N) is visible again.

That’s our infinite slider!

Dissecting the animation

If you remember the previous article, I defined only one animation and played with delays to control each image. We will be doing the same thing here. Let’s first try to visualize the timeline of our animation. We will start with three images, then generalize it later for any number (N) of images.

Our animation is divided into three parts: “slide to right”, “slide to left” and “don’t move”. We can easily identify the delay between each image. If we consider that the first image starts at 0s, and the duration is equal to 6s, then the second one will start at -2s and the third one at -4s.

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */ .gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */

We can also see that the “don’t move” part takes two-thirds of the whole animation (2*100%/3) while the “slide to right” and “slide to left” parts take one-third of it together — so, each one is equal to 100%/6 of the total animation.

We can write our animation keyframes like this:

@keyframes slide { 0% { transform: translateX(0%); } 16.67% { transform: translateX(120%); } 33.34% { transform: translateX(0%); } 100% { transform: translateX(0%); } }

That 120% is an arbitrary value. I needed something bigger than 100%. The images need to slide to the right away from the rest of the images. To do that, it needs to move by at least 100% of its size. That’s why I went 120% — to gain some extra space.

Now we need to consider the z-index. Don’t forget that we need to update the image’s z-index value after it slides to the right of the pile, and before we slide it back to the bottom of the pile.

@keyframes slide { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */ 33.34% { transform: translateX(0%); z-index: 1; } 100% { transform: translateX(0% ); z-index: 1; } }

Instead of defining one state at the 16.67% (100%/6) point in the timeline, we are defining two states at nearly identical points (16.66% and 16.67%) where the z-index value decreases before we slide back the image back to the deck.

Here’s what happens when we pull of all that together:

CodePen Embed Fallback

Hmmm, the sliding part seems to work fine, but the stacking order is all scrambled! The animation starts nicely since the top image is moving to the back… but the subsequent images don’t follow suit. If you notice, the second image in the sequence returns to the top of the stack before the next image blinks on top of it.

We need to closely follow the z-index changes. Initially, all the images have are z-index: 2. That means the stacking order should go…

Our eyes &#x1f440; --> 3rd (2) | 2nd (2) | 1st (2)

We slide the third image and update its z-index to get this order:

Our eyes &#x1f440; --> 2nd (2) | 1st (2) | 3rd (1)

We do the same with the second one:

Our eyes &#x1f440; --> 1st (2) | 3rd (1) | 2nd (1)

…and the first one:

Our eyes &#x1f440; --> 3rd (1) | 2nd (1) | 1st (1)

We do that and everything seems to be fine. But in reality, it’s not! When the first image is moved to the back, the third image will start another iteration, meaning it returns to z-index: 2:

Our eyes &#x1f440; --> 3rd (2) | 2nd (1) | 1st (1)

So, in reality we never had all the images at z-index: 2 at all! When the images aren’t moving (i.e., the “don’t move” part of the animation) the z-index is 1. If we slide the third image and update its z-index value from 2 to 1, it will remain on the top! When all the images have the same z-index, the last one in the source order — our third image in this case — is on top of the stack. Sliding the third image results in the following:

Our eyes &#x1f440; --> 3rd (1) | 2nd (1) | 1st (1)

The third image is still on the top and, right after it, we move the second image to the top when its animation restarts at z-index: 2:

Our eyes &#x1f440; --> 2nd (2) | 3rd (1) | 1st (1)

Once we slide it, we get:

Our eyes &#x1f440; --> 3rd (1) | 2nd (1) | 1st (1)

Then the first image will jump on the top:

Our eyes &#x1f440; --> 1st(2) | 3rd (1) | 2nd (1) OK, I am lost. All the logic is wrong then?

I know, it’s confusing. But our logic is not completely wrong. We only have to rectify the animation a little to make everything work the way we want. The trick is to correctly reset the z-index.

Let’s take the situation where the third image is on the top:

Our eyes &#x1f440; --> 3rd (2) | 2nd (1) | 1st (1)

We saw that sliding the third image and changing its z-index keeps it on top. What we need to do is update the z-index of the second image. So, before we slide the third image away from the deck, we update the z-index of the second image to 2.

In other words, we reset the z-index of the second image before the animation ends.

The green plus symbol represents increasing z-index to 2, and the red minus symbol correlates to z-index: 1. The second image starts with z-index: 2, then we update it to 1 when it slides away from the deck. But before the first image slides away from the deck, we change the z-index of the second image back to 2. This will make sure both images have the same z-index, but still, the third one will remain on the top because it appears later in the DOM. But after the third image slides and its z-index is updated, it moves to the bottom.

This two-thirds through the animation, so let’s update our keyframes accordingly:

@keyframes slide { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */ 33.34% { transform: translateX(0%); z-index: 1; } 66.33% { transform: translateX(0%); z-index: 1; } 66.34% { transform: translateX(0%); z-index: 2; } /* and also here */ 100% { transform: translateX(0%); z-index: 2; } } CodePen Embed Fallback

A little better, but still not quite there. There’s another issue…

Oh no, this will never end!

Don’t worry, we are not going to change the keyframes again because this issue only happens when the last image is involved. We can make a “special” keyframe animation specifically for the last image to fix things up.

When the first image is on the top, we have the following situation:

Our eyes &#x1f440; --> 1st (2) | 3rd (1) | 2nd (1)

Considering the previous adjustment we made, the third image will jump on the top before the first image slides. It only happens in this situation because the next image that moves after the first image is the last image which has a higher order in the DOM. The rest of the images are fine because we have N, then N - 1, then we go from 3 to 2, and 2 to 1… but then we go from 1 to N.

To avoid that, we will use the following keyframes for the last image:

@keyframes slide-last { 0% { transform: translateX(0%); z-index: 2;} 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */ 33.34% { transform: translateX(0%); z-index: 1; } 83.33% { transform: translateX(0%); z-index: 1; } 83.34% { transform: translateX(0%); z-index: 2; } /* and also here */ 100% { transform: translateX(0%); z-index: 2; } }

We reset the z-index value 5/6 through the animation (instead of two-thirds) which is when the first image is out of the pile. So we don’t see any jumping!

CodePen Embed Fallback

TADA! Our infinite slider is now perfect! Here’s our final code in all its glory:

.gallery > img { animation: slide 6s infinite; } .gallery > img:last-child { animation-name: slide-last; } .gallery > img:nth-child(2) { animation-delay: -2s; } .gallery > img:nth-child(3) { animation-delay: -4s; } @keyframes slide { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } 33.34% { transform: translateX(0%); z-index: 1; } 66.33% { transform: translateX(0%); z-index: 1; } 66.34% { transform: translateX(0%); z-index: 2; } 100% { transform: translateX(0%); z-index: 2; } } @keyframes slide-last { 0% { transform: translateX(0%); z-index: 2; } 16.66% { transform: translateX(120%); z-index: 2; } 16.67% { transform: translateX(120%); z-index: 1; } 33.34% { transform: translateX(0%); z-index: 1; } 83.33% { transform: translateX(0%); z-index: 1; } 83.34% { transform: translateX(0%); z-index: 2; } 100% { transform: translateX(0%); z-index: 2; } } Supporting any number of images

Now that our animation works for three images, let’s make it work for any number (N) of images. But first, we can optimize our work a little by splitting the animation up to avoid redundancy:

.gallery > img { z-index: 2; animation: slide 6s infinite, z-order 6s infinite steps(1); } .gallery > img:last-child { animation-name: slide, z-order-last; } .gallery > img:nth-child(2) { animation-delay: -2s; } .gallery > img:nth-child(3) { animation-delay: -4s; } @keyframes slide { 16.67% { transform: translateX(120%); } 33.33% { transform: translateX(0%); } } @keyframes z-order { 16.67%, 33.33% { z-index: 1; } 66.33% { z-index: 2; } } @keyframes z-order-last { 16.67%, 33.33% { z-index: 1; } 83.33% { z-index: 2; } }

Way less code now! We make one animation for the sliding part and another one for the z-index updates. Note that we use steps(1) on the z-index animation. That’s because I want to abruptly change the z-index value, unlike the sliding animation where we want smooth movement.

Now that the code is easier to read and maintain, we have a better view for figuring out how to support any number of images. What we need to do is update the animation delays and the percentages of the keyframes. The delay are easy because we can use the exact same loop we made in the last article to support multiple images in the circular slider:

@for $i from 2 to ($n + 1) { .gallery > img:nth-child(#{$i}) { animation-delay: calc(#{(1 - $i)/$n}*6s); } }

That means we’re moving from vanilla CSS to Sass. Next, we need to imagine how the timeline scale with N images. Let’s not forget that the animation happens in three phases:

After “slide to right” and “slide to left”, the image should stay put until the rest of the images go through the sequence. So the “don’t move” part needs to take the same amount of time as (N - 1) as “slide to right” and “slide to left”. And within one iteration, N images will slide. So, “slide to right” and “slide to left” both take 100%/N of the total animation timeline. The image slides away from the pile at (100%/N)/2 and slides back at 100%/N .

We can change this:

@keyframes slide { 16.67% { transform: translateX(120%); } 33.33% { transform: translateX(0%); } }

…to this:

@keyframes slide { #{50/$n}% { transform: translateX(120%); } #{100/$n}% { transform: translateX(0%); } }

If we replace N with 3, we get 16.67% and 33.33% when there are 3 images in the stack. It’s the same logic with the stacking order where we will have this:

@keyframes z-order { #{50/$n}%, #{100/$n}% { z-index: 1; } 66.33% { z-index: 2; } }

We still need to update the 66.33% point. That’s supposed to be where the image resets its z-index before the end of the animation. At that same time, the next image starts to slide. Since the sliding part takes 100%/N, the reset should happen at 100% - 100%/N:

@keyframes z-order { #{50/$n}%, #{100/$n}% { z-index: 1; } #{100 - 100/$n}% { z-index: 2; } }

But for our z-order-last animation to work, it should happen a bit later in the sequence. Remember the fix we did for the last image? Resetting the z-index value needs to happen when the first image is out of the pile and not when it starts sliding. We can use the same reasoning here in our keyframes:

@keyframes z-order-last { #{50/$n}%, #{100/$n}% { z-index: 1; } #{100 - 50/$n}% { z-index: 2; } }

We are done! Here’s what we get when using five images:

CodePen Embed Fallback

We can add a touch of rotation to make things a bit fancier:

CodePen Embed Fallback

All I did is append rotate(var(--r)) to the transform property. Inside the loop, --r is defined with a random angle:

@for $i from 1 to ($n + 1) { .gallery > img:nth-child(#{$i}) { --r: #{(-20 + random(40))*1deg}; /* a random angle between -20deg and 20deg */ } }

The rotation creates small glitches as we can sometimes see some of the images jumping to the back of the stack, but it’s not a big deal.

Wrapping up

All that z-index work was a big balancing act, right? If you were unsure how stacking order work before this exercise, then you probably have a much better idea now! If you found some of the explanations hard to follow, I highly recommend you to take another read of the article and map things out with pencil and paper. Try to illustrate each step of the animation using a different number of images to better understand the trick.

Last time, we used a few geometry tricks to create a circular slider that rotates back to the first image after a full sequence. This time, we accomplished a similar trick using z-index. In both cases, we didn’t duplicate any of the images to simulate a continuous animation, nor did we reach for JavaScript to help with the calculations.

Next time, we will make 3D sliders. Stay tuned!

CSS Infinite Slider Flipping Through Polaroid Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Animated Background Stripes That Transition on Hover

Css Tricks - Thu, 12/08/2022 - 5:36am

How often to do you reach for the CSS background-size property? If you’re like me — and probably lots of other front-end folks — then it’s usually when you background-size: cover an image to fill the space of an entire element.

Well, I was presented with an interesting challenge that required more advanced background sizing: background stripes that transition on hover. Check this out and hover it with your cursor:

CodePen Embed Fallback

There’s a lot more going on there than the size of the background, but that was the trick I needed to get the stripes to transition. I thought I’d show you how I arrived there, not only because I think it’s a really nice visual effect, but because it required me to get creative with gradients and blend modes that I think you might enjoy.

Let’s start with a very basic setup to keep things simple. I’m talking about a single <div> in the HTML that’s styled as a green square:

<div></div> div { width: 500px; height: 500px; background: palegreen; } Setting up the background stripes

If your mind went straight to a CSS linear gradient when you saw those stripes, then we’re already on the same page. We can’t exactly do a repeating gradient in this case since we want the stripes to occupy uneven amounts of space and transition them, but we can create five stripes by chaining five backgrounds on top of our existing background color and placing them to the top-right of the container:

div { width: 500px; height: 500px; background: linear-gradient(black, black) top right, linear-gradient(black, black) top 100px right, linear-gradient(black, black) top 200px right, linear-gradient(black, black) top 300px right, linear-gradient(black, black) top 400px right, palegreen; }

I made horizontal stripes, but we could also go vertical with the approach we’re covering here. And we can simplify this quite a bit with custom properties:

div { --gt: linear-gradient(black, black); --n: 100px; width: 500px; height: 500px; background: var(--gt) top right, var(--gt) top var(--n) right, var(--gt) top calc(var(--n) * 2) right, var(--gt) top calc(var(--n) * 3) right, var(--gt) top calc(var(--n) * 4) right, palegreen; }

So, the --gt value is the gradient and --n is a constant we’re using to nudge the stripes downward so they are offset vertically. And you may have noticed that I haven’t set a true gradient, but rather solid black stripes in the linear-gradient() function — that’s intentional and we’ll get to why I did that in a bit.

One more thing we ought to do before moving on is prevent our backgrounds from repeating; otherwise, they’ll tile and fill the entire space:

div { --gt: linear-gradient(black, black); --n: 100px; width: 500px; height: 500px; background: var(--gt) top right, var(--gt) top var(--n) right, var(--gt) top calc(var(--n) * 2) right, var(--gt) top calc(var(--n) * 3) right, var(--gt) top calc(var(--n) * 4) right, palegreen; background-repeat: no-repeat; }

We could have set background-repeat in the background shorthand, but I decided to break it out here to keep things easy to read.

Offsetting the stripes

We technically have stripes, but it’s pretty tough to tell because there’s no spacing between them and they cover the entire container. It’s more like we have a solid black square.

This is where we get to use the background-size property. We want to set both the height and the width of the stripes and the property supports a two-value syntax that allows us to do exactly that. And, we can chain those sizes by comma separating them the same way we did on background.

Let’s start simple by setting the widths first. Using the single-value syntax for background-size sets the width and defaults the height to auto. I’m using totally arbitrary values here, so set the values to what works best for your design:

div { --gt: linear-gradient(black, black); --n: 100px; width: 500px; height: 500px; background: var(--gt) top right, var(--gt) top var(--n) right, var(--gt) top calc(var(--n) * 2) right, var(--gt) top calc(var(--n) * 3) right, var(--gt) top calc(var(--n) * 4) right, palegreen; background-repeat: no-repeat; background-size: 60%, 90%, 70%, 40%, 10%; }

If you’re using the same values that I am, you’ll get this:

CodePen Embed Fallback

Doesn’t exactly look like we set the width for all the stripes, does it? That’s because of the auto height behavior of the single-value syntax. The second stripe is wider than the others below it, and it is covering them. We ought to set the heights so we can see our work. They should all be the same height and we can actually re-use our --n variable, again, to keep things simple:

div { --gt: linear-gradient(black, black); --n: 100px; width: 500px; height: 500px; background: var(--gt) top right, var(--gt) top var(--n) right, var(--gt) top calc(var(--n) * 2) right, var(--gt) top calc(var(--n) * 3) right, var(--gt) top calc(var(--n) * 4) right, palegreen; background-repeat: no-repeat; background-size: 60% var(--n), 90% var(--n), 70% var(--n), 40% var(--n), 10% var(--n); // HIGHLIGHT 15 }

Ah, much better!

CodePen Embed Fallback Adding gaps between the stripes

This is a totally optional step if your design doesn’t require gaps between the stripes, but mine did and it’s not overly complicated. We change the height of each stripe’s background-size a smidge, decreasing the value so they fall short of filling the full vertical space.

We can continue to use our --n variable, but subtract a small amount, say 5px, using calc() to get what we want.

background-size: 60% calc(var(--n) - 5px), 90% calc(var(--n) - 5px), 70% calc(var(--n) - 5px), 40% calc(var(--n) - 5px), 10% calc(var(--n) - 5px);

That’s a lot of repetition we can eliminate with another variable:

div { --h: calc(var(--n) - 5px); /* etc. */ background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h); } CodePen Embed Fallback Masking and blending

Now let’s swap the palegreen background color we’ve been using for visual purposes up to this point for white.

div { /* etc. */ background: var(--gt) top right, var(--gt) top var(--n) right, var(--gt) top calc(var(--n) * 2) right, var(--gt) top calc(var(--n) * 3) right, var(--gt) top calc(var(--n) * 4) right, #fff; /* etc. */ }

A black and white pattern like this is perfect for masking and blending. To do that, we’re first going to wrap our <div> in a new parent container and introduce a second <div> under it:

<section> <div></div> <div></div> </section>

We’re going to do a little CSS re-factoring here. Now that we have a new parent container, we can pass the fixed width and height properties we were using on our <div> over there:

section { width: 500px; height: 500px; }

I’m also going to use CSS Grid to position the two <div> elements on top of one another. This is the same trick Temani Afif uses to create his super cool image galleries. The idea is that we place both divs over the full container using the grid-area property and align everything toward the center:

section { display: grid; align-items: center; justify-items: center; width: 500px; height: 500px; } section > div { width: inherit; height: inherit; grid-area: 1 / 1; }

Now, check this out. The reason I used a solid gradient that goes from black to black earlier is to set us up for masking and blending the two <div> layers. This isn’t true masking in the sense that we’re calling the mask property, but the contrast between the layers controls what colors are visible. The area covered by white will remain white, and the area covered by black leaks through. MDN’s documentation on blend modes has a nice explanation of how this works.

To get that working, I’ll apply the real gradient we want to see on the first <div> while applying the style rules from our initial <div> on the new one, using the :nth-child() pseudo-selector:

div:nth-child(1) { background: linear-gradient(to right, red, orange); } div:nth-child(2) { --gt: linear-gradient(black, black); --n: 100px; --h: calc(var(--n) - 5px); background: var(--gt) top right, var(--gt) top var(--n) right, var(--gt) top calc(var(--n) * 2) right, var(--gt) top calc(var(--n) * 3) right, var(--gt) top calc(var(--n) * 4) right, white; background-repeat: no-repeat; background-size: 60% var(--h), 90% var(--h), 70% var(--h), 40% var(--h), 10% var(--h); }

If we stop here, we actually won’t see any visual difference from what we had before. That’s because we haven’t done the actual blending yet. So, let’s do that now using the screen blend mode:

div:nth-child(2) { /* etc. */ mix-blend-mode: screen; } CodePen Embed Fallback

I used a beige background color in the demo I showed at the beginning of this article. That slightly darker sort of off-white coloring allows a little color to bleed through the rest of the background:

CodePen Embed Fallback The hover effect

The last piece of this puzzle is the hover effect that widens the stripes to full width. First, let’s write out our selector for it. We want this to happen when the parent container (<section> in our case) is hovered. When it’s hovered, we’ll change the background size of the stripes contained in the second <div>:

/* When <section> is hovered, change the second div's styles */ section:hover > div:nth-child(2){ /* styles go here */ }

We’ll want to change the background-size of the stripes to the full width of the container while maintaining the same height:

section:hover > div:nth-child(2){ background-size: 100% var(--h); }

That “snaps” the background to full-width. If we add a little transition to this, then we see the stripes expand on hover:

section:hover > div:nth-child(2){ background-size: 100% var(--h); transition: background-size 1s; }

Here’s that final demo once again:

CodePen Embed Fallback

I only added text in there to show what it might look like to use this in a different context. If you do the same, then it’s worth making sure there’s enough contrast between the text color and the colors used in the gradient to comply with WCAG guidelines. And while we’re touching briefly on accessibility, it’s worth considering user preferences for reduced motion when it comes to the hover effect.

That’s a wrap!

Pretty neat, right? I certainly think so. What I like about this, too, is that it’s pretty maintainable and customizable. For example, we can alter the height, colors, and direction of the stripes by changing a few values. You might even variablize a few more things in there — like the colors and widths — to make it even more configurable.

I’m really interested if you would have approached this a different way. If so, please share in the comments! It’d be neat to see how many variations we can collect.

Animated Background Stripes That Transition on Hover originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Adding Box Shadows to WordPress Blocks and Elements

Css Tricks - Wed, 12/07/2022 - 3:59am

I stumbled across this tweet from Ana Segota looking for a way to add a CSS box-shadow to a button’s hover state in WordPress in the theme.json file.

Is it possible to add a box-shadow for the button on hover state in theme.json? #WordPress

— Ana Segota (@Ana_Segota) November 1, 2022

She’s asking because theme.json is where WordPress wants us to start moving basic styles for block themes. Traditionally, we’d do any and all styling in style.css when working in a “classic” theme. But with the default Twenty Twenty-Three (TT3) theme that recently shipped with WordPress 6.1 moving all of its styles to theme.json, we’re getting closer and closer to being able to do the same with our own themes. I covered this in great detail in a recent article.

I say “closer and closer” because there are still plenty of CSS properties and selectors that are unsupported in theme.json. For example, if you’re hoping to style something with like perspective-origin in theme.json, it just won’t happen — at least as I’m writing this today.

Ana is looking at box-shadow and, luckily for her, that CSS property is supported by theme.json as of WordPress 6.1. Her tweet is dated Nov. 1, the same exact day that 6.1 released. It’s not like support for the property was a headline feature in the release. The bigger headlines were more related to spacing and layout techniques for blocks and block themes.

Here’s how we can apply a box-shadow to a specific block — say the Featured Image block — in theme.json:

{ "version": 2, "settings": {}, // etc. "styles": { "blocks" :{ "core/post-featured-image": { "shadow": "10px 10px 5px 0px rgba(0, 0, 0, 0.66)" } } } }

Wondering if the new color syntax works? Me too! But when I tried — rgb(0 0 0 / 0.66) — I got nothing. Perhaps that’s already in the works or could use a pull request.

Easy, right? Sure, it’s way different than writing vanilla CSS in style.css and takes some getting used to. But it is indeed possible as of the most recent WordPress release.

And, hey, we can do the same thing to individual “elements”, like a button. A button is a block in and of itself, but it can also be a nested block within another block. So, to apply a box-shadow globally to all buttons, we’d do something like this in theme.json:

{ "version": 2, "settings": {}, // etc. "styles": { "elements": { "button": { "shadow": "10px 10px 5px 0px rgba(0,0,0,0.66)" } } } }

But Ana wants to add the shadow to the button’s :hover state. Thankfully, support for styling interactive states for certain elements, like buttons and links, using pseudo-classes — including :hover, :focus, :active, and :visited — also gained theme.json support in WordPress 6.1.

{ "version": 2, "settings": {}, // etc. "styles": { "elements": { "button": { ":hover": { "shadow": "10px 10px 5px 0px rgba(0,0,0,0.66)" } } } } }

If you’re using a parent theme, you can certainly override a theme’s styles in a child theme. Here, I am completely overriding TT3’s button styles.

View full code { "version": 2, "settings": {}, // etc. "styles": { "elements": { "button": { "border": { "radius": "0" }, "color": { "background": "var(--wp--preset--color--tertiary)", "text": "var(--wp--preset--color--contrast)" }, "outline": { "offset": "3px", "width": "3px", "style": "dashed", "color": "red" }, "typography": { "fontSize": "var(--wp--preset--font-size--medium)" }, "shadow": "5px 5px 5px 0px rgba(9, 30, 66, 0.25), 5px 5px 5px 1px rgba(9, 30, 66, 0.08)", ":hover": { "color": { "background": "var(--wp--preset--color--contrast)", "text": "var(--wp--preset--color--base)" }, "outline": { "offset": "3px", "width": "3px", "style": "solid", "color": "blue" } }, ":focus": { "color": { "background": "var(--wp--preset--color--contrast)", "text": "var(--wp--preset--color--base)" } }, ":active": { "color": { "background": "var(--wp--preset--color--secondary)", "text": "var(--wp--preset--color--base)" } } } } } }

Here’s how that renders:

The button’s natural state (left) and it’s hovered state (right) Another way to do it: custom styles

The recently released Pixl block theme provides another example of real-world usage of the box-shadow property in theme.json using an alternative method that defines custom values. In the theme, a custom box-shadow property is defined as .settings.custom.shadow:

{ "version": 2, "settings": { // etc. "custom": { // etc. "shadow": "5px 5px 0px -2px var(--wp--preset--color--background), 5px 5px var(--wp--preset--color--foreground)" }, // etc. } }

Then, later in the file, the custom shadow property is called on a button element:

{ "version": 2, "settings": { // etc. }, "styles": { "elements": { "button": { // etc. "shadow": "var(--wp--custom--shadow) !important", // etc. ":active": { // etc. "shadow": "2px 2px var(--wp--preset--color--primary) !important" } }, // etc. } }

I’m not totally sure about the use of !important in this context. My hunch is that it’s an attempt to prevent overriding those styles using the Global Styles UI in the Site Editor, which has high specificity than styles defined in theme.json. Here’s an anchored link to more information from my previous article on managing block theme styles.

Update: Turns out there was a whole discussion about this in Pull Request #34689, which notes that it was addressed in WordPress 5.9.

And there’s more…

In addition to shadows, the CSS outline property also gained theme.json support in WordPress 6.1 and can be applied to buttons and their interactive states. This GitHub PR shows a good example.

"elements": { "button": { "outline": { "offset": "3px", "width": "3px", "style": "dashed", "color": "red" }, ":hover": { "outline": { "offset": "3px", "width": "3px", "style": "solid", "color": "blue" } } } }

You can also find the real examples of how the outline property works in other themes, including Loudness, Block Canvas, and Blockbase.

Wrapping up

Who knew there was so much to talk about with a single CSS property when it comes to block theming in WordPress 6.1? We saw the officially supported methods for setting a box-shadow on blocks and individual elements, including the interactive states of a button element. We also checked out how we could override shadows in a child theme. And, finally, we cracked open a real-world example that defines and sets shadows in a custom property.

You can find more detailed in-depth discussions about the WordPress and it’s box-shadow implementation in this GitHub PR. There is also a GitHub proposal for adding UI directly in WordPress to set shadow values on blocks — you can jump directly to an animated GIF showing how that would work.

Speaking of which, Justin Tadlock recently developed a block that renders a progress bar and integrated box shadow controls into it. He shows it off in this video:

More information

If you’d like to dig deeper into the box-shadow and other CSS properties that are supported by the theme.json file in a block theme, here are a couple of resources you can use:

Adding Box Shadows to WordPress Blocks and Elements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS is OK, I guess.

Css Tricks - Tue, 12/06/2022 - 5:37am

Nothing but ear-to-ear smiles as I was watching this video from @quayjn on YouTube. (No actual name in the byline, though I think it’s Brian Katz if my paper trail is correct).

The best is this Pen you can use to sing along…

CodePen Embed Fallback

The little song Una did for memorizing for JavaScript’s map(), filter(), and reduce()methods at the end of this article comes to mind for sure.

To Shared LinkPermalink on CSS-Tricks

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

Does WWW still belong in URLs?

Css Tricks - Mon, 12/05/2022 - 5:20am

For years, a small pedantry war has been raging in our address bars. In one corner are brands like Google, Instagram, and Facebook. This group has chosen to redirect to In the opposite corner: GitHub, DuckDuckGo, and Discord. This group has chosen to do the reverse and redirect to

Does “WWW” belong in a URL? Some developers hold strong opinions on the subject. We’ll explore arguments for and against it after a bit of history.

What’s with the Ws?

The three Ws stand for “World Wide Web”, a late-1980s invention that introduced the world to browsers and websites. The practice of using “WWW” stems from a tradition of naming subdomains after the type of service they provide:

  • a web server at
  • an FTP server at
  • an IRC server at
WWW-less domain concern 1: Leaking cookies to subdomains

Critics of “WWW-less” domains have pointed out that in certain situations, would be able to read cookies set by This may be undesirable if, for example, you are a web hosting provider that lets clients operate subdomains on your domain. While the concern is valid, the behavior was specific to Internet Explorer.

RFC 6265 standardizes how browsers treat cookies and explicitly calls out this behavior as incorrect.

Another potential source of leaks is the Domain value of any cookies set by If the Domain value is explicitly set to, the cookies will also be exposed to its subdomains.

Cookie valueExposed to example.comExposed to subdomain.example.comsecret=data✅❌secret=data;✅✅

In conclusion, as long as you don’t explicitly set a Domain value and your users don’t use Internet Explorer, no cookie leaks should occur.

WWW-less domain concern 2: DNS headaches

Sometimes, a “WWW-less” domain may complicate your Domain Name System (DNS) setup.

When a user types into their browser’s address bar, the browser needs to know the Internet Protocol (IP) address of the web server they’re trying to visit. The browser requests this IP address from your domain’s nameservers – usually indirectly through the DNS servers of the user’s Internet Service Provider (ISP). If your nameservers are configured to respond with an A record containing the IP address, a “WWW-less” domain will work fine.

In some cases, you may want to instead use a Canonical Name (CNAME) record for your website. Such a record can declare that is an alias of, which tells the user’s browser to instead look up the IP address of and send the HTTP request there.

Notice that the example above used a WWW subdomain. It’s not possible to define a CNAME record for As per RFC 1912, CNAME records cannot coexist with other records. If you tried to define a CNAME record for, a MX (Mail Exchanger) record for would not be allowed to exist. As a result, it would not be possible to accept mail on

Some DNS providers will allow you to work around this limitation. Cloudflare calls their solution CNAME flattening. With this technique, domain administrators configure a CNAME record, but their nameservers will expose an A record.

For instance, if the administrator configures a CNAME record for pointing to, and an A record for exists pointing to, then Cloudflare would expose an A record for pointing to

In conclusion, while the concern is valid for domain owners who wish to use CNAME records, certain DNS providers now offer a suitable workaround.

WWW-less benefits

Most of the arguments against WWW are practical or cosmetic. “No-WWW” advocates have argued that it’s easier to say and type than (which may be less confusing for less tech-savvy users).

Opponents of the WWW subdomain have also pointed out that dropping it comes with a humble performance advantage. Website owners could shave 4 bytes off each HTTP request by doing so. While these savings could add up for high-traffic websites like Facebook, bandwidth generally isn’t a scarce resource.

WWW benefits

One practical argument in favor of WWW is in situations with newer top-level domains. For example, is immediately recognizable as a web address when isn’t. This is less of a concern for sites that have recognizable top-level domains like .com.

Impact on your search engine ranking

The current consensus is that your choice does not influence your search engine performance. If you wish to migrate from one to the other, you’ll want to configure permanent redirects (HTTP 301) instead of temporary ones (HTTP 302). Permanent redirects ensure that the SEO value of your old URLs transfers to the new ones.

Tips for supporting both

Sites typically pick either or as their official website and configure HTTP 301 redirects for the other. In theory, it is possible to support both and In practice, the costs may outweigh the benefits.

From a technical perspective, you’ll want to verify that your tech stack can handle it. Your content management system (CMS) or statically generated site would have to output internal links as relative URLs to preserve the visitor’s preferred hostname. Your analytics tools may log traffic to both hostnames separately unless you can configure the hostnames as aliases.

Lastly, you’ll need to take an extra step to safeguard your search engine performance. Google will consider the “WWW” and “non-WWW” versions of a URL to be duplicate content. To deduplicate content in its search index, Google will display whichever of the two it thinks the user will prefer – for better or worse.

To preserve control over how you appear in Google, it recommends inserting canonical link tags. First, decide which hostname will be the official (canonical) one.

For example, if you pick, you will have to insert the following snippet in the <head> tag on

<link href="" rel="canonical">

This snippet indicates to Google that the “WWW-less” variant represents the same content. In general, Google will prefer the version you’ve marked as canonical in search results, which would be the “WWW” variant in this example.


Despite intense campaigning on either side, both approaches remain valid as long as you are aware of the benefits and limitations. To cover all your bases, be sure to set up permanent redirects from one to the other and you’re all set.

The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program.

Does WWW still belong in URLs? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Wed, 12/31/1969 - 2:00pm
Syndicate content
©2003 - Present Akamai Design & Development.