Tech News

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="https://webaim.org/projects/million/"> 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: attributes.settings.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(attributes.settings.country); 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: {attributes.settings.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.

Steven Heller’s Font of the Month: Geetype

Typography - Wed, 12/07/2022 - 1:15pm

Read the book, Typographic Firsts

Steven Heller takes a closer look at Geetype’s magnificent, bold and curvaceous Geetype!

The post Steven Heller’s Font of the Month: Geetype appeared first on I Love Typography.

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 example.com to www.example.com. In the opposite corner: GitHub, DuckDuckGo, and Discord. This group has chosen to do the reverse and redirect www.example.com to example.com.

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 www.example.com
  • an FTP server at ftp.example.com
  • an IRC server at irc.example.com
WWW-less domain concern 1: Leaking cookies to subdomains

Critics of “WWW-less” domains have pointed out that in certain situations, subdomain.example.com would be able to read cookies set by example.com. 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 example.com. If the Domain value is explicitly set to example.com, the cookies will also be exposed to its subdomains.

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

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 example.com 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 www.example.com is an alias of example123.somecdnprovider.com, which tells the user’s browser to instead look up the IP address of example123.somecdnprovider.com 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 example.com. As per RFC 1912, CNAME records cannot coexist with other records. If you tried to define a CNAME record for example.com, a MX (Mail Exchanger) record for example.com would not be allowed to exist. As a result, it would not be possible to accept mail on @example.com.

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 example.com pointing to example123.somecdnprovider.com, and an A record for example123.somecdnprovider.com exists pointing to 1.2.3.4, then Cloudflare would expose an A record for example.com pointing to 1.2.3.4.

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 example.com than www.example.com (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, www.example.miami is immediately recognizable as a web address when example.miami 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 example.com or www.example.com as their official website and configure HTTP 301 redirects for the other. In theory, it is possible to support both www.example.com and example.com. 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 www.example.com, you will have to insert the following snippet in the <head> tag on https://example.com/my-article:

<link href="https://www.example.com/my-article" 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.

Conclusion

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.

CSS Infinite and Circular Rotating Image Slider

Css Tricks - Fri, 12/02/2022 - 4:12am

Image sliders (also called carousels) are everywhere. There are a lot of CSS tricks to create the common slider where the images slide from left to right (or the opposite). It’s the same deal with the many JavaScript libraries out there that create fancy sliders with complex animations. We are not going to do any of that in this post.

Through a little series of articles, we are going to explore some fancy and uncommon CSS-only sliders. If you are of tired seeing the same ol’ classic sliders, then you are in the right place!

CSS Sliders series

For this first article, we will start with something I call the “circular rotating image slider”:

CodePen Embed Fallback

Cool right? let’s dissect the code!

The HTML markup

If you followed my series of fancy image decorations or CSS grid and custom shapes, then you know that my first rule is to work with the smallest HTML possible. I always try hard to find CSS solutions before cluttering my code with a lot <div>s and other stuff.

The same rule applies here — our code is nothing but a list of images in a container.

Let’s say we’re working with four images:

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

That’s it! Now let’s move to the interesting part of the code. But first, we’re going to dive into this to understand the logic of how our slider works.

How does it work?

Here is a video where I remove overflow: hidden from the CSS so we can better understand how the images are moving:

It’s like our four images are placed on a large circle that rotates counter-clockwise.

All the images have the same size (denoted by S in the figure). Note the blue circle which is the circle that intersects with the center of all the images and has a radius (R). We will need this value later for our animation. R is equal to 0.707 * S. (I’m going to skip the geometry that gives us that equation.)

Let’s write some CSS!

We will be using CSS Grid to place all the images in the same area above each other:

.gallery { --s: 280px; /* control the size */ display: grid; width: var(--s); aspect-ratio: 1; padding: calc(var(--s) / 20); /* we will see the utility of this later */ border-radius: 50%; } .gallery > img { grid-area: 1 / 1; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

Nothing too complex so far. The tricky part is the animation.

We talked about rotating a big circle, but in reality, we will rotate each image individually creating the illusion of a big rotating circle. So, let’s define an animation, m, and apply it to the image elements:

.gallery > img { /* same as before */ animation: m 8s infinite linear; transform-origin: 50% 120.7%; } @keyframes m { 100% { transform: rotate(-360deg); } }

The main trick relies on that highlighted line. By default, the CSS transform-origin property is equal to center (or 50% 50%) which makes the image rotate around its center, but we don’t need it to do that. We need the image to rotate around the center of the big circle that contains our images hence the new value for transform-origin.

Since R is equal to 0.707 * S, we can say that R is equal to 70.7% of the image size. Here’s a figure to illustrate how we got the 120.7% value:

Let’s run the animation and see what happens:

CodePen Embed Fallback

I know, I know. The result is far from what we want, but in reality we are very close. It may looks like there’s just one image there, but don’t forget that we have stacked all the images on top of each other. All of them are rotating at the same time and only the top image is visible. What we need is to delay the animation of each image to avoid this overlap.

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

Things are already getting better!

CodePen Embed Fallback

If we hide the overflow on the container we can already see a slider, but we will update the animation a little so that each image remains visible for a short period before it moves along.

We’re going to update our animation keyframes to do just that:

@keyframes m { 0%, 3% { transform: rotate(0); } 22%, 27% { transform: rotate(-90deg); } 47%, 52% { transform: rotate(-180deg); } 72%, 77% { transform: rotate(-270deg); } 98%, 100% { transform: rotate(-360deg); } }

For each 90deg (360deg/4, where 4 is the number of images) we will add a small pause. Each image will remain visible for 5% of the overall duration before we slide to the next one (27%-22%, 52%-47%, etc.). I’m going to update the animation-timing-function using a cubic-bezier() function to make the animation a bit fancier:

CodePen Embed Fallback

Now our slider is perfect! Well, almost perfect because we are still missing the final touch: the colorful circular border that rotates around our images. We can use a pseudo-element on the .gallery wrapper to make it:

.gallery { padding: calc(var(--s) / 20); /* the padding is needed here */ position: relative; } .gallery::after { content: ""; position: absolute; inset: 0; padding: inherit; /* Inherits the same padding */ border-radius: 50%; background: repeating-conic-gradient(#789048 0 30deg, #DFBA69 0 60deg); mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite: exclude; } .gallery::after, .gallery >img { animation: m 8s infinite cubic-bezier(.5, -0.2, .5, 1.2); }

I have created a circle with a repeating conic gradient for the background while using a masking trick that only shows the padded area. Then I apply to it the same animation we defined for the images.

We are done! We have a cool circular slider:

CodePen Embed Fallback Let’s add more images

Working with four images is good, but it would be better if we can scale it to any number of images. After all, this is the purpose of an image slider. We should be able to consider N images.

For this, we are going to make the code more generic by introducing Sass. First, we define a variable for the number of images ($n) and we will update every part where we hard-coded the number of images (4).

Let’s start with the delays:

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

The formula for the delay is (1 - $i)*duration/$n, which gives us the following Sass loop:

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

We can make the duration a variable as well if we really want to. But let’s move on to the animation:

@keyframes m { 0%, 3% { transform: rotate(0); } 22%, 27% { transform: rotate(-90deg); } 47%, 52% { transform: rotate(-180deg); } 72%, 77% { transform: rotate(-270deg); } 98%, 100% {transform: rotate(-360deg); } }

Let’s simplify it to get a better view of the pattern:

@keyframes m { 0% { transform: rotate(0); } 25% { transform: rotate(-90deg); } 50% { transform: rotate(-180deg); } 75% { transform: rotate(-270deg); } 100% { transform: rotate(-360deg); } }

The step between each state is equal to 25% — which is 100%/4 — and we add a -90deg angle — which is -360deg/4. That means we can write our loop like this instead:

@keyframes m { 0% { transform: rotate(0); } @for $i from 1 to $n { #{($i / $n) * 100}% { transform: rotate(#{($i / $n) * -360}deg); } } 100% { transform: rotate(-360deg); } }

Since each image takes 5% of the animation, we change this:

#{($i / $n) * 100}%

…with this:

#{($i / $n) * 100 - 2}%, #{($i / $n) * 100 + 3}%

It should be noted that 5% is an arbitrary value I choose for this example. We can also make it a variable to control how much time each image should stay visible. I am going to skip that for the sake of simplicity, but for homework, you can try to do it and share your implementation in the comments!

@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 last bit is to update transform-origin. We will need some geometry tricks. Whatever the number of images, the configuration is always the same. We have our images (small circles) placed inside a big circle and we need to find the value of the radius, R.

You probably don’t want a boring geometry explanation so here’s how we find R:

R = S / (2 * sin(180deg / N))

If we express that as a percentage, that gives us:

R = 100% / (2 * sin(180deg / N)) = 50% / sin(180deg / N)

…which means the transform-origin value is equal to:

transform-origin: 50% (50% / math.sin(180deg / $n) + 50%);

We’re done! We have a slider that works with any number images!

CodePen Embed Fallback

Let’s toss nine images in there:

CodePen Embed Fallback

Add as many images as you want and update the $n variable with the total number of images.

Wrapping up

With a few tricks using CSS transforms and standard geometry, we created a nice circular slider that doesn’t require a lot of code. What is cool about this slider is that we don’t need to bother duplicating the images to keep the infinite animation since we have a circle. After a full rotation, we will get back to the first image!

CSS Infinite and Circular Rotating Image Slider originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Digging Deeper Into Container Style Queries

Css Tricks - Thu, 12/01/2022 - 3:59am

I wrote up some early thoughts on container style queries a little while back. It’s still early days. They’re already defined in the CSS Containment Module Level 1 specification (currently in Editor’s Draft status) but there’s still a couple of outstanding discussions taking place.

The basic idea is that we can define a container and then apply styles conditionally to its descendants based on its computed styling.

@container <name>? <conditions> { /* conditional styles */ }

The best example I’ve seen so far is removing italics from something like <em>, <i>, and <q> when they are used in a context where content is already italicized:

em, i, q { font-style: italic; /* default UA behavior */ } /* When the container's font-style is italic, remove italics from these elements. */ @container style(font-style: italic) { em, i, q { font-style: normal; } }

That’s the general idea. But if you didn’t know it, Miriam Suzanne, who is an editor of the spec, keeps an ongoing and thorough set of personal notes on container style queries that is publicly available. It was updated the other day and I spent some time in there trying to wrap my head around more nuanced aspects of style queries. It’s unofficial stuff, but I thought I’d jot down some things that stood out to me. Who knows? Maybe it’s stuff we can eventually look forward to!

Every element is a style container

We don’t even need to explictly assign a container-name or container-type to define a style container because everything is a style container by default.

So, you see that example above that removes italics? Notice it doesn’t identify a container. It jumps right to the query using the style() function. So, what container is being queried? It’s going to be the direct parent of the elements receiving the applied styles. And if not that, then it’s the next nearest relative container that takes precedence.

I like that. It’s very CSS-y for the query to search up for a match, then continue to bubble up until it finds a matching condition.

It was hard for my little brain to understand why we can get away with an implicit container based on styles but not so much when we’re dealing with dimensional queries, like size and inline-size. Miriam explains it nicely:

Dimensional queries require css containment on the size, layout, and style of the container in order to prevent layout loops. Containment is an invasive thing to apply broadly, so it was important that authors have careful control over what elements are (or are not) size containers.

Style-based queries don’t have the same limitation. There is already no way in CSS for descendant styles to have an impact on the computed styles of an ancestor. So no containment is required, and there are no invasive or unexpected side-effects in establishing an element as a style query container.

(Emphasis mine)

It all comes down to consequences — of which there are none as far as everything being a style query container right out of the box.

  • If a container is found: conditions are resolved against that container.
  • If multiple containers match: the nearest relative container takes precedence.
  • If no matches are found: unknown returned.

That’s the same “forgiving” spirit as the rest of CSS.

A container can support both dimensional and style queries

Let’s say we want define a style query without an explicit container-name:

@container style(font-style: italic) { em { font-style: normal; } }

This works because all elements are style containers, no matter the container-type. That’s what allows us to implicitly query styles and rely on the nearest match. And this is totally fine since, again, there are no adverse side effects when establishing style containers.

We have to use an explicit container-type for dimensional queries, but not so much for style queries since every element is a style query. That also means this container is both a style and dimensional query:

.card-container { container: card / inline-size; /* implictly a style query container as well */ } Excluding a container from being queried

Perhaps we don’t want a container to participate in the matching process. That’s where it might be possible to set container-type: none on an element.

.some-element { container-type: none; } Explicit style query containers offer more control of what gets queried

If, say, we were to write a style query for padding , there is no reliable way to determine the best matching container, regardless of whether we’re working with an explicitly named container or the nearest direct parent. That’s because padding is not an inherited property.

So, in those instances, we ought to use container-name to explictly inform the browser which containers they can pull from. We can even give a container multiple explicit names to make it match more conditions:

.card { container-name: card layout theme; }

Oh, and container-name accepts any number of optional and reusable names for a container! That’s even more flexibility when it comes to helping the browser make a choice when searching for matches.

.theme { container-name: theme; } .grid { container-name: layout; } .card { container-name: card layout theme; }

I sort of wonder if that might also be considered a “fallback” in the event that one container is passed over.

Style queries can be combined

The or and and operators allow us to combine wueries to keep things DRY:

@container bubble style(--arrow-position: start start) or style(--arrow-position: end start) { .bubble::after { border-block-end-color: inherit; inset-block-end: 100%; } } /* is the same as... */ @container bubble style(--arrow-position: start start) { /* etc. */ } @container bubble style(--arrow-position: end start) { /* etc. */ } Toggling styles

There’s a little overlap between container style queries and work being done to define a toggle() function. For example, we can cycle through two font-style values, say italic and normal:

em, i, q { font-style: italic; } @container style(font-style: italic) { em, i, q { font-style: normal; } }

Cool. But the proposal for CSS Toggles suggests that the toggle() function would be a simpler approach:

em, i, q { font-style: toggle(italic, normal); }

But anything beyond this sort of binary use case is where toggle() is less suitable. Style queries, though, are good to go. Miriam identifies three instances where style queries are more suitable than a toggle():

/* When font-style is italic, apply background color. */ /* Toggles can only handle one property at a time. */ @container style(font-style: italic) { em, i, q { background: lightpink; } } /* When font-style is italic and --color-mode equals light */ /* Toggles can only evaluate one condition at a time */ @container style((font-style: italic) and (--color-mode: light)) { em, i, q { background: lightpink; } } /* Apply the same query condition to multiple properties */ /* Toggles have to set each one individually as separate toggles */ @container style(font-style: italic) { em, i, q { /* clipped gradient text */ background: var(--feature-gradient); background-clip: text; box-decoration-break: clone; color: transparent; text-shadow: none; } } Style queries solve the “Custom Property Toggle Hack”

Notice that style queries are a formal solution for the “CSS custom property toggle trick”. In there, we set an empty custom property (--foo: ;) and use the comma-separated fallback method to “toggle” properties on and off when then custom property is set to a real value.

button { --is-raised: ; /* off by default */ border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1)); box-shadow: var( --is-raised, 0 1px hsl(0 0% 100% / 0.8) inset, 0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2) ); text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3)); } button:active { box-shadow: var(--is-raised, 0 1px 0.2em black inset); } #foo { --is-raised: initial; /* turned on, all fallbacks take effect. */ }

That’s super cool, also a lot of work that style container queries makes trivial.

Style queries and CSS generated content

For generated content produced by the content property of ::before and ::after pseudo-elements, the matching container is the element on which the content is generated.

.bubble { --arrow-position: end end; container: bubble; border: medium solid green; position: relative; } .bubble::after { content: ""; border: 1em solid transparent; position: absolute; } @container bubble style(--arrow-position: end end) { .bubble::after { border-block-start-color: inherit; inset-block-start: 100%; inset-inline-end: 1em; } } Style queries and web components

We can define a web component as a container and query it by style. First, we have the <template> of the component:

<template id="media-host"> <article> <div part="img"> <slot name="img">…</slot> </div> <div part="content"> <slot name="title">…</slot> <slot name="content">…</slot> </div> </article> </template>

Then we use the :host pseudo-element as a container to set a container-name, a container-type, and some high-level attributes on it:

:host { container: media-host / inline-size; --media-location: before; --media-style: square; --theme: light; }

Elements inside the <media-host> can query the parameters of the <media-host> element:

@container media-host style(--media-style: round) { [part='img'] { border-radius: 100%; } } What’s next?

Again, all the stuff I’ve jotted down here is based on Miriam’s notes, and those notes are not a substitute for the official spec. But they are an indication of what’s being discussed and where things could land in the future. I appreciate Miriam linked up a handful of outstanding discussions still taking place that we can follow to stay on top of things:

Digging Deeper Into Container Style Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Using The New Constrained Layout In WordPress Block Themes

Css Tricks - Wed, 11/30/2022 - 4:11am

One of the main goals of the WordPress Site Editor (and, yes, that is now the “official” name) is to move basic block styling from CSS to structured JSON. JSON files are machine-readable, which makes it consumable by the JavaScript-based Site Editor for configuring a theme’s global styles directly in WordPress.

It’s not all the way there yet! If we look at the Twenty Twenty-Two (TT2) default theme, there were two main unresolved issues: styling interactions (like :hover, :active, :focus), and the margins and padding of layout containers. You can see how those were temporarily fixed in the TT2 style.css file rather than making it into the theme.json file.

WordPress 6.1 fixed those issues and what I want to do is look specifically at the latter. Now that we have JSON-ified styles for the margins and padding of layout containers, that opens us up to more flexible and robust ways to define spacing in our theme layouts.

What kind of spacing are we talking about?

First off, we already have root-level padding which is a fancy way of describing padding on the <body> element. That’s nice because it ensures consistent spacing on an element that is shared on all pages and posts.

But there’s more to it because now we have a way for blocks to bypass that padding and align themselves full-width. That’s thanks to padding-aware alignments which is a new opt-in feature in theme.json. So, even if you have root-level padding, you can still allow, say, an image (or some other block) to break out and go full-width.

That gets us to another thing we get: constrained layouts. The idea here is that any blocks nested in the layout respect the layout’s content width — which is a global setting — and do not flow outside of it. We can override that behavior on a block-by-block basis with alignments, but we’ll get to that.

Let’s start with…

Root-level padding

Again, this isn’t new. We’ve had the ability to set padding on the <body> element in theme.json since the experimental Gutenberg plugin introduced it in version 11.7. We set it on the styles.spacing object, where we have margin and padding objects to define the top, right, bottom, and left spacing on the body:

{ "version": 2, "styles": { "spacing": { "margin": { "top": "60px", "right": "30px", "bottom": "60px", "left": "30px" }, "padding": { "top": "30px", "right": "30px", "bottom": "30px", "left": "30px" } } } }

This is a global setting. So, if we were to crack open DevTools and inspect the <body> element, we would see these CSS styles:

body { margin-top: 60px; margin-right: 30px; margin-bottom: 60px; margin-left: 30px; padding-top: 30px; padding-right: 30px; padding-bottom: 30px; padding-left: 30px; }

Cool. But herein lies the issue of how in the world we can allow some blocks to break out of that spacing to fill the full screen, edge-to-edge. That’s why the spacing is there, right? It helps prevent that from happening!

But there are indeed plenty of cases where you might want to break out of that spacing on a one-off instance when working in the Block Editor. Say we plop an Image block on a page and we want it to go full-width while the rest of the content respects the root-level padding?

Enter…

Padding-aware alignments

While attempting to create the first default WordPress theme that defines all styles in the theme.json file, lead designer Kjell Reigstad illustrates the challenging aspects of breaking out of root-level padding in this GitHub issue.

Root-level padding prevents blocks from taking up the full viewport width (left). But with padding-aware alignments, some blocks can “opt-out” of that spacing and take up the full viewport width (right). (Image credit: Kjell Reigstad)

New features in WordPress 6.1 were created to address this issue. Let’s dig into those next.

useRootPaddingAwareAlignments

A new useRootPaddingAwareAlignments property was created to address the problem. It was actually first introduced in the Gutenberg plugin v13.8. The original pull request is a nice primer on how it works.

{ "version": 2, "settings": { "appearanceTools": true, "useRootPaddingAwareAlignments": true, // etc. },

Right off the bat, notice that this is a feature we have to opt into. The property is set to false by default and we have to explicitly set it to true in order to enable it. Also notice that we have appearanceTools set to true as well. That opts us into UI controls in the Site Editor for styling borders, link colors, typography, and, yes, spacing which includes margin and padding.

Setting appearanceTools set to true automatically opts blocks into margin and padding without having to set either settings.spacing.padding or setting.spacing.margin to true.

When we do enable useRootPaddingAwareAlignments, we are provided with custom properties with root padding values that are set on the <body> element on the front end. Interestingly, it also applies the padding to the .editor-styles-wrapper class so the spacing is displayed when working in the back-end Block Editor. Pretty cool!

I was able to confirm those CSS custom properties in DevTools while digging around.

Enabling useRootPaddingAwareAlignments also applies left and right padding to any block that supports the “content” width and “wide” width values in the Global Styles image above. We can also define those values in theme.json:

{ "version": 2, "settings": { "layout": { "contentSize": "640px", "wideSize": "1000px" } } }

If the Global Styles settings are different than what is defined in theme.json, then the Global Styles take precedence. You can learn all about managing block theme styles in my last article.

  • contentSize is the default width for blocks.
  • wideSize provides a “wide” layout option and establishes a wider column for blocks to stretch out.

So, that last code example will give us the following CSS:

/* The default content container */ .wp-container-[id] > * { max-width: 640px; margin-left: auto !important; margin-right: auto !important; } /* The wider content container */ .wp-container-[id] > .alignwide { max-width: 1000px; }

[id] indicates a unique number automatically generated by WordPress.

But guess what else we get? Full alignment as well!

.wp-container-[id] .alignfull { max-width: none; }

See that? By enabling useRootPaddingAwareAlignments and defining contentSize and wideSize, we also get a full alignment CSS class for a total of three container configurations for controlling the width of blocks that are added to pages and posts.

This applies to the following layout-specific blocks: Columns, Group, Post Content, and Query Loop.

Block layout controls

Let’s say we add any of those aforementioned layout-specific blocks to a page. When we select the block, the block settings UI offers us new layout settings based on the settings.layout values we defined in theme.json (or the Global Styles UI).

We’re dealing with very specific blocks here — ones that can have other blocks nested inside. So, these Layout settings are really about controlling the width and alignment of those nested blocks. The “Inner blocks use content width” setting is enabled by default. If we toggle it off, then we have no max-width on the container and the blocks inside it go edge-to-edge.

If we leave the toggle on, then nested blocks will adhere to either the contentWidth or wideWidth values (more on that in a bit). Or we can use the numeric inputs to define custom contentWidth and wideWidth values in this one-off instance. That’s great flexibility!

Wide blocks

The settings we just looked are set on the parent block. Once we’ve nested a block inside and select it, we have additional options in that block to use the contentWidth, wideWidth, or go full-width.

This Image block is set to respect the contentWidth setting, labeled “None” in the contextual menu, but can also be set to wideWidth (labeled “Wide width”) or “Full width”.

Notice how WordPress multiplies the root-level padding CSS custom properties by -1 to create negative margins when selecting the “Full width” option.

The .alignfull class sets negative margins on a nested block to ensure it takes up the full viewport width without conflicting with the root-level padding settings. Using a constrained layout

We just covered the new spacing and alignments we get with WordPress 6.1. Those are specific to blocks and any nested blocks within blocks. But WordPress 6.1 also introduces new layout features for even more flexibility and consistency in a theme’s templates.

Case in point: WordPress has completely restructured its Flex and Flow layout types and gave us a constrained layout type that makes it easier to align block layouts in themes using the content width settings in the Site Editor’s Global Styles UI.

Flex, Flow, and Constrained layouts

The difference between these three layout types is the styles that they output. Isabel Brison has an excellent write-up that nicely outlines the differences, but let’s paraphrase them here for reference:

  • Flow layout: Adds vertical spacing between nested blocks in the margin-block direction. Those nested blocks can also be aligned to the left, right, or center.
  • Constrained layout: Same exact deal as a Flow layout, but with width constraints on nested blocks that are based on the contentWidth and wideWidth settings (either in theme.json or Global Styles).
  • Flex layout: This was unchanged in WordPress 6.1. It uses CSS Flexbox to create a layout that flows horizontally (in a row) by default, but can flow vertically as well so blocks stack one on top of another. Spacing is applied using the CSS gap property.

This new slate of layout types creates semantic class names for each layout:

Semantic layout classLayout typeSupported blocks.is-layout-flowFlow layoutColumns, Group, Post Content, and Query Loop..is-layout-constrainedConstrained layoutColumns, Group, Post Content, and Query Loop..is-layout-flexFlex layoutColumns, Buttons, Social Icons

Justin Tadlock has an extensive write-up on the different layout types and semantic classes, including use cases and examples.

Updating your theme to support constrained layouts

If you’re already using a block theme of your own making, you’re going to want to update it to support constrained layouts. All it takes is swapping out a couple of things in theme.json:

{ "version": 2, "settings": { "layout": { "type": "constrained", // replaces `"inherit": true` "type": "default", // replaces `"inherit": false` } } }

These are recently released block themes that have enabled spacing settings with useRootPaddingAwareAlignments and have an updated theme.json file that defines a constrained layout:

ThemeRoot-level paddingConstrained layout featuresTT3Source codeSource codeTemplatesProWPSource codeSource codeTemplatesTriangulateSource codeSource codeTemplatesOaknutSource codeSource codeTemplatesLoudnessSource codeSource codeTemplatesPixlSource codeSource codeTemplatesBlock CanvasSource codeSource code, TemplatesRainfallSource codeSource codeTemplates Disabling layout styles

The base layout styles are default features that ship in WordPress 6.1 Core. In other words, they’re enabled right out of the box. But we can disable them if we need to with this little snippet in functions.php:

// Remove layout styles. add_theme_support( 'disable-layout-styles' );

Big warning here: disabling support for the default layout types also removes all of the base styling for those layouts. That means you’ll need to roll your own styles for spacing, alignments, and anything else needed to display content in different template and block contexts.

Wrapping up

As a great fan of full-width images, the new contained WordPress 6.1 layout and padding aware alignment features are two of my most favorites yet. Taken together with other tools including, better margin and padding control, fluid typography, and updated List and Quote blocks, among others, is solid proof that WordPress is moving towards a better content creation experience.

Now, we have to wait and look at how the imagination and creativity of ordinary designers and content creators use these incredible tools and take it to a new level.

Because of the site editor development iterations in progress, we should always anticipate a difficult path ahead. However, as an optimist, I am eager to see what will happen in the upcoming version of WordPress 6.2. Some of the thing, that I am keeping a close eye on are things like features being considered for inclusion, support for sticky positioning, new layout class names for inner block wrappers, updated footer alignment options, and adding constrained and flow layout options to Cover blocks.

This GitHub issues #44720 lists the layout related discussions slated for WordPress 6.2.

Additional resources

I consulted and referenced a lot of sources while digging into all of this. Here’s a big ol’ list of things I found helpful and think you might enjoy as well.

Tutorials WordPress posts GitHub pull requests and issues

Using The New Constrained Layout In WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

More Than “Slapping Paint on a Website”

Css Tricks - Tue, 11/29/2022 - 5:03am

I’m a sucker for anything about front-end job titles.

Anselm Hannemann:

CSS evolved and we’re beyond the point where everyone can just do it as a side interest. We all can learn it and build amazing stuff with it, but using it wisely and correctly in a large-scale context isn’t an easy job anymore. It deserves people whose work is to focus on that part of the code.

Anselm is partly in responding to Sacha Greif’s “Is There Too Much CSS Now?” and the overall sentiment that CSS has a much higher barrier to entry for those learning it today than it did, say, in the CSS3 days. Back then, there was a super direct path to see the magic of CSS. Rachel Andrew perfectly captures that magic feeling in a prescient post from 2019:

There is something remarkable about the fact that, with everything we have created in the past 20 years or so, I can still take a complete beginner and teach them to build a simple webpage with HTML and CSS, in a day. […] We just need a text editor and a few hours. This is how we make things show up on a webpage.

That’s the real entry point here […]

“HTML, CSS and our vanishing industry entry points”

Rachel is speaking to the abstraction of frameworks on top of vanilla CSS (and HTML) but you might as well tack big, shiny, and fairly new features on there, like CSS grid, flexbox, container queries, cascade layers, custom properties, and relational pseudo-classes, to name a few. Not that those are abstractions, of course. There’s just a lot to learn right now, whether you’ve been writing CSS for 20 days or 20 years.

But back to Anselm’s post. Do we need to think about CSS as more than just, you know, styling things? I often joke that my job is slapping paint on websites to make them pretty. But, honestly, I know it’s a lot more than that. We all know it’s more than that.

Maybe CSS is an industry in itself. Think of all the possible considerations that have to pass through your brain when writing CSS rules. Heck, Ahmad Shadeed recently shared all the things his brain processes just to style a Hero component. CSS touches so much of the overall user experience — responsiveness, accessibility, performance, cross-browser, etc. — that it clearly goes well beyond “slapping paint on websites”. So far beyond that each of those things could be someone’s full-time gig, depending on the project.

So, yes, CSS has reached a point where I could imagine seeing “CSS Engineer” on some job board. As Anselm said, “[CSS] deserves people whose work is to focus on that part of the code.” Seen that way, it’s not so hard to imagine front-end development as a whole evolving into areas of specialization, just like many other industries.

More Than “Slapping Paint on a Website” originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

10 Must-have Typefaces for 2023

Typography - Mon, 11/28/2022 - 6:49pm

Read the book, Typographic Firsts

Finding the best typefaces among thousands can be time-consuming and frustrating! To save your time and sanity, we’ve curated a top-notch list of must-have typefaces for 2023. No super-quirky, one-use-only display fonts, but workhorse font families that will serve you and your clients well for years to come.

The post 10 Must-have Typefaces for 2023 appeared first on I Love Typography.

Newer Things to Know About Good Ol’ HTML Lists

Css Tricks - Mon, 11/28/2022 - 4:05am

HTML lists are boring. They don’t do much, so we don’t really think about them despite how widely used they are. And we’re still able to do the same things we’ve always done to customize them, like removing markers, reversing order, and making custom counters.

There are, however, a few “newer” things — including dangers — to know when using lists. The dangers are mostly minor, but way more common than you might think. We’ll get to those, plus some new stuff we can do with lists, and even new ways to approach old solutions.

To clarify, these are the HTML elements we’re talking about:

  • Ordered lists <ol>
  • Unordered lists <ul>
  • Description lists <dl>
  • Interactive lists <menu>

Ordered lists, unordered lists, and interactive lists contain list items (<li>) which are displayed according to what kind of list we’re dealing with. An ordered list (<ol>) displays numbers next to list items. Unordered lists (<ul>) and menu elements (<menu>) displays bullet points next to list items. We call these “list markers” and they can even be styled using the ::marker pseudo-element. Description lists use description terms (<dt>) and description details (<dd>) instead of <li> and don’t have list markers. They‘re supposed to be used to display metadata and glossaries, but I can’t say I’ve ever seen them in the wild.

CodePen Embed Fallback

Let’s start off with the easy stuff — how to correctly (at least in my opinion) reset list styles. After that, we’ll take a look at a couple of accessibility issues before shining a light on the elusive <menu> element, which you may be surprised to learn… is actually a type of list, too!

Resetting list styles

Browsers automatically apply their own User Agent styles to help with the visual structure of lists right out of the box. That can be great! But if we want to start with a blank slate free of styling opinions, then we have to reset those styles first.

For example, we can remove the markers next to list items pretty easily. Nothing new here:

/* Zap all list markers! */ ol, ul, menu { list-style: none; }

But modern CSS has new ways to help us target specific list instances. Let’s say we want to clear markers from all lists, except if those lists appear in long-form content, like an article. If we combine the powers of newer CSS pseudo-class functions :where() and :not(), we can isolate those instances and allow the markers in those cases:

/* Where there are lists that are not articles where there are lists... */ :where(ol, ul, menu):not(article :where(ol, ul, menu)) { list-style: none; }

Why use :where() instead of :is()? The specificity of :where() is always zero, whereas :is() takes the specificity of the most specific element in its list of selectors. So, using :where() is a less forceful way of overriding things and can be easily overridden itself.

UA styles also apply padding to space a list item’s content from its marker. Again, that’s a pretty nice affordance right out of the box in some cases, but if we’re already removing the list markers like we did above, then we may as well wipe out that padding too. This is another case for :where():

:where(ol, ul, menu) { padding-left: 0; /* or padding-inline-start */ }

OK, that’s going to prevent marker-less list items from appearing to float in space. But we sort of tossed out the baby with the bathwater and removed the padding in all instances, including the ones we previously isolated in an <article>. So, now those lists with markers sorta hang off the edge of the content box.

CodePen Embed Fallback

Notice that UA styles apply an extra 40px to the <menu> element.

So what we want to do is prevent the list markers from “hanging” outside the container. We can fix that with the list-style-position property:

CodePen Embed Fallback

Or not… maybe it comes down to stylistic preference?

Newer accessibility concerns with lists

Unfortunately, there are a couple of accessibility concerns when it comes to lists — even in these more modern times. One concern is a result of applying list-style: none; as we did when resetting UA styles.

In a nutshell, Safari does not read ordered and unordered lists styled with list-style: none as actual lists, like when navigating content with a screen reader. In other words, removing the markers also removes the list’s semantic meaning. The fix for this fix it to apply an ARIA list role on the list and a listitem role to the list items so screen readers will pick them up:

<ol style="list-style: none;" role="list"> <li role="listItem">...</li> <li role="listItem">...</li> <li role="listItem">...</li> </ol> <ul style="list-style: none;" role="list"> <li role="listItem">...</li> <li role="listItem">...</li> <li role="listItem">...</li> </ul>

Oddly, Safari considers this to be a feature rather than a bug. Basically, users would report that screen readers were announcing too many lists (because developers tend to overuse them), so now, only those with role="list" are announced by screen readers, which actually isn’t that odd after all. Scott O’Hara has a detailed rundown of how it all went down.

A second accessibility concern isn’t one of our own making (hooray!). So, you know how you’re supposed to add an aria-label to <section> elements without headings? Well, it sometimes makes sense to do the same with a list that doesn’t contain a heading element that helps describe the list.

<!-- This list is somewhat described by the heading --> <section> <h2>Grocery list</h2> <ol role="list"> <!-- ... --> </ol> </section> <!-- This list is described by the aria-label --> <ol role="list" aria-label="Grocery list"> <!-- ... --> </ol>

You absolutely don’t have to use either method. Using a heading or an ARIA label is just added context, not a requirement — be sure to test your websites with screen readers and do what offers the best user experience for the situation.

In somewhat related news, Eric Bailey wrote up an excellent piece on why and how he considers aria-label to be a code smell.

Wait, <menu> is a list, too?

OK, so, you’re likely wondering about all of the <menu> elements that I’ve been slipping into the code examples. It’s actually super simple; menus are unordered lists except that they’re meant for interactive items. They’re even exposed to the accessibility tree as unordered lists.

In the early days of the semantic web, I mistakenly believed that menus were like <nav>s before believing that they were for context menus (or “toolbars” as the spec says) because that’s what early versions of the HTML spec said. (MDN has an interesting write-up on all of the deprecated stuff related to <menu> if you’re at all interested.)

Today, however, this is the semantic way to use menus:

<menu aria-label="Share article"> <li><button>Email</button></li> <li><button>Twitter</button></li> <li><button>Facebook</button></li> </menu>

Personally, I think there are some good use-cases for <menu>. That last example shows a list of social sharing buttons wrapped up in a labeled <menu> element, the notable aspect being that the “Share article” label contributes a significant amount of context that helps describe what the buttons do.

Are menus absolutely necessary? No. Are they HTML landmarks? Definitely not. But they’re there if you enjoy fewer <div>s and you feel like the component could use an aria-label for additional context.

Anything else?

Yes, there’s also the aforementioned <dl> (description list) element, however, MDN doesn’t seem to consider them lists in the same way — it’s a list of groups containing terms — and I can’t say that I’ve really seen them in use. According to MDN, they’re supposed to be used for metadata, glossaries, and other types of key-value pairs. I would just avoid them on the grounds that all screen readers announce them differently.

But let’s not end things on a negative note. Here’s a list of super cool things you can do with lists:

Newer Things to Know About Good Ol’ HTML Lists originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Apple Messages & Color Contrast

Css Tricks - Wed, 11/23/2022 - 4:25am

Well, color me this! I was griping to myself last night about just how gosh dang hard it is to read text messages in Apple Messages. You know, not the blue bubbles that you get when messaging other iPhone users. Those are iMessages.

What I’m talking about are the green bubbles you get when messaging non-iPhone users. Those are standard text messages.

iMessage (left) and text message (right)

Let’s run the green through a contrast checker to see what’s up.

Permalink

Oomph. Now I know why I always reach for my reading glasses when a text message pops up. That 2.17:1 ratio is below the WCAG 2.0 AA requirement of 4.5:1 and wayyyyy below the AAA level of 7:1.

Turns out I’m not the only one griping. A quick search turned up a little trove of news and blog posts — some as recent as last week — about the readability of those green text message bubbles.

I’m no conspiracy theorist and like to give benefit to doubt. Buuuuut…

Apple Messages in iOS 6 (left) and iOS 7 (right)
Credit: Phoceis
  • iOS 6: Dark text on a green gradient background
  • iOS 7: White text on a #5AB539 background (or something close to it)
  • iOS 16.1: White text on a #6ACC46 background

That second one is based on old screenshots and might not be the most accurate color value. But still, the transition from iOS 6 with dark text to what we have today in iOS 16.1 shows a clear regression. I’d like think the design team checked the updated values against WCAG guidelines, sure, but at least against their own Human Interface Guidelines.

The current green background (#65C466) appears to be different than what is listed as the green “system color” (#30D158, converted from a RGB of 48, 209, 88) in the iOS palette listed in the guidelines. But it’s not like that gets us any closer to a passing WCAG AA or AAA rating.

Permalink

&#x1f937;‍♂️

Apple Messages & Color Contrast originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

WordPress Developer Blog

Css Tricks - Tue, 11/22/2022 - 8:36am

Well, hey check this out. Looks like there is a brand spankin’ new blog over at WordPress.org all about WordPress development. In the original proposal for the blog, Birgit Pauli-Haak writes:

The Make Core blog has a heavy emphasis on meeting notes for the various core teams, rather than highlighting new features. This makes it difficult for developers who are not contributors or who just occasionally contribute to find the relevant information among the team-related posts.

Josepha describes the blog further in the announcement post:

These are types of content that lend themselves more toward the long-form content of a blog.  However, there are more practical reasons for this new home for developers on WordPress.org:

  • Posts that detail updated or new APIs.
  • A way to subscribe to development-related updates.
  • A place to keep up with ongoing discussions.

Perhaps the most important reason for the Developer Blog is to have a central place for WordPress extenders.  Information can fragment across various sites, and developers spend valuable time seeking it out.  This blog is an attempt to provide a curated experience of the most important updates. 

Hear, hear! This is exactly the sort of thing I feel has been missing in the WordPress development space: quality information from established developers that shares useful tips, tricks, and best practices for working with WordPress in this new era of full-site editing. With WordPress Core development taking place at break-neck speeds, having a central source of updated information and a way to syndicate it is a welcome enhancement for sure.

There are already a few excellent articles in there to kick-start things:

It’s WordPress, of course, so anyone and everyone is encouraged to contribute. If you do, it’s a good idea to first check our the writing tips and guidelines. And, naturally, there is an RSS feed you can use to keep up with the lastest posts.

If you wanna go down the ol’ rabbit trail for how the blog came together, here are a few links to get that context:

(High fives to Ganesh Dahal for the tip!)

To Shared LinkPermalink on CSS-Tricks

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

Syndicate content
©2003 - Present Akamai Design & Development.