Web Standards

Single Element Loaders: The Bars

Css Tricks - Fri, 06/24/2022 - 10:00am

We’ve looked at spinners. We’ve looked at dots. Now we’re going to tackle another common pattern for loaders: bars. And we’re going to do the same thing in this third article of the series as we have the others by making it with only one element and with flexible CSS that makes it easy to create variations.

Article series

Let’s start with not one, not two, but 20 examples of bar loaders.

CodePen Embed Fallback CodePen Embed Fallback

What?! Are you going to detail each one of them? That’s too much for an article!

It might seem like that at first glance! But all of them rely on the same code structure and we only update a few values to create variations. That’s all the power of CSS. We don’t learn how to create one loader, but we learn different techniques that allow us to create as much loader as we want using merely the same code structure.

Let’s make some bars!

We start by defining the dimensions for them using width (or height) with aspect-ratio to maintain proportion:

.bars { width: 45px; aspect-ratio: 1; }

We sort of “fake” three bars with a linear gradient on the background — very similar to how we created dot loaders in Part 2 of this series.

.bars { width: 45px; aspect-ratio: 1; --c: no-repeat linear-gradient(#000 0 0); /* we define the color here */ background: var(--c) 0% 50%, var(--c) 50% 50%, var(--c) 100% 50%; background-size: 20% 100%; /* 20% * (3 bars + 2 spaces) = 100% */ }

The above code will give us the following result:

Like the other articles in this series, we are going to deal with a lot of background trickery. So, if you ever feel like we’re jumping around too fast or feel you need a little more detail, please do check those out. You can also read my Stack Overflow answer where I give a detailed explanation on how all this works.

Animating the bars

We either animate the element’s size or position to create the bar loader. Let’s animate the size by defining the following animation keyframes:

@keyframes load { 0% { background-size: 20% 100%, 20% 100%, 20% 100%; } /* 1 */ 33% { background-size: 20% 10% , 20% 100%, 20% 100%; } /* 2 */ 50% { background-size: 20% 100%, 20% 10% , 20% 100%; } /* 3 */ 66% { background-size: 20% 100%, 20% 100%, 20% 10%; } /* 4 */ 100% { background-size: 20% 100%, 20% 100%, 20% 100%; } /* 5 */ }

See what’s happening there? Between 0% and 100%, the animation changes the background-size of the element’s background gradient. Each keyframe sets three background sizes (one for each gradient).

And here’s what we get:

CodePen Embed Fallback

Can you start to imagine all the possible variations we can get by playing with different animation configurations for the sizes or the positions?

Let’s fix the size to 20% 50% and update the positions this time:

.loader { width: 45px; aspect-ratio: .75; --c: no-repeat linear-gradient(#000 0 0); background: var(--c), var(--c), var(--c); background-size: 20% 50%; animation: load 1s infinite linear; } @keyframes load { 0% { background-position: 0% 100%, 50% 100%, 100% 100%; } /* 1 */ 20% { background-position: 0% 50% , 50% 100%, 100% 100%; } /* 2 */ 40% { background-position: 0% 0% , 50% 50% , 100% 100%; } /* 3 */ 60% { background-position: 0% 100%, 50% 0% , 100% 50%; } /* 4 */ 80% { background-position: 0% 100%, 50% 100%, 100% 0%; } /* 5 */ 100% { background-position: 0% 100%, 50% 100%, 100% 100%; } /* 6 */ }

…which gets us another loader!

CodePen Embed Fallback

You’ve probably got the trick by now. All you need is to define a timeline that you translate into a keyframe. By animating the size, the position — or both! — there’s an infinite number of loader possibilities at our fingertips.

And once we get comfortable with such a technique we can go further and use a more complex gradient to create even more loaders.

CodePen Embed Fallback

Expect for the last two examples in that demo, all of the bar loaders use the same underlying markup and styles and different combinations of animations. Open the code and try to visualize each frame independently; you’ll see how relatively trivial it is to make dozens — if not hundreds — of variations.

Getting fancy

Did you remember the mask trick we did with the dot loaders in the second article of this series? We can do the same here!

If we apply all the above logic inside the mask property we can use any background configuration to add a fancy coloration to our loaders.

Let’s take one demo and update it:

CodePen Embed Fallback

All I did is updating all the background-* with mask-* and I added a gradient coloration. As simple as that and yet we get another cool loader.

So there is no difference between the dots and the bars?

No difference! I wrote two different articles to cover as many examples as possible but in both, I am relying on the same techniques:

  1. Gradients to create the shapes (dots or bars or maybe something else)
  2. Animating background-size and/or background-position to create the loader animation
  3. Adding mask to add a touch of colors
Rounding the bars

Let’s try something different this time where we can round the edges of our bars.

CodePen Embed Fallback

Using one element and its ::before and ::after pseudos, we define three identical bars:

.loader { --s: 100px; /* control the size */ display: grid; place-items: center; place-content: center; margin: 0 calc(var(--s) / 2); /* 50px */ } .loader::before, .loader::after { content: ""; grid-area: 1/1; } .loader, .loader::before, .loader::after { height: var(--s); width: calc(var(--s) / 5); /* 20px */ border-radius: var(--s); transform: translate(calc(var(--_i, 0) * 200%)); } .loader::before { --_i: -1; } .loader::after { --_i: 1; }

That gives us three bars, this time without relying on a linear gradient:

Now the trick is to fill in those bars with a lovely gradient. To simulate a continuous gradient, we need to play with background properties. In the above figure, the green area defines the area covered by the loader. That area should be the size of the gradient and, if we do the math, it’s equal to multiplying both sides labeled S in the diagram, or background-size: var(--s) var(--s).

Since our elements are individually placed, we need to update the position of the gradient inside each one to make sure all of them overlap. This way, we’re simulating one continuous gradient even though it’s really three of them.

For the main element (placed at the center), the background needs to be at the center. We use the following:

.loader { /* etc. */ background: linear-gradient() 50% / var(--s) var(--s); }

For the pseudo-element on the left, we need the background on the left

.loader::before { /* etc. */ background: linear-gradient() 0% / var(--s) var(--s); }

And for the pseudo on the right, the background needs to be positioned to the right:

.loader::after { background: linear-gradient() 100% / var(--s) var(--s); }

Using the same CSS variable, --_i, that we used for the translate, we can write the code like this:

.loader { --s: 100px; /* control the size */ --c: linear-gradient(/* etc. */); /* control the coloration */ display: grid; place-items: center; place-content: center; } .loader::before, .loader::after{ content: ""; grid-area: 1/1; } .loader, .loader::before, .loader::after{ height: var(--s); width: calc(var(--s) / 5); border-radius: var(--s); background: var(--c) calc(50% + var(--_i, 0) * 50%) / var(--s) var(--s); transform: translate(calc(var(--_i, 0) * 200%)); } .loader::before { --_i: -1; } .loader::after { --_i: 1; }

Now, all we have to do is to animate the height and add some delays! Here are three examples where all that’s different are the colors and sizes:

CodePen Embed Fallback Wrapping up

I hope so far you are feeling super encouraged by all the powers you have to make complex-looking loading animations. All we need is one element, either gradients or pseudos to draw the bars, then some keyframes to move things around. That’s the entire recipe for getting an endless number of possibilities, so go out and starting cooking up some neat stuff!

Until the next article, I will leave you with a funny collection of loaders where I am combining the dots and the bars!

CodePen Embed Fallback CodePen Embed Fallback Article series

Single Element Loaders: The Bars originally published on CSS-Tricks. You should get the newsletter.

Different Ways to Write CSS in React

Css Tricks - Wed, 06/22/2022 - 4:24am

We’re all familiar with the standard way of linking up a stylesheet to the <head> of an HTML doc, right? That’s just one of several ways we’re able to write CSS. But what does it look like to style things in a single-page application (SPA), say in a React project?

Turns out there are several ways to go about styling a React application. Some overlap with traditional styling, others not so much. But let’s count all the ways we can do it.

Importing external stylesheets

As the name suggests, React can import CSS files. The process is similar to how we link up CSS file in the HTML <head>:

  1. Create a new CSS file in your project directory.
  2. Write CSS.
  3. Import it into the React file.

Like this:

import "./style.css";

That usually goes at the top of the file where other imports happen:

import { React } from "react"; import "./Components/css/App.css"; function App() { return ( <div className="main"> </div> ); } export default App;

In this example, a CSS file is imported into an App.js from the /Components/css folder.

Write inline styles

You may be used to hearing that inline styling isn’t all that great for maintainability and whatnot, but there are definitely situations (here’s one!) where it makes sense. And maintainability is less of an issue in React, as the CSS often already sits inside the same file anyway.

This is a super simple example of inline styling in React:

<div className="main" style={{color:"red"}}>

A better approach, though, is to use objects:

  1. First, create an object that contains styles for different elements.
  2. Then add it to an element using the style attribute and then select the property to style.

Let’s see that in context:

import { React } from "react"; function App() { const styles = { main: { backgroundColor: "#f1f1f1", width: "100%", }, inputText: { padding: "10px", color: "red", }, }; return ( <div className="main" style={styles.main}> <input type="text" style={styles.inputText}></input> </div> ); } export default App;

This example contains a styles object containing two more objects, one for the .main class and the other for a text input, which contain style rules similar to what we’d expect to see in an external stylesheet. Those objects are then applied to the style attribute of elements that are in the returned markup.

Note that curly brackets are used when referencing styles rather than the quotation marks we’d normally use in plain HTML.

Use CSS Modules

CSS Modules… what the heck happened to those, right? They have the benefit of locally scoped variables and can be used right alongside React. But what are they, again, exactly?

Quoting the repo’s documentation:

CSS Modules works by compiling individual CSS files into both CSS and data. The CSS output is normal, global CSS, which can be injected directly into the browser or concatenated together and written to a file for production use. The data is used to map the human-readable names you’ve used in the files to the globally-safe output CSS.

In simpler terms, CSS Modules allows us to use the same class name in multiple files without clashes since each class name is given a unique programmatic name. This is especially useful in larger applications. Every class name is scoped locally to the specific component in which it is being imported.

A CSS Module stylesheet is similar to a regular stylesheet, only with a different extension (e.g. styles.module.css). Here’s how they’re set up:

  1. Create a file with .module.css as the extension.
  2. Import that module into the React app (like we saw earlier)
  3. Add a className to an element or component and reference the particular style from the imported styles.

Super simple example:

/* styles.module.css */ .font { color: #f00; font-size: 20px; } import { React } from "react"; import styles from "./styles.module.css"; function App() { return ( <h1 className={styles.heading}>Hello World</h1> ); } export default App; Use styled-components

Have you used styled-components? It’s quite popular and allows you to build custom components using actual CSS in your JavaScript. A styled-component is basically a React component with — get ready for it — styles. Some of the features include unique class names, dynamic styling and better management of the CSS as each component has its own separate styles.

Install the styled-components npm package in the command line:

npm install styled-components

Next up, import it into the React app:

import styled from 'styled-components'

Create a component and assign a styled property to it. Note the use of template literals denoted by backticks in the Wrapper object:

import { React } from "react"; import styled from "styled-components"; function App() { const Wrapper = styled.div` width: 100%; height: 100px; background-color: red; display: block; `; return <Wrapper />; } export default App;

The above Wrapper component will be rendered as a div that contains those styles.

Conditional styling

One of the advantages of styled-components is that the components themselves are functional, as in you can use props within the CSS. This opens the door up to conditional statements and changing styles based on a state or prop.

Here’s a demo showing that off:

Here, we are manipulating the div’s display property on the display state. This state is controlled by a button that toggles the div’s state when clicked. This, in turn, toggles between the styles of two different states.

In inline if statements, we use a ? instead of the usual if/else syntax. The else part is after the semicolon. And remember to always call or use the state after it has been initialized. In that last demo, for example, the state should be above the Wrapper component’s styles.

Happy React styling!

That’s a wrap, folks! We looked at a handful of different ways to write styles in a React application. And it’s not like one is any better than the rest; the approach you use depends on the situation, of course. Hopefully now you’ve got a good understanding of them and know that you have a bunch of tools in your React styling arsenal.

Different Ways to Write CSS in React originally published on CSS-Tricks. You should get the newsletter.

Single Element Loaders: The Dots

Css Tricks - Fri, 06/17/2022 - 4:47am

We’re looking at loaders in this series. More than that, we’re breaking down some common loader patterns and how to re-create them with nothing more than a single div. So far, we’ve picked apart the classic spinning loader. Now, let’s look at another one you’re likely well aware of: the dots.

Dot loaders are all over the place. They’re neat because they usually consist of three dots that sort of look like a text ellipsis (…) that dances around.

Article series

Our goal here is to make this same thing out of a single div element. In other words, there is no one div per dot or individual animations for each dot.

CodePen Embed Fallback

That example of a loader up above is made with a single div element, a few CSS declarations, and no pseudo-elements. I am combining two techniques using CSS background and mask. And when we’re done, we’ll see how animating a background gradient helps create the illusion of each dot changing colors as they move up and down in succession.

The background animation

Let’s start with the background animation:

.loader { width: 180px; /* this controls the size */ aspect-ratio: 8/5; /* maintain the scale */ background: conic-gradient(red 50%, blue 0) no-repeat, /* top colors */ conic-gradient(green 50%, purple 0) no-repeat; /* bottom colors */ background-size: 200% 50%; animation: back 4s infinite linear; /* applies the animation */ } /* define the animation */ @keyframes back { 0%, /* X Y , X Y */ 100% { background-position: 0% 0%, 0% 100%; } 25% { background-position: 100% 0%, 0% 100%; } 50% { background-position: 100% 0%, 100% 100%; } 75% { background-position: 0% 0%, 100% 100%; } }

I hope this looks pretty straightforward. What we’ve got is a 180px-wide .loader element that shows two conic gradients sporting hard color stops between two colors each — the first gradient is red and blue along the top half of the .loader, and the second gradient is green and purple along the bottom half.

The way the loader’s background is sized (200% wide), we only see one of those colors in each half at a time. Then we have this little animation that pushes the position of those background gradients left, right, and back again forever and ever.

When dealing with background properties — especially background-position — I always refer to my Stack Overflow answer where I am giving a detailed explanation on how all this works. If you are uncomfortable with CSS background trickery, I highly recommend reading that answer to help with what comes next.

In the animation, notice that the first layer is Y=0% (placed at the top) while X is changes from 0% to 100%. For the second layer, we have the same for X but Y=100% (placed at the bottom).

CodePen Embed Fallback

Why using a conic-gradient() instead of linear-gradient()?

Good question! Intuitively, we should use a linear gradient to create a two-color gradients like this:

linear-gradient(90deg, red 50%, blue 0)

But we can also reach for the same using a conic-gradient() — and with less of code. We reduce the code and also learn a new trick in the process!

Sliding the colors left and right is a nice way to make it look like we’re changing colors, but it might be better if we instantly change colors instead — that way, there’s no chance of a loader dot flashing two colors at the same time. To do this, let’s change the animation‘s timing function from linear to steps(1)

CodePen Embed Fallback The loader dots

If you followed along with the first article in this series, I bet you know what comes next: CSS masks! What makes masks so great is that they let us sort of “cut out” parts of a background in the shape of another element. So, in this case, we want to make a few dots, show the background gradients through the dots, and cut out any parts of the background that are not part of a dot.

We are going to use radial-gradient() for this:

.loader { width: 180px; aspect-ratio: 8/5; mask: radial-gradient(#000 68%, #0000 71%) no-repeat, radial-gradient(#000 68%, #0000 71%) no-repeat, radial-gradient(#000 68%, #0000 71%) no-repeat; mask-size: 25% 40%; /* the size of our dots */ }

There’s some duplicated code in there, so let’s make a CSS variable to slim things down:

.loader { width: 180px; aspect-ratio: 8/5; --_g: radial-gradient(#000 68%, #0000 71%) no-repeat; mask: var(--_g),var(--_g),var(--_g); mask-size: 25% 40%; }

Cool cool. But now we need a new animation that helps move the dots up and down between the animated gradients.

.loader { /* same as before */ animation: load 2s infinite; } @keyframes load { /* X Y, X Y, X Y */ 0% { mask-position: 0% 0% , 50% 0% , 100% 0%; } /* all of them at the top */ 16.67% { mask-position: 0% 100%, 50% 0% , 100% 0%; } 33.33% { mask-position: 0% 100%, 50% 100%, 100% 0%; } 50% { mask-position: 0% 100%, 50% 100%, 100% 100%; } /* all of them at the bottom */ 66.67% { mask-position: 0% 0% , 50% 100%, 100% 100%; } 83.33% { mask-position: 0% 0% , 50% 0% , 100% 100%; } 100% { mask-position: 0% 0% , 50% 0% , 100% 0%; } /* all of them at the top */ }

Yes, that’s a total of three radial gradients in there, all with the same configuration and the same size — the animation will update the position of each one. Note that the X coordinate of each dot is fixed. The mask-position is defined such that the first dot is at the left (0%), the second one at the center (50%), and the third one at the right (100%). We only update the Y coordinate from 0% to 100% to make the dots dance.

Here’s what we get:

CodePen Embed Fallback

Now, combine this with our gradient animation and magic starts to happen:

CodePen Embed Fallback Dot loader variations

The CSS variable we made in the last example makes it all that much easier to swap in new colors and create more variations of the same loader. For example, different colors and sizes:

CodePen Embed Fallback

What about another movement for our dots?

CodePen Embed Fallback

Here, all I did was update the animation to consider different positions, and we get another loader with the same code structure!

The animation technique I used for the mask layers can also be used with background layers to create a lot of different loaders with a single color. I wrote a detailed article about this. You will see that from the same code structure we can create different variations by simply changing a few values. I am sharing a few examples at the end of the article.

Why not a loader with one dot?

CodePen Embed Fallback

This one should be fairly easy to grok as I am using the same technique but with a more simple logic:

CodePen Embed Fallback

Here is another example of loader where I am also animating radial-gradient combined with CSS filters and mix-blend-mode to create a blobby effect:

CodePen Embed Fallback

If you check the code, you will see that all I am really doing there is animating the background-position, exactly like we did with the previous loader, but adding a dash of background-size to make it look like the blob gets bigger as it absorbs dots.

If you want to understand the magic behind that blob effect, you can refer to these interactive slides (Chrome only) by Ana Tudor because she covers the topic so well!

Here is another dot loader idea, this time using a different technique:

CodePen Embed Fallback

This one is only 10 CSS declarations and a keyframe. The main element and its two pseudo-elements have the same background configuration with one radial gradient. Each one creates one dot, for a total of three. The animation moves the gradient from top to bottom by using different delays for each dot..

Oh, and take note how this demo uses CSS Grid. This allows us to leverage the grid’s default stretch alignment so that both pseudo-elements cover the whole area of their parent. No need for sizing! Push the around a little with translate() and we’re all set.

More examples!

Just to drive the point home, I want to leave you with a bunch of additional examples that are really variations of what we’ve looked at. As you view the demos, you’ll see that the approaches we’ve covered here are super flexible and open up tons of design possibilities.

CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback Next up…

OK, so we covered dot loaders in this article and spinners in the last one. In the next article of this four-part series, we’ll turn our attention to another common type of loader: the bars. We’ll take a lot of what we learned so far and see how we can extend them to create yet another single element loader with as little code and as much flexibility as possible.

Article series

Single Element Loaders: The Dots originally published on CSS-Tricks. You should get the newsletter.

De-Mystifying IndieWeb on a WordPress Site

Css Tricks - Tue, 06/14/2022 - 6:48am

Well, sheesh. I opened a little can of worms when sharing Miriam’s “Am I on the IndieWeb yet?” with a short post bemoaning my own trouble getting on the IndieWeb train. But it’s a good can of worms.

I think it was something like the next day after publishing that short post that David Shanske reached out and offered to help wrap my head around IndieWeb and the components that it comprises. And gosh dang if it wasn’t ridiculously helpful! So much so that I’d like to link you up to a new post David wrote after we talked, then summarize things here as best as I can because (1) it’s helpful to write things down and (2) have a reference for later.

Yes, IndieWeb is confusing.

David had actually helped someone get their WordPress site all set up with IndieWeb powers. That person, too, was struggling to understand how the various pieces fit together. So, David already had this top of mind when Miriam and I were writing.

“IndieWeb” is a new term for many folks and that’s where a lot of confusion breeds. Is it a framework? A philosophy? A set of standards? Depending on which one it is, the expectations shift as far as what it looks like to be a part of it.

It’s sort of all of the above. And that needs to be solidified a bit. There’s nothing inherently confusing about IndieWeb itself once you view it through those different lenses. After reading David’s post my understanding is that IndieWeb is more of a set of protocols. Sorta like working with structured data or OpenGraph in markup. There’s nothing to install per se, but there are standards for how to integrate them into your work.

Your identity powers IndieWeb. In other words, your site establishes your identity and can be used to do lots of things, like:

  • Notify other IndieWeb-supported sites when they are mentioned
  • Receive notifications from other IndieWeb sites when you are mentioned
  • Fetch information from a mention and format it for display
  • Authenticate your identity through your own website (a là a Google sign-in button but connected to your site)
  • …among other things.

If that sorta sounds like pingbacks, well, it sorta is. But much more robust and maintained.

It’s different (and perhaps easier) to implement IndieWeb features on WordPress than it is a static site.

The big difference is that WordPress provides a lot of the requirements needed to do IndieWeb-y things. I like how David explains it:

The IndieWeb implementation on WordPress is a [series] of building blocks that you can or cannot choose to use, which is what makes it wonderful, but sometimes confusing. WordPress has a philosophy of decisions, not options. But the IndieWeb is all about options…about building the features that are right for you.

Those building blocks are plugins that you install to add IndieWeb protocols and technologies to WordPress. It’s awesome those are readily available because that takes a a lot of the work out of things. Running a static site, though, you’re on the hook for establishing most of that yourself.

David’s post is 100% focused on the WordPress implementation. Your mileage may vary, but you will certainly walk away with a better idea of what protocols are available and how they fit together after reading his post — and hopefully this one as well.

The IndieWeb WordPress plugin establishes your identity.

I thought it was doing so much stuff behind the scenes, but it’s a lot more simple than that:

The plugin by itself handles establishing your identity as the IndieWeb sees it. It offers an h-card template and widget. H-Card is the markup for marking up information about a person or place. So, this is an element many people opt to put on their site anyway.

So, really, it’s possible to get the same sort of thing by correctly marking up a WordPress theme. The convenience here is that you get a handy little template that’s marked up to support the h-card open format and a widget to drop it into a theme’s widget area.

Here’s a super detailed example of the h-card markup pulled from the documentation for Microformats2:

<div class="h-card"> <img class="u-photo" alt="photo of Mitchell" src="https://webfwd.org/content/about-experts/300.mitchellbaker/mentor_mbaker.jpg"/> <a class="p-name u-url" href="http://blog.lizardwrangler.com/" >Mitchell Baker</a> (<a class="u-url" href="https://twitter.com/MitchellBaker" >@MitchellBaker</a>) <span class="p-org">Mozilla Foundation</span> <p class="p-note"> Mitchell is responsible for setting the direction and scope of the Mozilla Foundation and its activities. </p> <span class="p-category">Strategy</span> <span class="p-category">Leadership</span> </div>

See those class names? Classes like .h-card, u-photo, p-name, etc. all provide contextual meaning for a person’s identity which it then parsed as JSON:

{ "items": [{ "type": ["h-card"], "properties": { "photo": ["https://webfwd.org/content/about-experts/300.mitchellbaker/mentor_mbaker.jpg"], "name": ["Mitchell Baker"], "url": [ "http://blog.lizardwrangler.com/", "https://twitter.com/MitchellBaker" ], "org": ["Mozilla Foundation"], "note": ["Mitchell is responsible for setting the direction and scope of the Mozilla Foundation and its activities."], "category": [ "Strategy", "Leadership" ] } }] }

The plugin isn’t doing the sending, receiving, or parsing. Instead, it provides a WordPress site with a way to verify your identity in the markup.

Not all WordPress themes support Microformats

If you scratched your head first time you saw “Microformats” like I did, David defines it nicely:

[…] Microformats…a way of marking up HTML to allow elements to be identified. It is one of several ways of doing this, but is a very simple and readable one, which is why it is popular in the IndieWeb community.

The problem, as David continues, is that many themes aren’t marked up in a Microformats-friendly way — which is what the Microformats2 plugin is designed to fix. That said, David is quick to call out that the plugin is extremely limited in how it accomplishes this, and he recommends instead marking up a theme by hand.

According to David, the next major release of the Webmention plugin will likely include smarter ways of detecting content and images it can use and formatting them for Microformats2 support.

Webmentions send and receive notifications.

OK, so if you’ve established your identity through your site so you are discoverable, and your site is marked up for h-card support using Microformats2. Great! You still need something in the middle working as an operator that sends and receives notifications. In other words, when another site mentions you — called a Webmention — the site mentioning you needs a way to support sending that mention to you, and your site needs a way to accept it (or vice versa).

That’s what the Webmention plugin is for. It’s also probably the source of most of my IndieWeb confusion. I thought it was formatting data and needed an additional service to send and receive it. Nope! It’s actually sending and receiving the data rather than creating the mention. Back to David:

Back when it was built, the plugin handled only the business of receiving and sending webmentions, not handling display to any degree. Semantic Linkbacks, a separate plugin handled that for not only webmentions, but the older pingback and trackback protocols.

So, the Webmention plugin is communicating notifications. Meanwhile, another plugin called Semantic Linkbacks is what handles the data. And what the heck are Semantic Linkbacks?

Semantic Linkbacks fetch and handle data.

Semantic Linkbacks is another plugin that handles another piece of the process. There’s no way I can explain it better than David already does:

Semantic Linkbacks takes a webmention, which is a notification that another site has linked to something on your site, fetches the other site, and tries to render a display of the information. How that is done can vary from just a profile photo (if it can find one), to interpreting it as a full comment.

It does this using Microformats.

I expected that the main IndieWeb plugin was already doing this since it handles other markup. But it only provides the template and widget to get your identity on your site. Once the Semantic Linkbacks plugin fetches an incoming webmention, it takes the data, formats it, then attempts to display it.

Sounds like the plugin will be somewhat merged with (or replaced by) an upcoming version of the Webmention plugin:

Since many people are not inclined, or not comfortable modifying a theme, the new version of Webmentions will include several different alternative ways to try to find an image or summary to display…from OpenGraph (which Facebook and Twitter use to display URLs provided to it) to detecting the WordPress REST API version of a page and using that to get the author name and profile image. None of them will provided as much context as Microformats, but the experience will still be something worth installing.

That’s certainly nice as it taps into the WordPress REST API for the JSON response and formats that for display.

Brid.gy is a service to help display interactions.

A Webmention can be an interaction, say someone likes your post on Twitter or retweets it.

Differentiating a like from a repost from a comment from a whatever needs to happen, and you’d need to implement the Twitter (or whatever) API to draw those distinctions.

That’s something you can certainly do! But if you’d rather plug and play, one of the IndieWeb community members made a service called Brid.gy. You create an account, hook up your site, and give app permissions to the service… then you’re done!

What Brid.gy has done is essentially implement the APIs for Twitter, Facebook, Instagram, and others, so that when it detects that a post in those services that interacts with your syndicated post, a Webmention is sent to your site and goes through the process of publishing on your own site.

There’s so much more!

Quick hits:

  • IndieAuth: This is a protocol based on OAuth 2. The plugin establishes an endpoint in the WordPress REST API that can be used to authenticate your identity through your own self-hosted site — essentially your own Google sign-in button but without establishing that endpoint yourself or needing to rely on a separate hosted API.
  • Micropub: For those who use WordPress but prefer a different editor can install the Micropub plugin. This adds an endpoint that allows you to publish content to your site and using a Micropub-supported editor create items in a Microformats2 feed, giving you way more options for writing content outside of the WordPress Block Editor.
  • Simple Location: David wrote this plugin and I was super impressed when he demoed it for me. The idea is it pulls in data from your current location that can used for everything from displaying the weather at the time you wrote a post, to creating an entire archive of posts on an embedded map based on the post location. I’d honestly love to see something like this baked directly into WordPress.
The updated flow

I attempted to make an illustration that outlines the various pieces in my last post, but let’s try again with an updated understanding of what’s happening:

(Full size) Is this all making sense?

High fives to David for both reaching out and taking the time to show me what it looks like to implement IndieWeb on WordPress. I can’t claim I fully understand all the nuances, but I at least feel like I have a decent grasp of the pieces — the philosophy, protocols, and tech — that are required to make it happen.

I’d like to turn it around to you! Does this help clarify things for you? Is there anything you’re struggling to understand? Think you’re able to configure a WordPress site with IndieWeb features now? Let’s take it to the comments!

De-Mystifying IndieWeb on a WordPress Site originally published on CSS-Tricks. You should get the newsletter.

Useful Tools for Visualizing Databases on a Budget

Css Tricks - Mon, 06/13/2022 - 9:15am

A diagram is a graphical representation of information that depicts the structure, relationship, or operation of anything. Diagrams enable your audience to visually grasp hidden information and engage with them in ways that words alone cannot. Depending on the type of project, there are numerous ways to use diagrams. For example, if you want to depict the relationship between distinct pieces, we usually use an Entity Relationship Diagram (ERD). There are many great tools that can help you sketch out your database designs beautifully.

In this article, I will be sharing some of my favorite tools that I use to curate my data structures and bring my ideas to life.

Google Docs Drawing

The drawing function in Google Docs allows you to add illustrations to your pages. You can add custom shapes, charts, graphs, infographics, and text boxes to your document with the built-in drawing tool.

Sketching with Google Docs

Although it is simple to add a graphic to your Google Docs, the procedure is not totally visible. Here’s how:

1 . Open a new document on Google Docs.

2 . Click on the insert button and select Drawing . Then, from the drop-down option, choose New to open the drawing screen.

3 . You can use the toolbox on this screen to add text boxes, select lines, and shapes, and modify the colors of your drawing.

4 . You may also use the cursor to adjust the size of your drawings and the color of your designs by using the toolbox at the top of your screen.

5 . When finished, click the Save and close button. You can click on the “File” toolbar displayed on the top of your screen to download your document.

Features CostFree.CLI? GUI? Online?Online.Requires an Account?Yes, a Google account is required.Collaborative Editing?Yes, with Google Drive sharing.Import SQLNot Applicable.Export SQLNot Applicable.Export Formats.doc, .pdf, .rtf, .odt, .txt, .html, .epubGenerate Shareable URLYes.

Google Docs offers amazing convenience. However, diagramming databases is not something it was intended for. You may find yourself frustrated with redrawing arrows and relationships if you are making frequent edits to your model.

Graphviz

Graphviz is a free graph visualization software that allows us to express information diagrammatically.

Graphviz implements the DOT language. The DOT language is an abstract grammar that makes use of terminals, non terminals, parentheses, square brackets, and vertical bars. More information about the DOT language can be found in its documentation.

Features CostFree.CLI? GUI? Online?CLI.
Visual Studio Code, Eclipse, and Notepad++.
Graphical Interfaces.Requires an Account?No.Collaborative Editing?Not Applicable.Import SQLYes, using SQL Graphviz.Export SQLYes, using SQL Graphviz.Export Formats.gif, .png, .jpeg, .json, .pdf and moreGenerate Shareable URLNot Applicable.

Graphviz has an impressive and supportive community. However, a high level of SQL support is only available when you install additional third-party software. This overhead may make it less approachable to users that are not comfortable setting up their computer to support these tools.

ERDPlus

ERDPlus is a database modeling tool that allows you to create Entity Relationship Diagrams, Relational Schemas, Star Schemas, and SQL DDL statements.

It includes a brief guide on how to create your ER diagrams, which is especially useful for beginners. You can also easily convert your created ER diagrams to relation schemas.

Features CostFree.CLI? GUI? Online?Online.Requires an Account?Not required, but recommended for saving.Collaborative Editing?Not Applicable.Import SQLNo.Export SQLYes, with the support of SQL DDL statements.Export Formats.pngGenerate Shareable URLNot Applicable.

ERDPlus is suited for SQL. It does lack additional export formats and ability to share with teams, but these features are not necessary with import and export.

Diagrams.net

Diagrams.net (previously Draw.io) is a free online diagramming tool that can be used to create flowcharts, UML diagrams, database models, and other types of diagrams.

Features CostFree.CLI? GUI? Online?Desktop and Online.Requires an Account?Not required, but recommended for saving.Collaborative Editing?Sharing requires Google Drive or OneDrive.Import SQLYes.Export SQLNo.Export Formats.png, .jpeg, .svg, .pdf, .html and more.Generate Shareable URLYes, export as URL an option.

Diagrams.net is designed to support many different workflows. Its ability to easily integrate with third-party integrations such as Trello, Quip, Notion, and others distinguishes it from the other options. The ability to share and collaborate may make it work well for collaborative teams.

Conclusion

This article is based on using free database tools that could help visualize your ideas and their capabilities with limitations to great details on how to use these tools.

In my research, I also came across other excellent tools with free trials available for creating database diagrams like Lucidchart, EDrawMax, and, DrawSQL. However, these free trials have limitations which may make them less suited for developers working on multiple projects.

I strongly recommend that you read the documentation for each of these tools to determine what works best for you and, most importantly, to avoid any difficulties in using these tools.

Thank you for taking the time to read this far, and I hope you found what you were looking for. Have a wonderful day!

Useful Tools for Visualizing Databases on a Budget originally published on CSS-Tricks. You should get the newsletter.

Single Element Loaders: The Spinner

Css Tricks - Fri, 06/10/2022 - 4:26am

Making CSS-only loaders is one of my favorite tasks. It’s always satisfying to look at those infinite animations. And, of course, there are lots of techniques and approaches to make them — no need to look further than CodePen to see just how many. In this article, though, we will see how to make a single element loader writing as little code as possible.

I have made a collection of more than 500 single div loaders and in this four-part series, I am going to share the tricks I used to create many of them. We will cover a huge number of examples, showing how small adjustments can lead to fun variations, and how little code we need to write to make it all happen!

Single-Element Loaders series:
  1. Single Element Loaders: The Spinner — you are here
  2. Single Element Loaders: The Dots
  3. Single Element Loaders: The Bars
  4. Single Element Loaders: Going 3D — coming July 1

For this first article, we are going to create a one of the more common loader patterns: spinning bars:

CodePen Embed Fallback Here’s the approach

A trivial implementation for this loader is to create one element for each bar wrapped inside a parent element (for nine total elements), then play with opacity and transform to get the spinning effect.

My implementation, though, requires only one element:

<div class="loader"></div>

…and 10 CSS declarations:

.loader { width: 150px; /* control the size */ aspect-ratio: 1; display: grid; mask: conic-gradient(from 22deg, #0003, #000); animation: load 1s steps(8) infinite; } .loader, .loader:before { --_g: linear-gradient(#17177c 0 0) 50%; /* update the color here */ background: var(--_g)/34% 8% space no-repeat, var(--_g)/8% 34% no-repeat space; } .loader:before { content: ""; transform: rotate(45deg); } @keyframes load { to { transform: rotate(1turn); } } Let’s break that down

At first glance, the code may look strange but you will see that it’s more simple than what you might think. The first step is to define the dimension of the element. In our case, it’s a 150px square. We can put aspect-ratio to use so the element stays square no matter what.

.loader { width: 150px; /* control the size */ aspect-ratio: 1; /* make height equal to width */ }

When building CSS loaders, I always try to have one value for controlling the overall size. In this case, it’s the width and all the calculations we cover will refer to that value. This allows me to change a single value to control the loader. It’s always important to be able to easily adjust the size of our loaders without the need to adjust a lot of additional values.

Next, we will use gradients to create the bars. This is the trickiest part! Let’s use one gradient to create two bars like the below:

background: linear-gradient(#17177c 0 0) 50%/34% 8% space no-repeat;

Our gradient is defined with one color and two color stops. The result is a solid color with no fading or transitions. The size is equal to 34% wide and 8% tall. It’s also placed in the center (50%). The trick is the use of the keyword value space — this duplicates the gradient, giving us two total bars.

From the specification:

The image is repeated as often as will fit within the background positioning area without being clipped and then the images are spaced out to fill the area. The first and last images touch the edges of the area.

I am using a width equal to 34% which means we cannot have more than two bars (3*34% is greater than 100%) but with two bars we will have empty spaces (100% - 2 * 34% = 32%). That space is placed in the center between the two bars. In other words, we use a width for the gradient that is between 33% and 50% to make sure we have at least two bars with a little bit of space between them. The value space is what correctly places them for us.

We do the same and make a second similar gradient to get two more bars at the top and bottom, which give us a background property value of:

background: linear-gradient(#17177c 0 0) 50%/34% 8% space no-repeat, linear-gradient(#17177c 0 0) 50%/8% 34% no-repeat space;

We can optimize that using a CSS variable to avoid repetition:

--_g: linear-gradient(#17177c 0 0) 50%; /* update the color here */ background: var(--_g)/34% 8% space no-repeat, var(--_g)/8% 34% no-repeat space;

So, now we have four bars and, thanks to CSS variables, we can write the color value once which makes it easy to update later (like we did with the size of the loader).

To create the remaining bars, let’s tap into the .loader element and its ::before pseudo-element to get four more bars for a grand total of eight in all.

.loader { width: 150px; /* control the size */ aspect-ratio: 1; display: grid; } .loader, .loader::before { --_g: linear-gradient(#17177c 0 0) 50%; /* update the color here */ background: var(--_g)/34% 8% space no-repeat, var(--_g)/8% 34% no-repeat space; } .loader::before { content: ""; transform: rotate(45deg); }

Note the use of display: grid. This allows us to rely on the grid’s default stretch alignment to make the pseudo-element cover the whole area of its parent; thus there’s no need to specify a dimension on it — another trick that reduces the code and avoid us to deal with a lot of values!

Now let’s rotate the pseudo-element by 45deg to position the remaining bars. Hover the following demo to see the trick:

CodePen Embed Fallback Setting opacity

What we’re trying to do is create the impression that there is one bar that leaves a trail of fading bars behind it as it travels a circular path. What we need now is to play with the transparency of our bars to make that trail, which we are going to do with CSS mask combined with a conic-gradient as follows:

mask: conic-gradient(from 22deg,#0003,#000);

To better see the trick, let’s apply this to a full-colored box:

CodePen Embed Fallback

The transparency of the red color is gradually increasing clockwise. We apply this to our loader and we have the bars with different opacity:

In reality, each bar appears to fade because it’s masked by a gradient and falls between two semi-transparent colors. It’s hardly noticeable when this runs, so it’s sort of like being able to say that all the bars have the same color with a different level of opacity.

The rotation

Let’s apply a rotation animation to get our loader. Note, that we need a stepped animation and not a continuous one that’s why I am using steps(8). 8 is nothing but the number of the bars, so that value can be changed depending on how many bars are in use.

.loader { animation: load 3s steps(8) infinite; } /* Same as before: */ @keyframes load { to { transform: rotate(1turn) } } CodePen Embed Fallback

That’s it! We have our loader with only one element and a few lines of CSS. We can easily control its size and color by adjusting one value.

CodePen Embed Fallback

Since we only used the ::before pseudo-element, we can add four more bars by using ::after to end with 12 bars in total and almost the same code:

CodePen Embed Fallback

We update the rotation of our pseudo-elements to consider 30deg and 60deg instead of 45deg while using an twelve-step animation, rather than eight. I also decreased the height to 5% instead of 8% to make the bars a little thinner.

Notice, too, that we have grid-area: 1/1 on the pseudo-elements. This allows us to place them in the same area as one another, stacked on top of each other.

Guess what? We can reach for the same loader using another implementation:

CodePen Embed Fallback

Can you figure out the logic behind the code? Here is a hint: the opacity is no longer handled with a CSS mask but inside the gradient and is also using the opacity property.

Why not dots instead?

We can totally do that:

CodePen Embed Fallback

If you check the code, you will see that we’re now working with a radial gradient instead of a linear one. Otherwise, the concept is exactly the same where the mask creates the impression of opacity, but we made the shapes as circles instead of lines.

Below is a figure to illustrate the new gradient configuration:

If you’re using Safari, note that the demo may be buggy. That’s because Safari currently lacks support for the at syntax in radial gradients. But we can reconfigure the gradient a bit to overcome that:

.loader, .loader:before, .loader:after { background: radial-gradient( circle closest-side, currentColor 90%, #0000 98% ) 50% -150%/20% 80% repeat-y, radial-gradient( circle closest-side, currentColor 90%, #0000 98% ) -150% 50%/80% 20% repeat-x; } CodePen Embed Fallback More loader examples

Here is another idea for a spinner loader similar to the previous one.

CodePen Embed Fallback

For this one, I am only relying on background and mask to create the shape (no pseudo-elements needed). I am also defining the configuration with CSS variables to be able to create a lot of variations from the same code — another example of just the powers of CSS variables. I wrote another article about this technique if you want to more details.

Note that some browsers still rely on a -webkit- prefix for mask-composite with its own set of values, and will not display the spinner in the demo. Here is a way to do it without mast-composite for more browser support.

I have another one for you:

CodePen Embed Fallback

For this one, I am using a background-color to control the color, and use mask and mask-composite to create the final shape:

Before we end, here are some more spinning loaders I made a while back. I am relying on different techniques but still using gradients, masks, pseudo-element, etc. It could be a good exercise to figure out the logic of each one and learn new tricks at the same time. This said, if you have any question about them, the comment section is down below.

CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback Wrapping up

See, there’s so much we can do in CSS with nothing but a single div, a couple of gradients, pseudo-elements, variables. It seems like we created a whole bunch of different spinning loaders, but they’re all basically the same thing with slight modifications.

This is only the the beginning. In this series, we will be looking at more ideas and advanced concepts for creating CSS loaders.

Single-Element Loaders series:
  1. Single Element Loaders: The Spinner — you are here
  2. Single Element Loaders: The Dots
  3. Single Element Loaders: The Bars
  4. Single Element Loaders: Going 3D — coming July 1

Single Element Loaders: The Spinner originally published on CSS-Tricks. You should get the newsletter.

Am I on the IndieWeb yet?

Css Tricks - Thu, 06/09/2022 - 5:32am

Can’t smash the Like button hard enough for what Miriam Suzanne has to say on the challenging technical hurdles of implementing Webmentions:

The first round required several online services along with HTML & JS changes to my static site, just to verify my indieweb identity. Then more changes to the site and more online services to help fetch any mentions (so far, nothing to see, but that’s probably expected). It seems the only way to test the setup is to launch all those changes publicly, and then ask for other devs to send you mentions.

[…]

I’m an experienced web developer, and I can figure it out. But the steps aren’t simple, and most of my friends are not web developers. So, to me, this all feels like the prototype of an idea – a proof of concept.

A proof of concept for sure. And one that has been around for quite some time. The IndieWeb idea of owning your own data and using your website as a social hub is right up my alley — and likely yours, too, as someone working on the front end.

Update! David Shanske is one of the developers of the WordPress plugins that support IndieWeb features and he not only published a wonderful explanation of how everything fits together, but also reached out directly and helped me wrap it all around my head.

Yet, I’ve tinkered on and off with it — specifically Webmentions — over the past like three years with little to show for it. The problem isn’t so much the documentation of getting started because it’s all there. It’s more a combination of things…

  • The wiki is confusing. Wikis are super cool in general, but the non-linear nature of it makes it tough to know where to start and where to end.
  • The plugin ecosystem is complex. My personal site is on WordPress and there’s a plugin designed to make it easy to integrate IndieWeb features on it. Except that it’s really one plugin that steers you to install several others, each one introducing a technology that I honestly struggle to understand.
  • There’s a bunch of terms to learn. I mean, “IndieWeb” and “Webmention” are already difficult to grok. Toss in things like “Micropub,” “Microformats,” “IndieAuth,” and “Semantic Linkbacks,” and suddenly it feels like a bunch of puzzle pieces from different puzzles trying to fit together.
  • Some middleware seems necessary? For example, I had to give a service called Bridgy access to my Twitter to get that activity going. It apparently has something to do with Twitter’s shortened t.co URLs and making them play well with microformats.

But, like Miriam, I struggled my way through it and got something working in the end. This is the sort of visual I wish I had when I was first getting started, and maybe it’ll help you too.

Feels like a lot to get Webmentions going, but maybe that’s only because I have such a light grasp of the tech and how it all fits together. All the pieces are there, though, and even with the initial struggle, I love the IndieWeb concept, er prototype.

To Shared LinkPermalink on CSS-Tricks

Am I on the IndieWeb yet? originally published on CSS-Tricks. You should get the newsletter.

Let’s Make a QR Code Generator With a Serverless Function!

Css Tricks - Wed, 06/08/2022 - 4:30am

QR codes are funny, right? We love them, then hate them, then love them again. Anyways, they’ve lately been popping up again and it got me thinking about how they’re made. There are like a gazillion QR code generators out there, but say it’s something you need to do on your own website. This package can do that. But it’s also weighs in at a hefty 180 KB for everything it needs to generate stuff. You wouldn’t want to serve all that along with the rest of your scripts.

Now, I’m relatively new to the concept of cloud functions, but I hear that’s the bee’s knees for something just like this. That way, the function lives somewhere on a server that can be called when it’s needed. Sorta like a little API to run the function.

Some hosts offer some sort of cloud function feature. DigitalOcean happens to be one of them! And, like Droplets, functions are pretty easy to deploy.

Create a functions folder locally

DigitalOcean has a CLI that with a command that’ll scaffold things for us, so cd wherever you want to set things up and run:

doctl serverless init --language js qr-generator

Notice the language is explicitly declared. DigitalOcean functions also support PHP and Python.

We get a nice clean project called qr-generator with a /packages folder that holds all the project’s functions. There’s a sample function in there, but we can overlook it for now and create a qr folder right next to it:

That folder is where both the qrcode package and our qr.js function are going to live. So, let’s cd into packages/sample/qr and install the package:

npm install --save qrcode

Now we can write the function in a new qr.js file:

const qrcode = require('qrcode') exports.main = (args) => { return qrcode.toDataURL(args.text).then(res => ({ headers: { 'content-type': 'text/html; charset=UTF-8' }, body: args.img == undefined ? res : `<img src="${res}">` })) } if (process.env.TEST) exports.main({text:"hello"}).then(console.log)

All that’s doing is requiring the the qrcode package and exporting a function that basically generates an <img> tag with the a base64 PNG for the source. We can even test it out in the terminal:

doctl serverless functions invoke sample/qr -p "text:css-tricks.com" Check the config file

There is one extra step we need here. When the project was scaffolded, we got this little project.yml file and it configures the function with some information about it. This is what’s in there by default:

targetNamespace: '' parameters: {} packages: - name: sample environment: {} parameters: {} annotations: {} actions: - name: hello binary: false main: '' runtime: 'nodejs:default' web: true parameters: {} environment: {} annotations: {} limits: {}

See those highlighted lines? The packages: name property is where in the packages folder the function lives, which is a folder called sample in this case. The actions/ name property is the name of the function itself, which is the name of the file. It’s hello by default when we spin up the project, but we named ours qr.js, so we oughta change that line from hello to qr before moving on.

Deploy the function

We can do it straight from the command line! First, we connect to the DigitalOcean sandbox environment so we have a live URL for testing:

## You will need an DO API key handy doctl sandbox connect

Now we can deploy the function:

doctl sandbox deploy qr-generator

Once deployed, we can access the function at a URL. What’s the URL? There’s a command for that:

doctl sbx fn get sample/qr --url https://faas-nyc1-2ef2e6cc.doserverless.co/api/v1/web/fn-10a937cb-1f12-427b-aadd-f43d0b08d64a/sample/qr

Heck yeah! No more need to ship that entire package with the rest of the scripts! We can hit that URL and generate the QR code from there.

Demo

We fetch the API and that’s really all there is to it!

CodePen Embed Fallback

Let’s Make a QR Code Generator With a Serverless Function! originally published on CSS-Tricks. You should get the newsletter.

Building Interoperable Web Components That Even Work With React

Css Tricks - Tue, 06/07/2022 - 3:57am

Those of us who’ve been web developers more than a few years have probably written code using more than one JavaScript framework. With all the choices out there — React, Svelte, Vue, Angular, Solid — it’s all but inevitable. One of the more frustrating things we have to deal with when working across frameworks is re-creating all those low-level UI components: buttons, tabs, dropdowns, etc. What’s particularly frustrating is that we’ll typically have them defined in one framework, say React, but then need to rewrite them if we want to build something in Svelte. Or Vue. Or Solid. And so on.

Wouldn’t it be better if we could define these low-level UI components once, in a framework-agnostic way, and then re-use them between frameworks? Of course it would! And we can; web components are the way. This post will show you how.

As of now, the SSR story for web components is a bit lacking. Declarative shadow DOM (DSD) is how a web component is server-side rendered, but, as of this writing, it’s not integrated with your favorite application frameworks like Next, Remix or SvelteKit. If that’s a requirement for you, be sure to check the latest status of DSD. But otherwise, if SSR isn’t something you’re using, read on.

First, some context

Web Components are essentially HTML elements that you define yourself, like <yummy-pizza> or whatever, from the ground up. They’re covered all over here at CSS-Tricks (including an extensive series by Caleb Williams and one by John Rhea) but we’ll briefly walk through the process. Essentially, you define a JavaScript class, inherit it from HTMLElement, and then define whatever properties, attributes and styles the web component has and, of course, the markup it will ultimately render to your users.

Being able to define custom HTML elements that aren’t bound to any particular component is exciting. But this freedom is also a limitation. Existing independently of any JavaScript framework means you can’t really interact with those JavaScript frameworks. Think of a React component which fetches some data and then renders some other React component, passing along the data. This wouldn’t really work as a web component, since a web component doesn’t know how to render a React component.

Web components particularly excel as leaf components. Leaf components are the last thing to be rendered in a component tree. These are the components which receive some props, and render some UI. These are not the components sitting in the middle of your component tree, passing data along, setting context, etc. — just pure pieces of UI that will look the same, no matter which JavaScript framework is powering the rest of the app.

The web component we’re building

Rather than build something boring (and common), like a button, let’s build something a little bit different. In my last post we looked at using blurry image previews to prevent content reflow, and provide a decent UI for users while our images load. We looked at base64 encoding a blurry, degraded versions of our images, and showing that in our UI while the real image loaded. We also looked at generating incredibly compact, blurry previews using a tool called Blurhash.

That post showed you how to generate those previews and use them in a React project. This post will show you how to use those previews from a web component so they can be used by any JavaScript framework.

But we need to walk before we can run, so we’ll walk through something trivial and silly first to see exactly how web components work.

Everything in this post will build vanilla web components without any tooling. That means the code will have a bit of boilerplate, but should be relatively easy to follow. Tools like Lit or Stencil are designed for building web components and can be used to remove much of this boilerplate. I urge you to check them out! But for this post, I’ll prefer a little more boilerplate in exchange for not having to introduce and teach another dependency.

A simple counter component

Let’s build the classic “Hello World” of JavaScript components: a counter. We’ll render a value, and a button that increments that value. Simple and boring, but it’ll let us look at the simplest possible web component.

In order to build a web component, the first step is to make a JavaScript class, which inherits from HTMLElement:

class Counter extends HTMLElement {}

The last step is to register the web component, but only if we haven’t registered it already:

if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter); }

And, of course, render it:

<counter-wc></counter-wc>

And everything in between is us making the web component do whatever we want it to. One common lifecycle method is connectedCallback, which fires when our web component is added to the DOM. We could use that method to render whatever content we’d like. Remember, this is a JS class inheriting from HTMLElement, which means our this value is the web component element itself, with all the normal DOM manipulation methods you already know and love.

At it’s most simple, we could do this:

class Counter extends HTMLElement { connectedCallback() { this.innerHTML = "<div style='color: green'>Hey</div>"; } } if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter); }

…which will work just fine.

Adding real content

Let’s add some useful, interactive content. We need a <span> to hold the current number value and a <button> to increment the counter. For now, we’ll create this content in our constructor and append it when the web component is actually in the DOM:

constructor() { super(); const container = document.createElement('div'); this.valSpan = document.createElement('span'); const increment = document.createElement('button'); increment.innerText = 'Increment'; increment.addEventListener('click', () => { this.#value = this.#currentValue + 1; }); container.appendChild(this.valSpan); container.appendChild(document.createElement('br')); container.appendChild(increment); this.container = container; } connectedCallback() { this.appendChild(this.container); this.update(); }

If you’re really grossed out by the manual DOM creation, remember you can set innerHTML, or even create a template element once as a static property of your web component class, clone it, and insert the contents for new web component instances. There’s probably some other options I’m not thinking of, or you can always use a web component framework like Lit or Stencil. But for this post, we’ll continue to keep it simple.

Moving on, we need a settable JavaScript class property named value

#currentValue = 0; set #value(val) { this.#currentValue = val; this.update(); }

It’s just a standard class property with a setter, along with a second property to hold the value. One fun twist is that I’m using the private JavaScript class property syntax for these values. That means nobody outside our web component can ever touch these values. This is standard JavaScript that’s supported in all modern browsers, so don’t be afraid to use it.

Or feel free to call it _value if you prefer. And, lastly, our update method:

update() { this.valSpan.innerText = this.#currentValue; }

It works!

Obviously this is not code you’d want to maintain at scale. Here’s a full working example if you’d like a closer look. As I’ve said, tools like Lit and Stencil are designed to make this simpler.

Adding some more functionality

This post is not a deep dive into web components. We won’t cover all the APIs and lifecycles; we won’t even cover shadow roots or slots. There’s endless content on those topics. My goal here is to provide a decent enough introduction to spark some interest, along with some useful guidance on actually using web components with the popular JavaScript frameworks you already know and love.

To that end, let’s enhance our counter web component a bit. Let’s have it accept a color attribute, to control the color of the value that’s displayed. And let’s also have it accept an increment property, so consumers of this web component can have it increment by 2, 3, 4 at a time. And to drive these state changes, let’s use our new counter in a Svelte sandbox — we’ll get to React in a bit.

We’ll start with the same web component as before and add a color attribute. To configure our web component to accept and respond to an attribute, we add a static observedAttributes property that returns the attributes that our web component listens for.

static observedAttributes = ["color"];

With that in place, we can add a attributeChangedCallback lifecycle method, which will run whenever any of the attributes listed in observedAttributes are set, or updated.

attributeChangedCallback(name, oldValue, newValue) { if (name === "color") { this.update(); } }

Now we update our update method to actually use it:

update() { this.valSpan.innerText = this._currentValue; this.valSpan.style.color = this.getAttribute("color") || "black"; }

Lastly, let’s add our increment property:

increment = 1;

Simple and humble.

Using the counter component in Svelte

Let’s use what we just made. We’ll go into our Svelte app component and add something like this:

<script> let color = "red"; </script> <style> main { text-align: center; } </style> <main> <select bind:value={color}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc color={color}></counter-wc> </main>

And it works! Our counter renders, increments, and the dropdown updates the color. As you can see, we render the color attribute in our Svelte template and, when the value changes, Svelte handles the legwork of calling setAttribute on our underlying web component instance. There’s nothing special here: this is the same thing it already does for the attributes of any HTML element.

Things get a little bit interesting with the increment prop. This is not an attribute on our web component; it’s a prop on the web component’s class. That means it needs to be set on the web component’s instance. Bear with me, as things will wind up much simpler in a bit.

First, we’ll add some variables to our Svelte component:

let increment = 1; let wcInstance;

Our powerhouse of a counter component will let you increment by 1, or by 2:

<button on:click={() => increment = 1}>Increment 1</button> <button on:click={() => increment = 2}>Increment 2</button>

But, in theory, we need to get the actual instance of our web component. This is the same thing we always do anytime we add a ref with React. With Svelte, it’s a simple bind:this directive:

<counter-wc bind:this={wcInstance} color={color}></counter-wc>

Now, in our Svelte template, we listen for changes to our component’s increment variable and set the underlying web component property.

$: { if (wcInstance) { wcInstance.increment = increment; } }

You can test it out over at this live demo.

We obviously don’t want to do this for every web component or prop we need to manage. Wouldn’t it be nice if we could just set increment right on our web component, in markup, like we normally do for component props, and have it, you know, just work? In other words, it’d be nice if we could delete all usages of wcInstance and use this simpler code instead:

<counter-wc increment={increment} color={color}></counter-wc>

It turns out we can. This code works; Svelte handles all that legwork for us. Check it out in this demo. This is standard behavior for pretty much all JavaScript frameworks.

So why did I show you the manual way of setting the web component’s prop? Two reasons: it’s useful to understand how these things work and, a moment ago, I said this works for “pretty much” all JavaScript frameworks. But there’s one framework which, maddeningly, does not support web component prop setting like we just saw.

React is a different beast

React. The most popular JavaScript framework on the planet does not support basic interop with web components. This is a well-known problem that’s unique to React. Interestingly, this is actually fixed in React’s experimental branch, but for some reason wasn’t merged into version 18. That said, we can still track the progress of it. And you can try this yourself with a live demo.

The solution, of course, is to use a ref, grab the web component instance, and manually set increment when that value changes. It looks like this:

import React, { useState, useRef, useEffect } from 'react'; import './counter-wc'; export default function App() { const [increment, setIncrement] = useState(1); const [color, setColor] = useState('red'); const wcRef = useRef(null); useEffect(() => { wcRef.current.increment = increment; }, [increment]); return ( <div> <div className="increment-container"> <button onClick={() => setIncrement(1)}>Increment by 1</button> <button onClick={() => setIncrement(2)}>Increment by 2</button> </div> <select value={color} onChange={(e) => setColor(e.target.value)}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc ref={wcRef} increment={increment} color={color}></counter-wc> </div> ); } Live demo

As we discussed, coding this up manually for every web component property is simply not scalable. But all is not lost because we have a couple of options.

Option 1: Use attributes everywhere

We have attributes. If you clicked the React demo above, the increment prop wasn’t working, but the color correctly changed. Can’t we code everything with attributes? Sadly, no. Attribute values can only be strings. That’s good enough here, and we’d be able to get somewhat far with this approach. Numbers like increment can be converted to and from strings. We could even JSON stringify/parse objects. But eventually we’ll need to pass a function into a web component, and at that point we’d be out of options.

Option 2: Wrap it

There’s an old saying that you can solve any problem in computer science by adding a level of indirection (except the problem of too many levels of indirection). The code to set these props is pretty predictable and simple. What if we hide it in a library? The smart folks behind Lit have one solution. This library creates a new React component for you after you give it a web component, and list out the properties it needs. While clever, I’m not a fan of this approach.

Rather than have a one-to-one mapping of web components to manually-created React components, what I prefer is just one React component that we pass our web component tag name to (counter-wc in our case) — along with all the attributes and properties — and for this component to render our web component, add the ref, then figure out what is a prop and what is an attribute. That’s the ideal solution in my opinion. I don’t know of a library that does this, but it should be straightforward to create. Let’s give it a shot!

This is the usage we’re looking for:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

wcTag is the web component tag name; the rest are the properties and attributes we want passed along.

Here’s what my implementation looks like:

import React, { createElement, useRef, useLayoutEffect, memo } from 'react'; const _WcWrapper = (props) => { const { wcTag, children, ...restProps } = props; const wcRef = useRef(null); useLayoutEffect(() => { const wc = wcRef.current; for (const [key, value] of Object.entries(restProps)) { if (key in wc) { if (wc[key] !== value) { wc[key] = value; } } else { if (wc.getAttribute(key) !== value) { wc.setAttribute(key, value); } } } }); return createElement(wcTag, { ref: wcRef }); }; export const WcWrapper = memo(_WcWrapper);

The most interesting line is at the end:

return createElement(wcTag, { ref: wcRef });

This is how we create an element in React with a dynamic name. In fact, this is what React normally transpiles JSX into. All our divs are converted to createElement("div") calls. We don’t normally need to call this API directly but it’s there when we need it.

Beyond that, we want to run a layout effect and loop through every prop that we’ve passed to our component. We loop through all of them and check to see if it’s a property with an in check that checks the web component instance object as well as its prototype chain, which will catch any getters/setters that wind up on the class prototype. If no such property exists, it’s assumed to be an attribute. In either case, we only set it if the value has actually changed.

If you’re wondering why we use useLayoutEffect instead of useEffect, it’s because we want to immediately run these updates before our content is rendered. Also, note that we have no dependency array to our useLayoutEffect; this means we want to run this update on every render. This can be risky since React tends to re-render a lot. I ameliorate this by wrapping the whole thing in React.memo. This is essentially the modern version of React.PureComponent, which means the component will only re-render if any of its actual props have changed — and it checks whether that’s happened via a simple equality check.

The only risk here is that if you’re passing an object prop that you’re mutating directly without re-assigning, then you won’t see the updates. But this is highly discouraged, especially in the React community, so I wouldn’t worry about it.

Before moving on, I’d like to call out one last thing. You might not be happy with how the usage looks. Again, this component is used like this:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

Specifically, you might not like passing the web component tag name to the <WcWrapper> component and prefer instead the @lit-labs/react package above, which creates a new individual React component for each web component. That’s totally fair and I’d encourage you to use whatever you’re most comfortable with. But for me, one advantage with this approach is that it’s easy to delete. If by some miracle React merges proper web component handling from their experimental branch into main tomorrow, you’d be able to change the above code from this:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

…to this:

<counter-wc ref={wcRef} increment={increment} color={color} />

You could probably even write a single codemod to do that everywhere, and then delete <WcWrapper> altogether. Actually, scratch that: a global search and replace with a RegEx would probably work.

The implementation

I know, it seems like it took a journey to get here. If you recall, our original goal was to take the image preview code we looked at in my last post, and move it to a web component so it can be used in any JavaScript framework. React’s lack of proper interop added a lot of detail to the mix. But now that we have a decent handle on how to create a web component, and use it, the implementation will almost be anti-climactic.

I’ll drop the entire web component here and call out some of the interesting bits. If you’d like to see it in action, here’s a working demo. It’ll switch between my three favorite books on my three favorite programming languages. The URL for each book will be unique each time, so you can see the preview, though you’ll likely want to throttle things in your DevTools Network tab to really see things taking place.

View entire code class BookCover extends HTMLElement { static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); } } set preview(val) { this.previewEl = this.createPreview(val); this.render(); } createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); } } createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () =&gt; { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img; } connectedCallback() { this.render(); } render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe); } }

First, we register the attribute we’re interested in and react when it changes:

static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); } }

This causes our image component to be created, which will show only when loaded:

createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () => { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img; }

Next we have our preview property, which can either be our base64 preview string, or our blurhash packet:

set preview(val) { this.previewEl = this.createPreview(val); this.render(); } createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); } }

This defers to whichever helper function we need:

function base64Preview(val) { const img = document.createElement('img'); img.src = val; return img; } function blurHashPreview(preview) { const canvasEl = document.createElement('canvas'); const { w: width, h: height } = preview; canvasEl.width = width; canvasEl.height = height; const pixels = decode(preview.blurhash, width, height); const ctx = canvasEl.getContext('2d'); const imageData = ctx.createImageData(width, height); imageData.data.set(pixels); ctx.putImageData(imageData, 0, 0); return canvasEl; }

And, lastly, our render method:

connectedCallback() { this.render(); } render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe); }

And a few helpers methods to tie everything together:

export function syncSingleChild(container, child) { const currentChild = container.firstElementChild; if (currentChild !== child) { clearContainer(container); if (child) { container.appendChild(child); } } } export function clearContainer(el) { let child; while ((child = el.firstElementChild)) { el.removeChild(child); } }

It’s a little bit more boilerplate than we’d need if we build this in a framework, but the upside is that we can re-use this in any framework we’d like — although React will need a wrapper for now, as we discussed.

Odds and ends

I’ve already mentioned Lit’s React wrapper. But if you find yourself using Stencil, it actually supports a separate output pipeline just for React. And the good folks at Microsoft have also created something similar to Lit’s wrapper, attached to the Fast web component library.

As I mentioned, all frameworks not named React will handle setting web component properties for you. Just note that some have some special flavors of syntax. For example, with Solid.js, <your-wc value={12}> always assumes that value is a property, which you can override with an attr prefix, like <your-wc attr:value={12}>.

Wrapping up

Web components are an interesting, often underused part of the web development landscape. They can help reduce your dependence on any single JavaScript framework by managing your UI, or “leaf” components. While creating these as web components — as opposed to Svelte or React components — won’t be as ergonomic, the upside is that they’ll be widely reusable.

Building Interoperable Web Components That Even Work With React originally published on CSS-Tricks. You should get the newsletter.

Please Give Me Some Space

Css Tricks - Fri, 06/03/2022 - 4:39am

There’s all kinds of ways to do that. Some more advisable and better-suited for certain situations than others, of course.

We could do it directly in HTML:

<p>We go from one line...<br><br> down a couple more.</p>

But that’s what CSS is really for:

<p>We go from one line...<span>down a couple more.</span></p> span { display: block; margin-block-start: 1.5rem; }

Line height can also give us extra breathing room between lines of text:

p { line-height: 1.35; }

Since we’re talking text, there’s also letter-spacing and word-spacing, not to mention text-indent:

CodePen Embed Fallback

But let’s talk boxes instead of text. Say we have two simple divs:

<div>Twiddle Dee</div> <div>Twiddle Dum</div>

Those are block-level so they’re already on different lines. We can reach for margin again. Or we could create the impression of space with padding. I suppose we could translate those suckers in either direction:

div:nth-child(2) { transform: translateY(100px); }

But maybe those elements are absolutely positioned so we can use physical offsets:

div { position: absolute; } div:nth-child(1) { inset: 0; } div:nth-child(2) { inset-inline-start: 100px; /* or top: 100px; */ }

If we’re working in a grid container, then we get gap-age:

<section> <div>Twiddle Dee</div> <div>Twiddle Dum</div> </section> section { display: grid; grid-template-columns: 1fr 1fr; gap: 100px; }

Same deal with a flexible container:

section { display: flex; gap: 100px; }

While we’re working in grid and flexible containers, we could call on any alignment property to generate space.

section { display: flex; align-items: space-between; justify-content: space-between; }

There are tables, of course:

<table cellspacing="100"> <!-- etc. --> <tbody> <tr> <td>Twiddle Dee</td> <td>Twiddle Dum</td> </tr> </tbody> </table>

Or the CSS-y approach:

/* We could use `display: table` if we're not working in a table element. */ table { border-spacing: 100px; }

Let’s go deeper into left field. We can make one element look like two using a linear gradient with a hard color stop:

div { background-image: linear-gradient( to right, rgb(255 105 0 / 1) 50%, rgb(207 46 46 / 1) 50%, rgb(207 46 46 / 1) 100% ); }

Then we do a head fake and insert a hard transparent color stop between the two colors:

CodePen Embed Fallback

As long as we’re fakin’ bacon here, might as well toss in the ol’ “transparent” border trick:

CodePen Embed Fallback

Let’s go back to text for a moment. Maybe we’re floating an element and want text to wrap around it… in the shape of the floated element while leaving some space between the two. We have shape-margin for that:

CodePen Embed Fallback

Dare I even mention the spacer.gif days?

<div>Twiddle Dee</div> <img src="spacer.gif"> <!-- &#x1f922; --> <div>Twiddle Dum</div> There’s gotta be more

You’re all a smart bunch with great ideas. Have at it!

Please Give Me Some Space originally published on CSS-Tricks. You should get the newsletter.

How to Create Block Theme Patterns in WordPress 6.0

Css Tricks - Wed, 06/01/2022 - 5:30am

Block patterns, also frequently referred to as sections, were introduced in WordPress 5.5 to allow users to build and share predefined block layouts in the pattern directory. The directory is the home of a wide range of curated patterns designed by the WordPress community. These patterns are available in simple copy and paste format, require no coding knowledge and thus are a big time saver for users.

Despite many articles on patterns, there is a lack of comprehensive and up-to-date articles on pattern creation covering the latest enhanced features. This article aims to fill the gap with a focus on the recent enhanced features like creating patterns without registration and offer an up-to-date step-by-step guide to create and use them in block themes for novices and experienced authors.

Since the launch of WordPress 5.9 and the Twenty Twenty-Two (TT2) default theme, the use of block patterns in block themes has proliferated. I have been a big fan of block patterns and have created and used them in my block themes.

The new WordPress 6.0 offers three major patterns feature enhancements to authors:

  • Allowing pattern registration through /patterns folder (similar to /parts, /templates, and /styles registration).
  • Registering patterns from the public patterns directory using the theme.json.
  • Adding patterns that can be offered to the user when creating a new page.

In an introductory Exploring WordPress 6.0 video, Automattic product liaison Ann McCathy highlights some newly enhanced patterns features (starting at 15:00) and discusses future patterns enhancement plans — which include patterns as sectioning elements, providing options to pick pattern on page creation, integrating pattern directory search, and more.

Prerequisites

The article assumes that readers have basic knowledge of WordPress full site editing (FSE) interface and block themes. The Block Editor Handbook and Full Site Editing website provide the most up-to-date tutorial guides to learn all FSE features, including block themes and patterns discussed in this article.

Section 1: Evolving approaches to creating block patterns

The initial approach to creating block patterns required the use of block pattern API either as a custom plugin or directly registered in the functions.php file to bundle with a block theme. The newly launched WordPress 6.0 introduced several new and enhanced features working with patterns and themes, including pattern registration via a /patterns folder and a page creation pattern modal.

For background, let’s first briefly overview how the pattern registration workflow evolved from using the register pattern API to directly loading without registration.

Use case example 1: Twenty Twenty-One

The default Twenty Twenty-One theme (TT1) and TT1 Blocks theme (a sibling of TT1) showcase how block patterns can be registered in the theme’s functions.php. In the TT1 Blocks experimental-theme, this single block-pattern.php file containing eight block patterns is added to the functions.php as an include as shown here.

A custom block pattern needs to be registered using the register_block_pattern function, which receives two arguments — title (name of the patterns) and properties (an array describing properties of the pattern).

Here is an example of registering a simple “Hello World” paragraph pattern from this Theme Shaper article:

register_block_pattern( 'my-plugin/hello-world', array( 'title' => __( 'Hello World', 'my-plugin' ), 'content' => "<!-- wp:paragraph -->\n<p>Hello World</p>\n<!-- /wp:paragraph -->", ) );

After registration, the register_block_pattern() function should be called from a handler attached to the init hook as described here.

function my_plugin_register_my_patterns() { register_block_pattern( ... ); } add_action( 'init', 'my_plugin_register_my_patterns' );

Once block patterns are registered they are visible in the block editor. More detailed documentation is found in this Block Pattern Registration.

Block pattern properties

In addition to required title and content properties, the block editor handbook lists the following optional pattern properties:

  • title (required): A human-readable title for the pattern.
  • content (required): Block HTML Markup for the pattern.
  • description (optional): A visually hidden text used to describe the pattern in the inserter. A description is optional but it is strongly encouraged when the title does not fully describe what the pattern does. The description will help users discover the pattern while searching.
  • categories (optional): An array of registered pattern categories used to group block patterns. Block patterns can be shown on multiple categories. A category must be registered separately in order to be used here.
  • keywords (optional): An array of aliases or keywords that help users discover the pattern while searching.
  • viewportWidth (optional): An integer specifying the intended width of the pattern to allow for a scaled preview of the pattern in the inserter.
  • blockTypes (optional): An array of block types that the pattern is intended to be used with. Each value needs to be the declared block’s name.
  • inserter (optional): By default, all patterns will appear in the inserter. To hide a pattern so that it can only be inserted programmatically, set the inserter to false.

The following is an example of a quote pattern plugin code snippets taken from the WordPress blog.

/* Plugin Name: Quote Pattern Example Plugin */ register_block_pattern( 'my-plugin/my-quote-pattern', array( 'title' => __( 'Quote with Avatar', 'my-plugin' ), 'categories' => array( 'text' ), 'description' => _x( 'A big quote with an avatar".', 'Block pattern description', 'my-plugin' ), 'content' => '<!-- wp:group --><div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --><!-- wp:image {"align":"center","id":553,"width":150,"height":150,"sizeSlug":"large","linkDestination":"none","className":"is-style-rounded"} --><div class="wp-block-image is-style-rounded"><figure class="aligncenter size-large is-resized"><img src="https://blockpatterndesigns.mystagingwebsite.com/wp-content/uploads/2021/02/StockSnap_HQR8BJFZID-1.jpg" alt="" class="wp-image-553" width="150" height="150"/></figure></div><!-- /wp:image --><!-- wp:quote {"align":"center","className":"is-style-large"} --><blockquote class="wp-block-quote has-text-align-center is-style-large"><p>"Contributing makes me feel like I\'m being useful to the planet."</p><cite>— Anna Wong, <em>Volunteer</em></cite></blockquote><!-- /wp:quote --><!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator --></div></div><!-- /wp:group -->', ) );

Using patterns in a template file

Once patterns are created, they can be used in a theme template file with the following block markup:

<!-- wp:pattern {"slug":"prefix/pattern-slug"} /-->

An example from this GitHub repository shows the use of “footer-with-background” pattern slug with “tt2gopher” prefix in TT2 Gopher blocks theme.

Early adopters of block themes and Gutenberg plugin took advantage of patterns in classic themes as well. The default Twenty Twenty and my favorite Eksell themes (a demo site here) are good examples that showcase how pattern features can be added to classic themes.

Use case example 2: Twenty Twenty-Two

If a theme includes only a few patterns, the development and maintenance are fairly manageable. However, if a block theme includes many patterns, like in TT2 theme, then the pattern.php file becomes very large and hard to read. The default TT2 theme, which bundles more than 60 patterns, showcases a refactored pattern registration workflow structure that is easier to read and maintain.

Taking examples from the TT2 theme, let’s briefly discuss how this simplified workflow works.

2.1: Registering Patterns Categories

For demonstration purposes, I created a TT2 child theme and set it up on my local test site with some dummy content. Following TT2, I registered footer-with-background and added to the following pattern categories array list in its block-patterns.php file.

/** * Registers block patterns and categories. */ function twentytwentytwo_register_block_patterns() { $block_pattern_categories = array( 'footer' => array( 'label' => __( 'Footers', 'twentytwentytwo' ) ), 'header' => array( 'label' => __( 'Headers', 'twentytwentytwo' ) ), 'pages' => array( 'label' => __( 'Pages', 'twentytwentytwo' ) ), // ... ); /** * Filters the theme block pattern categories. */ $block_pattern_categories = apply_filters( 'twentytwentytwo_block_pattern_categories', $block_pattern_categories ); foreach ( $block_pattern_categories as $name => $properties ) { if ( ! WP_Block_Pattern_Categories_Registry::get_instance()->is_registered( $name ) ) { register_block_pattern_category( $name, $properties ); } } $block_patterns = array( 'footer-default', 'footer-dark', 'footer-with-background', //... 'header-default', 'header-large-dark', 'header-small-dark', 'hidden-404', 'hidden-bird', //... ); /** * Filters the theme block patterns. */ $block_patterns = apply_filters( 'twentytwentytwo_block_patterns', $block_patterns ); foreach ( $block_patterns as $block_pattern ) { $pattern_file = get_theme_file_path( '/inc/patterns/' . $block_pattern . '.php' ); register_block_pattern( 'twentytwentytwo/' . $block_pattern, require $pattern_file ); } } add_action( 'init', 'twentytwentytwo_register_block_patterns', 9 );

In this code example, each pattern listed in the $block_patterns = array() is called by foreach() function which requires a patterns directory file with the listed pattern name in the array which we will add in the next step.

2.2: Adding a pattern file to the /inc/patterns folder

Next, we should have all the listed patterns files in the $block_patterns = array(). Here is an example of one of the pattern files, footer-with-background.php:

/** * Dark footer wtih title and citation */ return array( 'title' => __( 'Footer with background', 'twentytwentytwo' ), 'categories' => array( 'footer' ), 'blockTypes' => array( 'core/template-part/footer' ), 'content' => '<!-- wp:group {"align":"full","style":{"elements":{"link":{"color":{"text":"var:preset|color|background"}}},"spacing":{"padding":{"top":"var(--wp--custom--spacing--small, 1.25rem)","bottom":"var(--wp--custom--spacing--small, 1.25rem)"}}},"backgroundColor":"background-header","textColor":"background","layout":{"inherit":true}} --> <div class="wp-block-group alignfull has-background-color has-background-header-background-color has-text-color has-background has-link-color" style="padding-top:var(--wp--custom--spacing--small, 1.25rem);padding-bottom:var(--wp--custom--spacing--small, 1.25rem)"><!-- wp:paragraph {"align":"center"} --> <p class="has-text-align-center">' . sprintf( /* Translators: WordPress link. */ esc_html__( 'Proudly powered by %s', 'twentytwentytwo' ), '<a href="' . esc_url( __( 'https://wordpress.org', 'twentytwentytwo' ) ) . '" rel="nofollow">WordPress</a> | a modified TT2 theme.' ) . '</p> <!-- /wp:paragraph --></div> <!-- /wp:group -->', );

Let’s reference the pattern in the footer.html template part:

<!-- wp:template-part {"slug":"footer"} /-->

This is similar to adding heading or footer parts in a template file.

The patterns can similarly be added to the parts/footer.html template by modifying it to refer to slug of the theme’s pattern file (footer-with-background):

<!-- wp:pattern {"slug":"twentytwentytwo/footer-with-background"} /-->

Now, if we visit the patterns inserter of the block editor, the Footer with background should be available for our use:

The following screenshot shows the newly created footer with background pattern on the front-end.

Now that patterns have become the integral part of block theme, many patterns are bundled in block themes — like Quadrat, Seedlet, Mayland, Zoologist, Geologist — following the workflow discussed above. Here is an example of the Quadrat theme /inc/patterns folder with a block-pattern registration file and list of files with content source and required pattern header within return array() function.

Section 2: Creating and loading patterns without registration

Please note that this feature requires the installation of WordPress 6.0 or Gutenberg plugin 13.0 or above in your site.

This new WordPress 6.0 feature allows pattern registration via standard files and folders – /patterns, bringing similar conventions like /parts, /templates, and /styles.

The process, as also described in this WP Tavern article, involves the following three steps:

  • creating a patterns folder at the theme’s root
  • adding plugin style pattern header
  • pattern source content

A typical pattern header markup, taken from the article is shown below:

<?php /** * Title: A Pattern Title * Slug: namespace/slug * Description: A human-friendly description. * Viewport Width: 1024 * Categories: comma, separated, values * Keywords: comma, separated, values * Block Types: comma, separated, values * Inserter: yes|no */ ?>

As described in the previous section, only Title and Slug fields are required and other fields are optional.

Referencing examples from recently released themes, I refactored pattern registration in this TT2 Gopher Blocks demo theme, prepared for a previous article on the CSS-Tricks.

In the following steps, let’s explore how a footer-with-background.php pattern registered with PHP and used in a footer.html template is refactored.

2.1: Create a /patterns folder at the root of the theme

The first step is to create a /patterns folder at TT2 Gopher theme’s root and move the footer-with-background.php pattern file to /patterns folder and refactor.

2.2: Add pattern data to the file header

Next, create the following pattern header registration fields.

<?php /** * Title: Footer with background * Slug: tt2gopher/footer-with-background * Categories: tt2gopher-footer * Viewport Width: 1280 * Block Types: core/parts/footer * Inserter: yes */ ?> <!-- some-block-content /-->

A pattern file has a top title field written as PHP comments. Followed by the block-content written in HTML format.

2.3: Add Pattern Content to the file

For the content section, let’s copy the code snippets within single quotes (e.g., '...') from the content section of the footer-with-background and replace the <!-- some-block-content /-->:

<!-- wp:group {"align":"full","style":{"elements":{"link":{"color":{"text":"var:preset|color|foreground"}}},"spacing":{"padding":{"top":"35px","bottom":"30px"}}},"backgroundColor":"background-header","textColor":"foreground","className":"has-foreground","layout":{"inherit":true}} --> <div class="wp-block-group alignfull has-foreground has-foreground-color has-background-header-background-color has-text-color has-background has-link-color" style="padding-top:35px;padding-bottom:30px"><!-- wp:paragraph {"align":"center","fontSize":"small"} --> <p class="has-text-align-center has-small-font-size">Powered by WordPress | TT2 Gopher, a modified TT2 theme</p> <!-- /wp:paragraph --></div> <!-- /wp:group -->

The entire code snippet of the patterns/footer-with-background.php file can be viewed here on the GitHub.

Please note that the /inc/patterns and block-patterns.php are extras, not required in WordPress 6.0, and included only for demo purposes.

2.4: Referencing the patterns slug in the template

Adding the above refactored footer-with-background.php pattern to footer.html template is exactly the same as described in the previous section (Section 1, 2.2).

Now, if we view the site’s footer part in a block editor or front-end of our site in a browser, the footer section is displayed.

Pattern categories and types Registration (optional)

To categorize block patterns, the pattern categories and types should be registered in theme’s functions.php file.

Let’s consider an example of registering block pattern categories from the TT2 Gopher theme.

After the registration, the patterns are displayed in the pattern inserter together with the core default patterns. To add theme specific category labels in the patterns inserter, we should modify the previous snippets by adding theme namespace:

/** * Registers block categories, and type. */ function tt2gopher_register_block_pattern_categories() { $block_pattern_categories = array( 'tt2gopher-header' => array( 'label' => __( 'TT2 Gopher - Headers', 'tt2gopher' ) ), 'tt2gopher-footer' => array( 'label' => __( 'TT2 Gopher - Footers', 'tt2gopher' ) ), 'tt2gopher-page' => array( 'label' => __( 'TT2 Gopher - Page', 'tt2gopher' ) ), // ... ); /** * Filters the theme block pattern categories. */ $block_pattern_categories = apply_filters( 'tt2gopher_block_pattern_categories', $block_pattern_categories ); foreach ( $block_pattern_categories as $name => $properties ) { if ( ! WP_Block_Pattern_Categories_Registry::get_instance()->is_registered( $name ) ) { register_block_pattern_category( $name, $properties ); } } add_action( 'init', 'tt2gopher_register_block_pattern_categories', 9 );

The footer-with-background pattern is visible in the patterns inserted with its preview (if selected):

This process greatly simplifies creating and displaying block patterns in block themes. It is available in WordPress 6.0 without using the Gutenberg plugin.

Examples of themes without patterns registration

Early adopters have already started using this feature in their block themes. A few examples of the themes, that are available from the theme directory, that load patterns without registration are listed below:

Section 3: Creating and using patterns with low-code

The official patterns directory contains community-contributed creative designs, which can be copied and customized as desired to create content. Using patterns with a block editor has never been so easier!

Any patterns from the ever-growing directory can also be added to block themes just by simple “copy and paste” or include in the theme.json file by referring to their directory pattern slug. Next, I will go through briefly how easily this can be accomplished with very limited coding.

Adding and customizing patterns from patterns directory

3.1: Copy pattern from directory into a page

Here, I am using this footer section pattern by FirstWebGeek from the patterns directory. Copied the pattern by selecting the “Copy Pattern” button and directly pasted it in a new page.

3.2: Make desired customizations

I made only a few changes to the color of the fonts and button background. Then copied the entire code from the code editor over to a clipboard.

If you are not familiar with using the code editor, go to options (with three dots, top right), click the Code editor button, and copy the entire code from here.

3.3: Create a new file in /patterns folder

First, let’s create a new /patterns/footer-pattern-test.php file and add the required pattern header section. Then paste the entire code (step 3, above). The pattern is categorized in the footer area (lines: 5), we can view the newly added in the pattern inserter.

<?php /** * Title: Footer pattern from patterns library * Slug: tt2gopher/footer-pattern-test * Categories: tt2gopher-footer * Viewport Width: 1280 * Block Types: core/template-part/footer * Inserter: yes */ ?> <!-- wp:group {"align":"full","style":{"spacing":{"padding":{"top":"100px","bottom":"70px","right":"30px","left":"30px"}}},"backgroundColor":"black","layout":{"contentSize":"1280px"}} --> <div class="wp-block-group alignfull has-black-background-color has-background" style="padding-top:100px;padding-right:30px;padding-bottom:70px;padding-left:30px"><!-- wp:columns --> <div class="wp-block-columns"><!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"style":{"typography":{"fontStyle":"normal","fontWeight":"700","textTransform":"uppercase"}},"textColor":"cyan-bluish-gray"} --> <h2 class="has-cyan-bluish-gray-color has-text-color" style="font-style:normal;font-weight:700;text-transform:uppercase">lorem</h2> <!-- /wp:heading --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px">One of the main benefits of using Lorem Ipsum is that it can be easily generated, and it takes the pressure off designers to create meaningful text. Instead, they can focus on crafting the best website data.</p> <!-- /wp:paragraph --> <!-- wp:social-links {"iconColor":"vivid-cyan-blue","iconColorValue":"#0693e3","openInNewTab":true,"className":"is-style-logos-only","style":{"spacing":{"blockGap":{"top":"15px","left":"15px"}}}} --> <ul class="wp-block-social-links has-icon-color is-style-logos-only"><!-- wp:social-link {"url":"#","service":"facebook"} /--> <!-- wp:social-link {"url":"#","service":"twitter"} /--> <!-- wp:social-link {"url":"#","service":"instagram"} /--> <!-- wp:social-link {"url":"#","service":"linkedin"} /--></ul> <!-- /wp:social-links --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"level":4,"style":{"typography":{"textTransform":"capitalize","fontStyle":"normal","fontWeight":"700","fontSize":"30px"}},"textColor":"cyan-bluish-gray"} --> <h4 class="has-cyan-bluish-gray-color has-text-color" style="font-size:30px;font-style:normal;font-weight:700;text-transform:capitalize">Contact Us</h4> <!-- /wp:heading --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px","lineHeight":"1.2"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px;line-height:1.2">123 BD Lorem, Ipsum<br><br>+123-456-7890</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px","lineHeight":"1"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px;line-height:1">sample@gmail.com</p> <!-- /wp:paragraph --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px","lineHeight":"1"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px;line-height:1">Opening Hours: 10:00 - 18:00</p> <!-- /wp:paragraph --></div> <!-- /wp:column --> <!-- wp:column --> <div class="wp-block-column"><!-- wp:heading {"level":4,"style":{"typography":{"fontSize":"30px","fontStyle":"normal","fontWeight":"700","textTransform":"capitalize"}},"textColor":"cyan-bluish-gray"} --> <h4 class="has-cyan-bluish-gray-color has-text-color" style="font-size:30px;font-style:normal;font-weight:700;text-transform:capitalize">Newsletter</h4> <!-- /wp:heading --> <!-- wp:paragraph {"style":{"typography":{"fontSize":"16px"}},"textColor":"cyan-bluish-gray"} --> <p class="has-cyan-bluish-gray-color has-text-color" style="font-size:16px">Lorem ipsum dolor sit amet, consectetur ut labore et dolore magna aliqua ipsum dolor sit</p> <!-- /wp:paragraph --> <!-- wp:search {"label":"","placeholder":"Enter Your Email...","buttonText":"Subscribe","buttonPosition":"button-inside","style":{"border":{"width":"1px"}},"borderColor":"tertiary","backgroundColor":"background-header","textColor":"background"} /--></div> <!-- /wp:column --></div> <!-- /wp:columns --></div> <!-- /wp:group -->

3.4: View the new pattern in the inserter

To view the newly added Footer pattern from patterns library pattern, go to any post or page and select the inserter icon (blue plus symbol, top left), and then select “TT2 Gopher – Footer” categories. The newly added pattern is shown on the left panel, together with other footer patterns and its preview on the right (if selected):

Registering patterns directly in theme.json file

In WordPress 6.0, it is possible to register any desired patterns from the pattern directory with theme.json file with the following syntax. The 6.0 dev note states, “the patterns field is an array of [pattern slugs] from the Pattern Directory. Pattern slugs can be extracted by the [URL] in single pattern view at the Pattern Directory.”

{ "version": 2, "patterns": ["short-text", "patterns-slug"] }

This short WordPress 6.0 features video demonstrates how patterns are registered in the /patterns folder (at 3:53) and registering the desired patterns from the pattern director in a theme.json file (at 3:13).

Then, the registered pattern is available in the patterns inserter search box, which is then available for use just like theme-bundled patterns library.

{ "version": 2, "patterns": [ "footer-from-directory", "footer-section-design-with-3-column-description-social-media-contact-and-newsletter" ] }

In this example, the pattern slug footer-section-design-with-3-column-description-social-media-contact-and-newsletter from the earlier example is registered via theme.json.

Page creation pattern model

As part of “building with patterns” initiatives, WordPress 6.0 offers a pattern modal option to theme authors to add page layout patterns into block theme, allowing site users to select page layout patterns (e.g., an about page, a contact page, a team page, etc.) while creating a page. The following is an example taken from the dev note:

register_block_pattern( 'my-plugin/about-page', array( 'title' => __( 'About page', 'my-plugin' ), 'blockTypes' => array( 'core/post-content' ), 'content' => '<!-- wp:paragraph {"backgroundColor":"black","textColor":"white"} --> <p class="has-white-color has-black-background-color has-text-color has-background">Write you about page here, feel free to use any block</p> <!-- /wp:paragraph -->', ) );

This feature is currently limited to Page Post Type only and not for “Posts Post Type”, yet.

The page creation pattern modal can also be disabled completely by removing the post-content block type of all the patterns. An example sample code is available here.

You can follow and participate in GitHub’s discussion from the links listed under the resource section below.

Using patterns directory to build page

Patterns from the directory can also be used to create the desired post or page layout, similar to page builders. The GutenbergHub team has created an experimental online page builder app using patterns directly from the directory (introductory video). Then the codes from the app can be copied and pasted in a site, which greatly simplifies the building complex page layout process without coding.

In this short video, Jamie Marsland demonstrates (at 1:30) how the app can be used to create an entire page layout similar to page builder using desired page sections of the directory.

Wrapping up

Patterns allow users to recreate their commonly used content layout (e.g., hero page, call out, etc.) in any page and lower the barriers to presenting content in styles, which were previously not possible without coding skills. Just like the plugins and themes directories, the new patterns directory offers users options to use a wide range of patterns of their choices from the pattern directory, and write and display content in style.

Indeed, block patterns will change everything and surely this is a game changer feature in the WordPress theme landscape. When the full potential of building with patterns effort becomes available, this is going to change the way we design block themes and create beautiful content even with low-code knowledge. For many creative designers, the patterns directory may also provide an appropriate avenue to showcase their creativity.

Resources WordPress 6.0 Creating patterns Patterns enhancement (GitHub) Blog articles

How to Create Block Theme Patterns in WordPress 6.0 originally published on CSS-Tricks. You should get the newsletter.

SPAs, Shared Element Transitions, and Re-Evaluating Technology

Css Tricks - Wed, 06/01/2022 - 4:12am

Nolan Lawson sparked some discussion when he described a noticeable shift away from single-page applications (SPAs):

Hip new frameworks like Astro, Qwik, and Elder.js are touting their MPA [multi-page application] with “0kB JavaScript by default.” Blog posts are making the rounds listing all the challenges with SPAs: history, focus management, scroll restoration, Cmd/Ctrl-click, memory leaks, etc. Gleeful potshots are being taken against SPAs.

I think what’s less discussed, though, is how the context has changed in recent years to give MPAs more of an upper hand against SPAs.

It seems a number of folks really clung to that first part because Nolan published a follow-up to clarify that SPAs are far from doomed:

[T]he point of my post wasn’t to bury SPAs and dance on their grave. I think SPAs are great, I’ve worked on many of them, and I think they have a bright future ahead of them. My main point was: if the only reason you’re using an SPA is because “it makes navigations faster,” then maybe it’s time to re-evaluate that.

And there’s good reason he says that. In fact, the first article specifically points to work being done on Shared Element Transitions. If they move forward, we’ll have an API for animating/transitioning/sizing/positioning elements on page entrance and exist. Jake Archibald demonstrated how it works at Google I/O 2022 and the video is a gem.

If you’re wondering how one page can transition into another, the browser takes screenshots of the outgoing page and the incoming page, then transitions between those. So, it’s not so much one page becoming another as much as it is the browser holding onto two images so it can animate one in while the other animates out. Jake says what’s happening behind the scene is a DOM structure is created out of pseudo-elements containing the page images:

<transition-container> <image-wrapper> <outgoing-image /> <incoming-image /> </> </>

We can “screenshot” a specific element if we want to isolate it and apply a different animation from the rest of the page:

.site-header { page-transition-tag: site-header; contain: paint; }

And we get pseudo-elements we can hook into and assign custom @keyframe animations:

<!-- ::page-transition=container(root) --> <transition-container> <!-- ::page-transition-image-wrapper(root) --> <image-wrapper> <!-- ::page-transition-outgoing-image(root) --> <outgoing-image /> <!-- ::page-transition-incoming-image(root) --> <incoming-image /> </> </>

Dang, that’s clever as heck!

It’s also proof in the pudding of just how much HTML, CSS, and JavaScript continue to evolve and improve. So much so that Jeremy Keith suggests it’s high time we re-evaluate our past judgment of some technologies:

If you weren’t aware of changes over the past few years, it would be easy to still think that single page apps offer some unique advantages that in fact no longer hold true. […] But developers remain suspicious, still prefering to trust third-party libraries over native browser features. They made a decision about those libraries in the past. They evaluated the state of browser support in the past. I wish they would re-evaluate those decisions.

The ingredients for SPAs specifically:

In recent years in particular it feels like the web has come on in leaps and bounds: service workers, native JavaScript APIs, and an astonishing boost in what you can do with CSS. Most important of all, the interoperability between browsers is getting better and better. Universal support for new web standards arrives at a faster rate than ever before.

HTML, CSS, and JavaScript: it’s still the best cocktail in town. Even if it takes a minute for it to catch up.

SPAs, Shared Element Transitions, and Re-Evaluating Technology originally published on CSS-Tricks. You should get the newsletter.

Just How Long Should Alt Text Be?

Css Tricks - Fri, 05/27/2022 - 4:25am

I teach a class over at the local college here in Long Beach and a majority of the content is hosted on the Canvas LMS so students can access it online. And, naturally, I want the content to be as accessible as possible, so thank goodness Canvas has a11y tooling built right into it.

But it ain’t all that rosy. It makes assumptions like all other a11y tooling and adheres to guidelines that were programmed into it. It’s not like the WCAG is baked right in and updated when it updates.

The reason this is even on my mind is that Jeremy yesterday described his love for writing image descriptions:

I enjoy writing alt text. I recently described how I updated my posting interface here on my own site to put a textarea for alt text front and centre for my notes with photos. Since then I’ve been enjoying the creative challenge of writing useful—but also evocative—alt text.

I buy into that! Writing alt text is a challenge that requires a delicate dance between the technical and the creative. It’s both an opportunity to make content more accessible and enhance the user experience.

One of those programmed guidelines in the Canvas tool is a cap of 120 characters on alt text. Why 120? I dunno, I couldn’t find any supporting guideline or rule for that exact number. One answer is that screen readers stop announcing text after 125 characters, but that’s apparently untrue, at least today. The general advice for how long alt text should be comes in varying degrees:

  • Jake Archibald talks of length in terms of emotion. Detail is great, but too much detail might distort the focal point, which makes total sense.
  • Dave sees them as short, succinct paragraphs.
  • Carrie Fisher suggests a 150-character limit not because screen readers will truncate them but more as a mental note that maybe things are getting too descriptive.
  • Daniel Göransson says in this 2017 guide that it comes down to context and knowing when certain details of an image are worth additional explanation. But he generally errs on the side of conciseness.

So, how long should alt text be? The general consensus here is that there is no hard limit, but more of a contextual awareness of what purpose the image serves and adapting to it accordingly.

Which gets me back to Jeremy’s article. He was writing alt text for a group of speaker headshots and realized the text was all starting to sound the same. He paused, thought about the experience, compared it to the experience of a sighted user, and created parity between them:

The more speakers were added to the line-up, the more I felt like I was repeating myself with the alt text. […] The experience of a sighted person looking at a page full of speakers is that after a while the images kind of blend together. So if the alt text also starts to sound a bit repetitive after a while, maybe that’s not such a bad thing. A screen reader user would be getting an equivalent experience.

I dig that. So if you’re looking for a hard and fast rule on character counts, sorry to disappoint. Like so many other things, context is king and that’s the sort of thing that can’t be codified, or even automated for that matter.

And while we’re on the topic, just noticed that Twitter has UI to display alt text:

Now if only there was more contrast between that text and the background… a11y is hard.

Just How Long Should Alt Text Be? originally published on CSS-Tricks. You should get the newsletter.

Beautify GitHub Profile

Css Tricks - Thu, 05/26/2022 - 8:13am

It wasn’t long ago that Nick Sypteras showed us how to make custom badges for a GitHub repo. Well, Reza Shakeri put Beautify GitHub Profile together and it’s a huuuuuuge repo of different badges that pulls lots of examples together with direct links to the repos you can use to create them.

And it doesn’t stop there! If you’re looking for some sort of embeddable widget, there’s everything from GitHub repo stats and contribution visualizations, all the way to embedded PageSpeed Insights and Spotify playlists. Basically, a big ol’ spot to get some inspiration.

Some things are simply wild!

I bet Jhey would like to get his hands on those cuboids!

Just scrolling through the repo gives me flashes of the GeoCities days, though. All it needs is a sparkly unicorn and a tiled background image to complete the outfit. &#x1f454;

To Shared LinkPermalink on CSS-Tricks

Beautify GitHub Profile originally published on CSS-Tricks. You should get the newsletter.

Cool CSS Hover Effects That Use Background Clipping, Masks, and 3D

Css Tricks - Thu, 05/26/2022 - 4:13am

We’ve walked through a series of posts now about interesting approaches to CSS hover effects. We started with a bunch of examples that use CSS background properties, then moved on to the text-shadow property where we technically didn’t use any shadows. We also combined them with CSS variables and calc() to optimize the code and make it easy to manage.

In this article, we will build off those two articles to create even more complex CSS hover animations. We’re talking about background clipping, CSS masks, and even getting our feet wet with 3D perspectives. In other words, we are going to explore advanced techniques this time around and push the limits of what CSS can do with hover effects!

Cool Hover Effects series:
  1. Cool Hover Effects That Use Background Properties
  2. Cool Hover Effects That Use CSS Text Shadow
  3. Cool Hover Effects That Use Background Clipping, Masks, and 3D (you are here!)

Here’s just a taste of what we’re making:

CodePen Embed Fallback Hover effects using background-clip

Let’s talk about background-clip. This CSS property accepts a text keyword value that allows us to apply gradients to the text of an element instead of the actual background.

So, for example, we can change the color of the text on hover as we would using the color property, but this way we animate the color change:

CodePen Embed Fallback

All I did was add background-clip: text to the element and transition the background-position. Doesn’t have to be more complicated than that!

But we can do better if we combine multiple gradients with different background clipping values.

CodePen Embed Fallback

In that example, I use two different gradients and two values with background-clip. The first background gradient is clipped to the text (thanks to the text value) to set the color on hover, while the second background gradient creates the bottom underline (thanks to the padding-box value). Everything else is straight up copied from the work we did in the first article of this series.

How about a hover effect where the bar slides from top to bottom in a way that looks like the text is scanned, then colored in:

CodePen Embed Fallback

This time I changed the size of the first gradient to create the line. Then I slide it with the other gradient that update the text color to create the illusion! You can visualize what’s happening in this pen:

CodePen Embed Fallback

We’ve only scratched the surface of what we can do with our background-clipping powers! However, this technique is likely something you’d want to avoid using in production, as Firefox is known to have a lot of reported bugs related to background-clip. Safari has support issues as well. That leaves only Chrome with solid support for this stuff, so maybe have it open as we continue.

Let’s move on to another hover effect using background-clip:

CodePen Embed Fallback

You’re probably thinking this one looks super easy compared to what we’ve just covered — and you are right, there’s nothing fancy here. All I am doing is sliding one gradient while increasing the size of another one.

But we’re here to look at advanced hover effects, right? Let’s change it up a bit so the animation is different when the mouse cursor leaves the element. Same hover effect, but a different ending to the animation:

CodePen Embed Fallback

Cool right? let’s dissect the code:

.hover { --c: #1095c1; /* the color */ color: #0000; background: linear-gradient(90deg, #fff 50%, var(--c) 0) calc(100% - var(--_p, 0%)) / 200%, linear-gradient(var(--c) 0 0) 0% 100% / var(--_p, 0%) no-repeat, var(--_c, #0000); -webkit-background-clip: text, padding-box, padding-box; background-clip: text, padding-box, padding-box; transition: 0s, color .5s, background-color .5s; } .hover:hover { color: #fff; --_c: var(--c); --_p: 100%; transition: 0.5s, color 0s .5s, background-color 0s .5s; }

We have three background layers — two gradients and the background-color defined using --_c variable which is initially set to transparent (#0000). On hover, we change the color to white and the --_c variable to the main color (--c).

Here’s what is happening on that transition: First, we apply a transition to everything but we delay the color and background-color by 0.5s to create the sliding effect. Right after that, we change the color and the background-color. You might notice no visual changes because the text is already white (thanks to the first gradient) and the background is already set to the main color (thanks to the second gradient).

Then, on mouse out, we apply an instant change to everything (notice the 0s delay), except for the color and background-color that have a transition. This means that we put all the gradients back to their initial states. Again, you will probably see no visual changes because the text color and background-color already changed on hover.

Lastly, we apply the fading to color and a background-color to create the mouse-out part of the animation. I know, it may be tricky to grasp but you can better visualize the trick by using different colors:

CodePen Embed Fallback

Hover the above a lot of times and you will see the properties that are animating on hover and the ones animating on mouse out. You can then understand how we reached two different animations for the same hover effect.

Let’s not forget the DRY switching technique we used in the previous articles of this series to help reduce the amount of code by using only one variable for the switch:

.hover { --c: 16 149 193; /* the color using the RGB format */ color: rgb(255 255 255 / var(--_i, 0)); background: /* Gradient #1 */ linear-gradient(90deg, #fff 50%, rgb(var(--c)) 0) calc(100% - var(--_i, 0) * 100%) / 200%, /* Gradient #2 */ linear-gradient(rgb(var(--c)) 0 0) 0% 100% / calc(var(--_i, 0) * 100%) no-repeat, /* Background Color */ rgb(var(--c)/ var(--_i, 0)); -webkit-background-clip: text, padding-box, padding-box; background-clip: text, padding-box, padding-box; --_t: calc(var(--_i,0)*.5s); transition: var(--_t), color calc(.5s - var(--_t)) var(--_t), background-color calc(.5s - var(--_t)) var(--_t); } .hover:hover { --_i: 1; }

If you’re wondering why I reached for the RGB syntax for the main color, it’s because I needed to play with the alpha transparency. I am also using the variable --_t to reduce a redundant calculation used in the transition property.

Before we move to the next part here are more examples of hover effects I did a while ago that rely on background-clip. It would be too long to detail each one but with what we have learned so far you can easily understand the code. It can be a good inspiration to try some of them alone without looking at the code.

CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback

I know, I know. These are crazy and uncommon hover effects and I realize they are too much in most situations. But this is how to practice and learn CSS. Remember, we pushing the limits of CSS hover effects. The hover effect may be a novelty, but we’re learning new techniques along the way that can most certainly be used for other things.

Hover effects using CSS mask

Guess what? The CSS mask property uses gradients the same way the background property does, so you will see that what we’re making next is pretty straightforward.

Let’s start by building a fancy underline.

CodePen Embed Fallback

I’m using background to create a zig-zag bottom border in that demo. If I wanted to apply an animation to that underline, it would be tedious to do it using background properties alone.

Enter CSS mask.

CodePen Embed Fallback

The code may look strange but the logic is still the same as we did with all the previous background animations. The mask is composed of two gradients. The first gradient is defined with an opaque color that covers the content area (thanks to the content-box value). That first gradient makes the text visible and hides the bottom zig-zag border. content-box is the mask-clip value which behaves the same as background-clip

linear-gradient(#000 0 0) content-box

The second gradient will cover the whole area (thanks to padding-box). This one has a width that’s defined using the --_p variable, and it will be placed on the left side of the element.

linear-gradient(#000 0 0) 0 / var(--_p, 0%) padding-box

Now, all we have to do is to change the value of --_p on hover to create a sliding effect for the second gradient and reveal the underline.

.hover:hover { --_p: 100%; color: var(--c); }

The following demo uses with the mask layers as backgrounds to better see the trick taking place. Imagine that the green and red parts are the visible parts of the element while everything else is transparent. That’s what the mask will do if we use the same gradients with it.

CodePen Embed Fallback

With such a trick, we can easily create a lot of variation by simply using a different gradient configuration with the mask property:

CodePen Embed Fallback

Each example in that demo uses a slightly different gradient configuration for the mask. Notice, too, the separation in the code between the background configuration and the mask configuration. They can be managed and maintained independently.

Let’s change the background configuration by replacing the zig-zag underline with a wavy underline instead:

CodePen Embed Fallback

Another collection of hover effects! I kept all the mask configurations and changed the background to create a different shape. Now, you can understand how I was able to reach 400 hover effects without pseudo-elements — and we can still have more!

Like, why not something like this:

CodePen Embed Fallback

Here’s a challenge for you: The border in that last demo is a gradient using the mask property to reveal it. Can you figure out the logic behind the animation? It may look complex at first glance, but it’s super similar to the logic we’ve looked at for most of the other hover effects that rely on gradients. Post your explanation in the comments!

Hover effects in 3D

You may think it’s impossible to create a 3D effect with a single element (and without resorting to pseudo-elements!) but CSS has a way to make it happen.

CodePen Embed Fallback

What you’re seeing there isn’t a real 3D effect, but rather a perfect illusion of 3D in the 2D space that combines the CSS background, clip-path, and transform properties.

The trick may look like we’re interacting with a 3D element, but we’re merely using 2D tactics to draw a 3D box

The first thing we do is to define our variables:

--c: #1095c1; /* color */ --b: .1em; /* border length */ --d: 20px; /* cube depth */

Then we create a transparent border with widths that use the above variables:

--_s: calc(var(--d) + var(--b)); color: var(--c); border: solid #0000; /* fourth value sets the color's alpha */ border-width: var(--b) var(--b) var(--_s) var(--_s);

The top and right sides of the element both need to equal the --b value while the bottom and left sides need to equal to the sum of --b and --d (which is the --_s variable).

For the second part of the trick, we need to define one gradient that covers all the border areas we previously defined. A conic-gradient will work for that:

background: conic-gradient( at left var(--_s) bottom var(--_s), #0000 90deg,var(--c) 0 ) 0 100% / calc(100% - var(--b)) calc(100% - var(--b)) border-box;

We add another gradient for the third part of the trick. This one will use two semi-transparent white color values that overlap the first previous gradient to create different shades of the main color, giving us the illusion of shading and depth.

conic-gradient( at left var(--d) bottom var(--d), #0000 90deg, rgb(255 255 255 / 0.3) 0 225deg, rgb(255 255 255 / 0.6) 0 ) border-box

The last step is to apply a CSS clip-path to cut the corners for that long shadow sorta feel:

clip-path: polygon( 0% var(--d), var(--d) 0%, 100% 0%, 100% calc(100% - var(--d)), calc(100% - var(--d)) 100%, 0% 100% )

That’s all! We just made a 3D rectangle with nothing but two gradients and a clip-path that we can easily adjust using CSS variables. Now, all we have to do is to animate it!

Notice the coordinates from the previous figure (indicated in red). Let’s update those to create the animation:

clip-path: polygon( 0% var(--d), /* reverses var(--d) 0% */ var(--d) 0%, 100% 0%, 100% calc(100% - var(--d)), calc(100% - var(--d)) 100%, /* reverses 100% calc(100% - var(--d)) */ 0% 100% /* reverses var(--d) calc(100% - var(--d)) */ )

The trick is to hide the bottom and left parts of the element so all that’s left is a rectangular element with no depth whatsoever.

This pen isolates the clip-path portion of the animation to see what it’s doing:

CodePen Embed Fallback

The final touch is to move the element in the opposite direction using translate — and the illusion is perfect! Here’s the effect using different custom property values for varying depths:

CodePen Embed Fallback

The second hover effect follows the same structure. All I did is to update a few values to create a top left movement instead of a top right one.

Combining effects!

The awesome thing about everything we’ve covered is that they all complement each other. Here is an example where I am adding the text-shadow effect from the second article in the series to the background animation technique from the first article while using the 3D trick we just covered:

CodePen Embed Fallback

The actual code might be confusing at first, but go ahead and dissect it a little further — you’ll notice that it’s merely a combination of those three different effects, pretty much smushed together.

Let me finish this article with a last hover effect where I am combining background, clip-path, and a dash of perspective to simulate another 3D effect:

CodePen Embed Fallback

I applied the same effect to images and the result was quite good for simulating 3D with a single element:

CodePen Embed Fallback

Want a closer look at how that last demo works? I wrote something up on it.

Wrapping up

Oof, we are done! I know, it’s a lot of tricky CSS but (1) we’re on the right website for that kind of thing, and (2) the goal is to push our understanding of different CSS properties to new levels by allowing them to interact with one another.

You may be asking what the next step is from here now that we’re closing out this little series of advanced CSS hover effects. I’d say the next step is to take all that we learned and apply them to other elements, like buttons, menu items, links, etc. We kept things rather simple as far as limiting our tricks to a heading element for that exact reason; the actual element doesn’t matter. Take the concepts and run with them to create, experiment with, and learn new things!

Cool CSS Hover Effects That Use Background Clipping, Masks, and 3D originally published on CSS-Tricks. You should get the newsletter.

Customizing Color Fonts on the Web

Css Tricks - Wed, 05/25/2022 - 2:15pm

Myles C. Maxfield on the WebKit Blog published a nifty how-to for color fonts. It comes on the heels of what Ollie wrote up here on CSS-Tricks the other day, and while they cover a lot of common ground, there’s some nice nuggets in the WebKit post that make them both worth reading.

Case in point: there’s a little progressive enhancement in there using @supports for older browsers lacking support the font-palette property. Then the post gets into a strategy that shows the property’s light and dark values at play to make the font more legible in certain contexts. There’s also a clever idea about how creating multiple @font-palette-values blocks with the same name can be used for fallbacks.

To Shared LinkPermalink on CSS-Tricks

Customizing Color Fonts on the Web originally published on CSS-Tricks. You should get the newsletter.

A Perfect Table of Contents With HTML + CSS

Css Tricks - Wed, 05/25/2022 - 4:08am

Earlier this year, I self-published an ebook called Understanding JavaScript Promises (free for download). Even though I didn’t have any intention of turning it into a print book, enough people reached out inquiring about a print version that I decided to self-publish that as well .I thought it would be an easy exercise using HTML and CSS to generate a PDF and then send it off to the printer. What I didn’t realize was that I didn’t have an answer to an important part of a print book: the table of contents.

The makeup of a table of contents

At its core, a table of contents is fairly simple. Each line represents a part of a book or webpage and indicates where you can find that content. Typically, the lines contain three parts:

  1. The title of the chapter or section
  2. Leaders (i.e. those dots, dashes, or lines) that visually connect the title to the page number
  3. The page number

A table of contents is easy to generate inside of word processing tools like Microsoft Word or Google Docs, but because my content was in Markdown and then transformed into HTML, that wasn’t a good option for me. I wanted something automated that would work with HTML to generate the table of contents in a format that was suitable for print. I also wanted each line to be a link so it could be used in webpages and PDFs to navigate around the document. I also wanted dot leaders between the title and page number.

And so I began researching.

I came across two excellent blog posts on creating a table of contents with HTML and CSS. The first was “Build a Table of Contents from your HTML” by Julie Blanc. Julie worked on PagedJS, a polyfill for missing paged media features in web browsers that properly formats documents for print. I started with Julie’s example, but found that it didn’t quite work for me. Next, I found Christoph Grabo’s “Responsive TOC leader lines with CSS” post, which introduced the concept of using CSS Grid (as opposed to Julie’s float-based approach) to make alignment easier. Once again, though, his approach wasn’t quite right for my purposes.

After reading these two posts, though, I felt I had a good enough understanding of the layout issues to embark on my own. I used pieces from both blog posts as well as adding some new HTML and CSS concepts into the approach to come up with a result I’m happy with.

Choosing the correct markup

When deciding on the correct markup for a table of contents, I thought primarily about the correct semantics. Fundamentally, a table of contents is about a title (chapter or subsection) being tied to a page number, almost like a key-value pair. That led me to two options:

  • One option is to use a table (<table>) with one column for the title and one column for the page.
  • Then there’s the often unused and forgotten definition list (<dl>) element. It also acts as a key-value map. So, once again, the relationship between the title and the page number would be obvious.

Either of these seemed like good options until I realized that they really only work for single-level tables of contents, namely, only if I wanted to have a table of contents with just chapter names. If I wanted to show subsections in the table of contents, though, I didn’t have any good options. Table elements aren’t great for hierarchical data, and while definition lists can technically be nested, the semantics didn’t seem correct. So, I went back to the drawing board.

I decided to build off of Julie’s approach and use a list; however, I opted for an ordered list (<ol>) instead of an unordered list (<ul>). I think an ordered list is more appropriate in this case. A table of contents represents a list of chapters and subheadings in the order in which they appear in the content. The order matters and shouldn’t get lost in the markup.

Unfortunately, using an ordered list means losing the semantic relationship between the title and the page number, so my next step was to re-establish that relationship within each list item. The easiest way to solve this is to simply insert the word “page” before the page number. That way, the relationship of the number relative to the text is clear, even without any other visual distinction.

Here’s a simple HTML skeleton that formed the basis of my markup:

<ol class="toc-list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol> <!-- subsection items --> </ol> </li> </ol> Applying styles to the table of contents

Once I had established the markup I planned to use, the next step was to apply some styles.

First, I removed the autogenerated numbers. You can choose to keep the autogenerated numbers in your own project if you’d like, but it’s common for books to have unnumbered forewords and afterwords included in the list of chapters, which makes the autogenerated numbers incorrect.

For my purpose, I would fill in the chapter numbers manually then adjust the layout so the top-level list doesn’t have any padding (thus aligning it with paragraphs) and each embedded list is indented by two spaces. I chose to use a 2ch padding value because I still wasn’t quite sure which font I would use. The ch length unit allows the padding to be relative to the width of a character — no matter what font is used — rather than an absolute pixel size that could wind up looking inconsistent.

Here’s the CSS I ended up with:

.toc-list, .toc-list ol { list-style-type: none; } .toc-list { padding: 0; } .toc-list ol { padding-inline-start: 2ch; }

Sara Soueidan pointed out to me that WebKit browsers remove list semantics when list-style-type is none, so I needed to add role="list" into the HTML to preserve it:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li> </ol> CodePen Embed Fallback Styling the title and page number

With the list styled to my liking, it was time to move on to styling an individual list item. For each item in the table of contents, the title and page number must be on the same line, with the title to the left and the page number aligned to the right.

You might be thinking, “No problem, that’s what flexbox is for!” You aren’t wrong! Flexbox can indeed achieve the correct title-page alignment. But there are some tricky alignment issues when the leaders are added, so I instead opted to go with Christoph’s approach using a grid, which as a bonus as it also helps with multiline titles. Here is the CSS for an individual item:

.toc-list li > a { text-decoration: none; display: grid; grid-template-columns: auto max-content; align-items: end; } .toc-list li > a > .page { text-align: right; }

The grid has two columns, the first of which is auto-sized to fill up the entire width of the container, minus the second column, which is sized to max-content. The page number is aligned to the right, as is traditional in a table of contents.

The only other change I made at this point was to hide the “Page” text. This is helpful for screen readers but unnecessary visually, so I used a traditional visually-hidden class to hide it from view:

.visually-hidden { clip: rect(0 0 0 0); clip-path: inset(100%); height: 1px; overflow: hidden; position: absolute; width: 1px; white-space: nowrap; }

And, of course, the HTML needs to be updated to use that class:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li> </ol>

With this foundation in place, I moved on to address the leaders between the title and the page.

CodePen Embed Fallback Creating dot leaders

Leaders are so common in print media that you might be wondering, why doesn’t CSS already support that? The answer is: it does. Well, kind of.

There is actually a leader() function defined in the CSS Generated Content for Paged Media specification. However, as with much of the paged media specifications, this function isn’t implemented in any browsers, therefore excluding it as an option (at least at the time I’m writing this). It’s not even listed on caniuse.com, presumably because no one has implemented it and there are no plans or signals that they will.

Fortunately, both Julie and Christoph already addressed this problem in their respective posts. To insert the dot leaders, they both used a ::after pseudo-element with its content property set to a very long string of dots, like this:

.toc-list li > a > .title { position: relative; overflow: hidden; } .toc-list li > a .title::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right; }

The ::after pseudo-element is set to an absolute position to take it out of the flow of the page and avoid wrapping to other lines. The text is aligned to the right because we want the last dots of each line flush to the number at the end of the line. (More on the complexities of this later.) The .title element is set to have a relative position so the ::after pseudo-element doesn’t break out of its box. Meanwhile, the overflow is hidden so all those extra dots invisible. The result is a pretty table of contents with dot leaders.

However, there’s something else that needs consideration.

Sara also pointed out to me that all of those dots count as text to screen readers. So what do you hear? “Introduction dot dot dot dot…” until all of the dots are announced. That’s an awful experience for screen reader users.

The solution is to insert an additional element with aria-hidden set to true and then use that element to insert the dots. So the HTML becomes:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title<span class="leaders" aria-hidden="true"></span></span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li> </ol>

And the CSS becomes:

.toc-list li > a > .title { position: relative; overflow: hidden; } .toc-list li > a .leaders::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right; }

Now screen readers will ignore the dots and spare users the frustration of listening to multiple dots being announced.

CodePen Embed Fallback Finishing touches

At this point, the table of contents component looks pretty good, but it could use some minor detail work. To start, most books visually offset chapter titles from subsection titles, so I made the top-level items bold and introduced a margin to separate subsections from the chapters that followed:

.toc-list > li > a { font-weight: bold; margin-block-start: 1em; }

Next, I wanted to clean up the alignment of the page numbers. Everything looked okay when I was using a fixed-width font, but for variable-width fonts, the leader dots could end up forming a zigzag pattern as they adjust to the width of a page number. For instance, any page number with a 1 would be narrower than others, resulting in leader dots that are misaligned with the dots on previous or following lines.

To fix this problem, I set font-variant-numeric to tabular-nums so all numbers are treated with the same width. By also setting the minimum width to 2ch, I ensured that all numbers with one or two digits are perfectly aligned. (You may want to set this to 3ch if your project has more than 100 pages.) Here is the final CSS for the page number:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right; }

And with that, the table of contents is complete!

CodePen Embed Fallback Conclusion

Creating a table of contents with nothing but HTML and CSS was more of a challenge than I expected, but I’m very happy with the result. Not only is this approach flexible enough to accommodate chapters and subsections, but it handles sub-subsections nicely without updating the CSS. The overall approach works on web pages where you want to link to the various locations of content, as well as PDFs where you want the table of contents to link to different pages. And of course, it also looks great in print if you’re ever inclined to use it in a brochure or book.

I’d like to thank Julie Blanc and Christoph Grabo for their excellent blog posts on creating a table of contents, as both of those were invaluable when I was getting started. I’d also like to thank Sara Soueidan for her accessibility feedback as I worked on this project.

A Perfect Table of Contents With HTML + CSS originally published on CSS-Tricks. You should get the newsletter.

First Look At The CSS object-view-box Property

Css Tricks - Mon, 05/23/2022 - 9:15am

Ahmad Shadeed — doing what he always does so well — provides an early look at the object-view-box property, something he describes as a native way to crop an image in the browser with CSS.

The use case? Well, Ahmad wastes no time showing how to use the property to accomplish what used to require either (1) a wrapping element with hidden overflow around an image that’s sized and positioned inside that element or (2) the background-image route.

But with object-view-box we can essentially draw the image boundaries as we can with an SVG’s viewbox. So, take a plain ol’ <img> and call on object-view-box to trim the edges using an inset function. I’ll simply drop Ahmad’s pen in here:

CodePen Embed Fallback

Only supported in Chrome Canary for now, I’m afraid. But it’s (currently) planned to release in Chrome 104. Elsewhere:

To Shared LinkPermalink on CSS-Tricks

First Look At The CSS object-view-box Property originally published on CSS-Tricks. You should get the newsletter.

Dialog Components: Go Native HTML or Roll Your Own?

Css Tricks - Mon, 05/23/2022 - 4:31am

As the author of a library called AgnosticUI, I’m always on the lookout for new components. And recently, I decided to dig in and start work on a new dialog (aka modal) component. That’s something many devs like to have in their toolset and my goal was to make the best one possible, with an extra special focus on making it inclusive and accessible.

My first thought was that I would avoid any dependencies and bite the bullet to build my own dialog component. As you may know, there’s a new <dialog> element making the rounds and I figured using it as a starting point would be the right thing, especially in the inclusiveness and accessibilities departments.

But, after doing some research, I instead elected to leverage a11y-dialog by Kitty Giraudel. I even wrote adapters so it integrates smoothly with Vue 3, Svelte, and Angular. Kitty has long offered a React adapter as well.

Why did I go that route? Let me take you through my thought process.

First question: Should I even use the native <dialog> element?

The native <dialog> element is being actively improved and will likely be the way forward. But, it still has some issues at the moment that Kitty pointed out quite well:

  1. Clicking the backdrop overlay does not close the dialog by default
  2. The alertdialog ARIA role used for alerts simply does not work with the native <dialog> element. We’re supposed to use that role when a dialog requires a user’s response and shouldn’t be closed by clicking the backdrop, or by pressing ESC.
  3. The <dialog> element comes with a ::backdrop pseudo-element but it is only available when a dialog is programmatically opened with dialog.showModal().

And as Kitty also points out, there are general issues with the element’s default styles, like the fact they are left to the browser and will require JavaScript. So, it’s sort of not 100% HTML anyway.

Here’s a pen demonstrating these points:

CodePen Embed Fallback

Now, some of these issues may not affect you or whatever project you’re working on specifically, and you may even be able to work around things. If you still would like to utilize the native dialog you should see Adam Argyle’s wonderful post on building a dialog component with native dialog.

OK, let’s discuss what actually are the requirements for an accessible dialog component…

What I’m looking for

I know there are lots of ideas about what a dialog component should or should not do. But as far as what I was personally going after for AgnosticUI hinged on what I believe make for an accessible dialog experience:

  1. The dialog should close when clicking outside the dialog (on the backdrop) or when pressing the ESC key.
  2. It should trap focus to prevent tabbing out of the component with a keyboard.
  3. It should allow forwarding tabbing with TAB and backward tabbing with SHIFT+TAB.
  4. It should return focus back to the previously focused element when closed.
  5. It should correctly apply aria-* attributes and toggles.
  6. It should provide Portals (only if we’re using it within a JavaScript framework).
  7. It should support the alertdialog ARIA role for alert situations.
  8. It should prevent the underlying body from scrolling, if needed.
  9. It would be great if our implementation could avoid the common pitfalls that come with the native <dialog> element.
  10. It would ideally provide a way to apply custom styling while also taking the prefers-reduced-motion user preference query as a further accessibility measure.

I’m not the only one with a wish list. You might want to see Scott O’Hara’s article on the topic as well as Kitty’s full write-up on creating an accessible dialog from scratch for more in-depth coverage.

It should be clear right about now why I nixed the native <dialog> element from my component library. I believe in the work going into it, of course, but my current needs simply outweigh the costs of it. That’s why I went with Kitty’s a11y-dialog as my starting point.

Auditing <dialog> accessibility

Before trusting any particular dialog implementation, it’s worth making sure it fits the bill as far as your requirements go. With my requirements so heavily leaning on accessibility, that meant auditing a11y-dialog.

Accessibility audits are a profession of their own. And even if it’s not my everyday primary focus, I know there are some things that are worth doing, like:

This is quite a lot of work, as you might imagine (or know from experience). It’s tempting to take a path of less resistance and try automating things but, in a study conducted by Deque Systems, automated tooling can only catch about 57% of accessibility issues. There’s no substitute for good ol’ fashioned hard work.

The auditing environment

The dialog component can be tested in lots of places, including Storybook, CodePen, CodeSandbox, or whatever. For this particular test, though, I prefer instead to make a skeleton page and test locally. This way I’m preventing myself from having to validate the validators, so to speak. Having to use, say, a Storybook-specific add-on for a11y verification is fine if you’re already using Storybook on your own components, but it adds another layer of complexity when testing the accessibility of an external component.

A skeleton page can verify the dialog with manual checks, existing a11y tooling, and screen readers. If you’re following along, you’ll want to run this page via a local server. There are many ways to do that; one is to use a tool called serve, and npm even provides a nice one-liner npx serve <DIRECTORY> command to fire things up.

Let’s do an example audit together!

I’m obviously bullish on a11y-dialog here, so let’s put it to the test and verify it using some of the the recommended approaches we’ve covered.

Again, all I’m doing here is starting with an HTML. You can use the same one I am (complete with styles and scripts baked right in).

View full code <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>A11y Dialog Test</title> <style> .dialog-container { display: flex; position: fixed; top: 0; left: 0; bottom: 0; right: 0; z-index: 2; } .dialog-container[aria-hidden='true'] { display: none; } .dialog-overlay { position: fixed; top: 0; left: 0; bottom: 0; right: 0; background-color: rgb(43 46 56 / 0.9); animation: fade-in 200ms both; } .dialog-content { background-color: rgb(255, 255, 255); margin: auto; z-index: 2; position: relative; animation: fade-in 400ms 200ms both, slide-up 400ms 200ms both; padding: 1em; max-width: 90%; width: 600px; border-radius: 2px; } @media screen and (min-width: 700px) { .dialog-content { padding: 2em; } } @keyframes fade-in { from { opacity: 0; } } @keyframes slide-up { from { transform: translateY(10%); } } /* Note, for brevity we haven't implemented prefers-reduced-motion */ .dialog h1 { margin: 0; font-size: 1.25em; } .dialog-close { position: absolute; top: 0.5em; right: 0.5em; border: 0; padding: 0; background-color: transparent; font-weight: bold; font-size: 1.25em; width: 1.2em; height: 1.2em; text-align: center; cursor: pointer; transition: 0.15s; } @media screen and (min-width: 700px) { .dialog-close { top: 1em; right: 1em; } } * { box-sizing: border-box; } body { font: 125% / 1.5 -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif; padding: 2em 0; } h1 { font-size: 1.6em; line-height: 1.1; font-family: 'ESPI Slab', sans-serif; margin-bottom: 0; } main { max-width: 700px; margin: 0 auto; padding: 0 1em; } </style> <script defer src="https://cdn.jsdelivr.net/npm/a11y-dialog@7/dist/a11y-dialog.min.js"></script> </head> <body> <main> <div class="dialog-container" id="my-dialog" aria-hidden="true" aria-labelledby="my-dialog-title" role="dialog"> <div class="dialog-overlay" data-a11y-dialog-hide></div> <div class="dialog-content" role="document"> <button data-a11y-dialog-hide class="dialog-close" aria-label="Close this dialog window"> × </button> <a href="https://www.yahoo.com/" target="_blank">Rando Yahoo Link</a> <h1 id="my-dialog-title">My Title</h1> <p id="my-dialog-description"> Some description of what's inside this dialog… </p> </div> </div> <button type="button" data-a11y-dialog-show="my-dialog"> Open the dialog </button> </main> <script> // We need to ensure our deferred A11yDialog has // had a chance to do its thing ;-) window.addEventListener('DOMContentLoaded', (event) => { const dialogEl = document.getElementById('my-dialog') const dialog = new A11yDialog(dialogEl) }); </script> </body> </html>

I know, we’re ignoring a bunch of best practices (what, styles in the <head>?!) and combined all of the HTML, CSS, and JavaScript in one file. I won’t go into the details of the code as the focus here is testing for accessibility, but know that this test requires an internet connection as we are importing a11y-dialog from a CDN.

First, the manual checks

I served this one-pager locally and here are my manual check results:

FeatureResultIt should close when clicking outside the dialog (on the backdrop) or when pressing the ESC key.✅It ought to trap focus to prevent tabbing out of the component with a keyboard.✅It should allow forwarding tabbing with TAB and backward tabbing with SHIFT+TAB.✅It should return focus back to the previously focused element when closed.✅It should correctly apply aria-* attributes and toggles.✅
I verified this one “by eye” after inspecting the elements in the DevTools Elements panel.It should provide Portals.Not applicable.
This is only useful when implementing the element with React, Svelte, Vue, etc. We’ve statically placed it on the page with aria-hidden for this test.It should support for the alertdialog ARIA role for alert situations.✅
You’ll need to do two things:

First, remove data-a11y-dialog-hide from the overlay in the HTML so that it is <div class="dialog-overlay"></div>. Replace the dialog role with alertdialog so that it becomes:

<div class="dialog-container" id="my-dialog" aria-hidden="true" aria-labelledby="my-dialog-title" aria-describedby="my-dialog-description" role="alertdialog">

Now, clicking on the overlay outside of the dialog box does not close the dialog, as expected.It should prevent the underlying body from scrolling, if needed.✅
I didn’t manually test but this, but it is clearly available per the documentation.It should avoid the common pitfalls that come with the native <dialog> element.✅
This component does not rely on the native <dialog> which means we’re good here. Next, let’s use some a11y tooling

I used Lighthouse to test the component both on a desktop computer and a mobile device, in two different scenarios where the dialog is open by default, and closed by default.

I’ve found that sometimes the tooling doesn’t account for DOM elements that are dynamically shown or hidden DOM elements, so this test ensures I’m getting full coverage of both scenarios.

I also tested with IBM Equal Access Accessibility Checker. Generally, this tool will give you a red violation error if there’s anything egregious wrong. It will also ask you to manually review certain items. As seen here, there a couple of items for manual review, but no red violations.

Moving on to screen readers

Between my manual and tooling checks, I’m already feeling relatively confident that a11y-dialog is an accessible option for my dialog of choice. However, we ought to do our due diligence and consult a screen reader.

VoiceOver is the most convenient screen reader for me since I work on a Mac at the moment, but JAWS and NVDA are big names to look at as well. Like checking for UI consistency across browsers, it’s probably a good idea to test on more than one screen reader if you can.

Here’s how I conducted the screen reader part of the audit with VoiceOver. Basically, I mapped out what actions needed testing and confirmed each one, like a script:

StepResultThe dialog component’s trigger button is announced.“Entering A11y Dialog Test, web content.”The dialog should open when pressing CTRL+ALT +Space should show the dialog.“Dialog. Some description of what’s inside this dialog. You are currently on a dialog, inside of web content.”The dialog should TAB to and put focus on the component’s Close button.“Close this dialog button. You are currently on a button, inside of web content.”Tab to the link element and confirm it is announced.“Link, Rando Yahoo Link”Pressing the SPACE key while focused on the Close button should close the dialog component and return to the last item in focus.✅ Testing with people

If you’re thinking we’re about to move on to testing with real people, I was unfortunately unable to find someone. If I had done this, though, I would have used a similar set of steps for them to run through while I observe, take notes, and ask a few questions about the general experience.

As you can see, a satisfactory audit involves a good deal of time and thought.

Fine, but I want to use a framework’s dialog component

That’s cool! Many frameworks have their own dialog component solution, so there’s lots to choose from. I don’t have some amazing spreadsheet audit of all the frameworks and libraries in the wild, and will spare you the work of evaluating them all.

Instead, here are some resources that might be good starting points and considerations for using a dialog component in some of the most widely used frameworks.

Disclaimer: I have not tested these personally. This is all stuff I found while researching.

Angular dialog options

In 2020, Deque published an article that audits Angular component libraries and the TL;DR was that Material (and its Angular/CDK library) and ngx-bootstrap both appear to provide decent dialog accessibility.

React dialog options

Reakit offers a dialog component that they claim is compliant with WAI-ARIA dialog guidelines, and chakra-ui appears to pay attention to its accessibility. Of course, Material is also available for React, so that’s worth a look as well. I’ve also heard good things about reach/dialog and Adobe’s @react-aria/dialog.

Vue dialog options

I’m a fan of Vuetensils, which is Austin Gil’s naked (aka headless) components library, which just so happens to have a dialog component. There’s also Vuetify, which is a popular Material implementation with a dialog of its own. I’ve also crossed paths with PrimeVue, but was surprised that its dialog component failed to return focus to the original element.

Svelte dialog options

You might want to look at svelte-headlessui. Material has a port in svelterial that is also worth a look. It seems that many current SvelteKit users prefer to build their own component sets as SvelteKit’s packaging idiom makes it super simple to do. If this is you, I would definitely recommend considering svelte-a11y-dialog as a convenient means to build custom dialogs, drawers, bottom sheets, etc.

I’ll also point out that my AgnosticUI library wraps the React, Vue, Svelte and Angular a11y-dialog adapter implementations we’ve been talking about earlier.

Bootstrap, of course

Bootstrap is still something many folks reach for, and unsurprisingly, it offers a dialog component. It requires you to follow some steps in order to make the modal accessible.

If you have other inclusive and accessible library-based dialog components that merit consideration, I’d love to know about them in the comments!

But I’m creating a custom design system

If you’re creating a design system or considering some other roll-your-own dialog approach, you can see just how many things need to be tested and taken into consideration… all for one component! It’s certainly doable to roll your own, of course, but I’d say it’s also extremely prone to error. You might ask yourself whether the effort is worthwhile when there are already battle-tested options to choose from.

I’ll simply leave you with something Scott O’Hara — co-editor of ARIA in HTML and HTML AAM specifications in addition to just being super helpful with all things accessibility — points out:

You could put in the effort to add in those extensions, or you could use a robust plugin like a11y-dialog and ensure that your dialogs will have a pretty consistent experience across all browsers.

Back to my objective…

I need that dialog to support React, Vue, Svelte, and Angular implementations.

I mentioned earlier that a11y-dialog already has ports for Vue and React. But the Vue port hasn’t yet been updated for Vue 3. Well, I was quite happy to spend the time I would have spent creating what likely would have been a buggy hand-rolled dialog component toward helping update the Vue port. I also added a Svelte port and one for Angular too. These are both very new and I would consider them experimental beta software at time of writing. Feedback welcome, of course!

It can support other components, too!

I think it’s worth pointing out that a dialog uses the same underlying concept for hiding and showing that can be used for a drawer (aka off-canvas) component. For example, if we borrow the CSS we used in our dialog accessibility audit and add a few additional classes, then a11y-dialog can be transformed into a working and effective drawer component:

.drawer-start { right: initial; } .drawer-end { left: initial; } .drawer-top { bottom: initial; } .drawer-bottom { top: initial; } .drawer-content { margin: initial; max-width: initial; width: 25rem; border-radius: initial; } .drawer-top .drawer-content, .drawer-bottom .drawer-content { width: 100%; }

These classes are used in an additive manner, essentially extending the base dialog component. This is exactly what I have started to do as I add my own drawer component to AgnosticUI. Saving time and reusing code FTW!

Wrapping up

Hopefully I’ve given you a good idea of the thinking process that goes into the making and maintenance of a component library. Could I have hand-rolled my own dialog component for the library? Absolutely! But I doubt it would have yielded better results than what a resource like Kitty’s a11y-dialog does, and the effort is daunting. There’s something cool about coming up with your own solution — and there may be good situations where you want to do that — but probably not at the cost of sacrificing something like accessibility.

Anyway, that’s how I arrived at my decision. I learned a lot about the native HTML <dialog> and its accessibility along the way, and I hope my journey gave you some of those nuggets too.

Dialog Components: Go Native HTML or Roll Your Own? originally published on CSS-Tricks. You should get the newsletter.

Verevolf

Css Tricks - Fri, 05/20/2022 - 10:24am

Zeldman:

You may not know his name, but he played a huge part in creating the web you take for granted today. And he’s back—kind of.

That would be Glenn Davis and the Verevolf site Zeldman’s talking about. The site is a growing archive of Davis’s personal (and unvarnished) recollections pioneering the early web, like the one where he recounts the origin story of his uber-famous Cool Site of the Day.

Credit: Web Design Museum

Or the how an email he wrote out of frustration snowballed into the Web Standards Project.

Personal retellings of history can be fraught with emotion and Davis’s posts are no exception. What’s super cool is how his experiences augment other retellings, including Chapter 7 of our own Web History series where Jay Hoffman documents the evolution of web standards.

To Shared LinkPermalink on CSS-Tricks

Verevolf originally published on CSS-Tricks. You should get the newsletter.

Syndicate content
©2003 - Present Akamai Design & Development.