Auto-Generated Social Media Images

I’ve been thinking about social media images again. You know, the images that (can) show up when you share a link in places like Twitter, Facebook, or iMessage. You’re essentially leaving money on the table without them, because they turn a regular post with a little ol’ link in it into a post with a big honkin’ attention grabbin’ image on it, with a massive clickable area. Of any image on a website, the social media image might be the #1 most viewed, most remembered, most network-requested image on the site.

It’s essentially this bit of HTML that makes them happen:

<meta property="og:image" content="/images/social-media-image.jpg" />

But make sure to read up on it as there are a bunch other other HTML tags to get right.

I think I’m thinking about it again because GitHub seems to have new social media cards. These are new, right?

Those GitHub social media images are clearly programmatically generated. Check out an example URL.


While I think you can get a lot of bang out of a totally hand-crafted bespoke-designed social media image, that’s not practical for sites with lots of pages: blogs, eCommerce… you know what I mean. The trick for sites like that is to automate their creation via templating somehow. I’ve mentioned other people’s takes on this in the past, but let’s recap:

You know what all those have in common? Puppeteer.

Puppeteer is a way to spin up and control a headless copy of Chrome. It has this incredibly useful feature of being able to take a screenshot of the browser window: await page.screenshot({path: 'screenshot.png'});. That’s how our coding fonts website does the screenshots. The screenshotting idea is what gets people’s minds going. Why not design a social media template in HTML and CSS, then ask Puppeteer to screenshot it, and use that as the social media image?

I love this idea, but it means having access to a Node server (Puppeteer runs on Node) that is either running all the time, or that you can hit as a serverless function. So it’s no wonder that this idea has resonated with the Jamstack crowd who are already used to doing things like running build processes and leveraging serverless functions.

I think the idea of “hosting” the serverless function at a URL — and passing it the dynamic values of what to include in the screenshot via URL parameter is also clever.

The SVG route

I kinda dig the idea of using SVG as the thing that you template for social media images, partially because it has such fixed coordinates to design inside of, which matches my mental model of making the exact dimensions you need to design social media images. I like how SVG is so composable.

George Francis blogged “Create Your Own Generative SVG Social Images” which is a wonderful example of all this coming together nicely, with a touch of randomization and whimsy. I like the contenteditable trick as well, making it a useful tool for one-off screenshotting.

I’ve dabbled in dynamic SVG creation as well: check out this conference page on our Conferences site.

Unfortunately, SVG isn’t a supported image format for social media images. Here’s Twitter specifically:

URL of image to use in the card. Images must be less than 5MB in size. JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an animated GIF will be used. SVG is not supported.

Twitter docs

Still, composing/templating in SVG can be cool. You convert it to another format for final usage. Once you have an SVG, the conversion from SVG to PNG is almost trivially easy. In my case, I used svg2png and a very tiny Gulp task that runs during the build process.

What about WordPress?

I don’t have a build process for my WordPress site — at least not one that runs every time I publish or update a post. But WordPress would benefit the most (in my world) from dynamic social media images.

It’s not that I don’t have them now. Jetpack goes a long way in making this work nicely. It makes the “featured image” of the post the social media image, allows me to preview it, and auto-posts to social networks. Here’s a video I did on that. That’s gonna get me to a place where the featured images are attached and showing nicely.

But it doesn’t automate their creation. Sometimes a bespoke graphic alone is the way to go (the one above might be a good example of that), but perhaps more often a nicely templated card is the way to go.

Fortunately I caught wind of Social Image Generator for WordPress from Daniel Post. Look how fancy:

This is exactly what WordPress needs!

Daniel himself helped me create a custom template just for CSS-Tricks. I had big dreams of having a bunch of templates to choose from that incorporate the title, author, chosen quotes, featured images, and other things. So far, we’ve settled on just two, a template with the title and author, and a template with a featured image, title, and author. The images are created from that metadata on the fly:

So meta.

This ain’t Puppeteer. This ain’t even the PhantomJS powered svgtopng. This is PHP generated images! And not even ImageMagick, but straight up GD, the thing built right into PHP. So these images are not created in any kind of syntax that would likely feel comfortable to a front-end developer. You’re probably better off using one of the templates, but if you wanna see how my custom one was coded (by Daniel), lemme know and I can post the code somewhere public.

Pretty cool result, right?


I get why it had to be built this way: it’s using technology that will work literally anywhere WordPress can run. That’s very much in the WordPress spirit. But it does make me wish creating the templates could be done in a more modern way. Like wouldn’t it be cool if the template for your social media images was just like social-image.php at the root of the theme like any other template file? And you template and design that page with all the normal WordPress APIs? Like an ACF Block almost? And it gets screenshot and used? I’ll answer for you: Yes, that would be cool.

The post Auto-Generated Social Media Images appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

Creating Stylesheet Feature Flags With Sass !default

!default is a Sass flag that indicates conditional assignment to a variable — it assigns a value only if the variable was previously undefined or null. Consider this code snippet:

$ variable: 'test' !default;

To the Sass compiler, this line says:

Assign $ variable to value 'test', but only if $ variable is not already assigned.

Here’s the counter-example, illustrating the other side of the !default flag’s conditional behavior:

$ variable: 'hello world'; $ variable: 'test' !default; // $ variable still contains `hello world`

After running these two lines, the value of $ variable is still 'hello world' from the original assignment on line 1. In this case, the !default assignment on line 2 is ignored since a value has already been provided, and no default value is needed.

Style libraries and @use...with

The primary motivation behind !default in Sass is to facilitate the usage of style libraries, and their convenient inclusion into downstream applications or projects. By specifying some of its variables as !default, the library can allow the importing application to customize or adjust these values, without completely forking the style library. In other words, !default variables essentially function as parameters which modify the behavior of the library code.

Sass has a special syntax just for this purpose, which combines a stylesheet import with its related variable overrides:

// style.scss @use 'library' with (   $ foo: 'hello',   $ bar: 'world' );

This statement functions almost the same as a variable assignment followed by an @import, like so:

// style.scss - a less idiomatic way of importing `library.scss` with configuration $ foo: 'hello'; $ bar: 'world'; @import 'library';

The important distinction here, and the reason @use...with is preferable, is about the scope of the overrides. The with block makes it crystal clear — to both the Sass compiler and anyone reading the source code — that the overrides apply specifically to variables which are defined and used inside of library.scss. Using this method keeps the global scope uncluttered and helps mitigate variable naming collisions between different libraries.

Most common use case: Theme customization

// library.scss $ color-primary: royalblue !default; $ color-secondary: salmon !default:    // style.scss @use 'library' with (   $ color-primary: seagreen !default;   $ color-secondary: lemonchiffon !default:  );

One of the most ubiquitous examples of this feature in action is the implementation of theming. A color palette may be defined in terms of Sass variables, with !default allowing customization of that color palette while all other styling remains the same (even including mixing or overlaying those colors).

Bootstrap exports its entire Sass variable API with the !default flag set on every item, including the theme color palette, as well as other shared values such as spacing, borders, font settings, and even animation easing functions and timings. This is one of the best examples of the flexibility provided by !default, even in an extremely comprehensive styling framework.

In modern web apps, this behavior by itself could be replicated using CSS Custom Properties with a fallback parameter. If your toolchain doesn’t already make use of Sass, modern CSS may be sufficient for the purposes of theming. However, we’ll examine use cases that can only be solved by use of the Sass !default flag in the next two examples.

Use case 2: Loading webfonts conditionally

// library.scss $ disable-font-cdn: false !default; @if not $ disable-font-cdn {   @import url(''''); }   // style.scss @use 'library' with (   $ disable-font-cdn: true ); // no external HTTP request is made

Sass starts to show its strength when it leverages its preprocessor appearance in the CSS lifecycle. Suppose the style library for your company’s design system makes use of a custom webfont. It’s loaded from a Google’s CDN — ideally as quick as it gets — but nevertheless a separate mobile experience team at your company has concerns about page load time; every millisecond matters for their app.

To solve this, you can introduce an optional boolean flag in your style library (slightly different from the CSS color values from the first example). With the default value set to false, you can check this feature flag in a Sass @if statement before running expensive operations such as external HTTP requests. Ordinary consumers of your library don’t even need to know that the option exists — the default behavior works for them and they automatically load the font from the CDN, while other teams have access to the toggle what they need in order to fine tune and optimize page loading.

A CSS variable would not be sufficient to solve this problem — although the font-family could be overridden, the HTTP request would have already gone out to load the unused font.

Use case 3: Visually debugging spacing tokens

View live demo

!default feature flags can also be used to create debugging tools for use during development. In this example, a visual debugging tool creates color-coded overlays for spacing tokens. The foundation is a set of spacing tokens defined in terms of ascending “t-shirt sizes” (aka “xs”/”extra-small” through “xl”/”extra-large”). From this single token set, a Sass @each loop generates every combination of utility classes applying that particular token to padding or margin, on every side (top, right, bottom, and left individually, or all four at once).

Since these selectors are all constructed dynamically in a nested loop, and single !default flag can switch the rendering behavior from standard (margin plus padding) to the colored debug view (using transparent borders with the same sizes). This color-coded view may look very similar to the deliverables and wireframes which a designer might hand off for development — especially if you are already sharing the same spacing values between design and dev. Placing the visual debug view side-by-side with the mockup can help quickly and intuitively spot discrepancies, as well as debug more complex styling issues, such as margin collapse behavior.

Again — by the time this code is compiled for production, none of the debugging visualization will be anywhere in the resulting CSS since it will be completely replaced by the corresponding margin or padding statement.

Further reading

These are just a few examples of Sass !default in the wild. Refer to these documentation resources and usage examples as you adapt the technique to your own variations.

The post Creating Stylesheet Feature Flags With Sass !default appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , ,

HTML Boilerplates

Manuel Matuzović goes line-by-line through a boilerplate HTML document. I like it. It’s a good reference and has a lot of the same type of stuff I tend to put in the main HTML template. It makes me think about how opinionated this kind of thing can be. Dang near every line! Not the DOCTYPE, not the <title>, but nearly everything else.

<!DOCTYPE html> <html lang="en" class="no-js"> <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width">    <title>Unique page title - My Site</title>    <script type="module">     document.documentElement.classList.remove('no-js');     document.documentElement.classList.add('js');   </script>    <link rel="stylesheet" href="/assets/css/styles.css">   <link rel="stylesheet" href="/assets/css/print.css" media="print">    <meta name="description" content="Page description">   <meta property="og:title" content="Unique page title - My Site">   <meta property="og:description" content="Page description">   <meta property="og:image" content="">   <meta property="og:image:alt" content="Image description">   <meta property="og:locale" content="en_GB">   <meta property="og:type" content="website">   <meta name="twitter:card" content="summary_large_image">   <meta property="og:url" content="">   <link rel="canonical" href="">    <link rel="icon" href="/favicon.ico">   <link rel="icon" href="/favicon.svg" type="image/svg+xml">   <link rel="apple-touch-icon" href="/apple-touch-icon.png">   <link rel="manifest" href="/my.webmanifest">   <meta name="theme-color" content="#FF00FF"> </head>  <body>   <!-- Content -->   <script src="/assets/js/xy-polyfill.js" nomodule></script>   <script src="/assets/js/script.js" type="module"></script> </body> </html>

Maybe my site doesn’t use any JavaScript or have no-JavaScript fallbacks so I don’t need any of the class name dancing. Maybe my site doesn’t need print styles, but I do need link prefetching. Maybe I don’t care about social images, but I do want critical CSS in the head. It’s a boilerplate, not a prescription — it’s meant to be changed.

There was a time when HTML5 Boilerplate was a huge project in this space. It has a whole GitHub Org! The boilerplate has 50,000 stars! Personally, I feel like the project lost its way when it started to have a src and dist folder and a 200-line Gulp build process, ya know? It worked best as a reference for what stuff any given web project might need, but now I feel like it is intimidating in a way it doesn’t need to be. The boilerplate index file is also quite opinionated. It assumes Normalize and Modernizr, which are certainly not deprecated projects, but also not things I see developers reaching for much anymore. It even assumes Google Analytics usage!

I have no problem with people having and sharing boilerplate documents, but considering how unavoidable being opinionated it is with them, I also like the reference guide approach. Just show me every possible thing that can go in the <head> (a lot of the value of these boilerplates), and I’ll pick and choose what I need (or may have forgotten). To that end, Josh Buchea’s HEAD project is pretty cool.

The post HTML Boilerplates appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



DevTools for CSS layouts 2021 edition

Chen Hui Jing covers some recent movement in DevTools:

Firefox’s grid inspector was pretty full-featured from the get-to and released together with CSS grid in Firefox 52. It was constantly improved upon since. Chrome added a basic grid inspector tool in Chrome 62 that let developers highlight elements using grid layout, but more robust features were only added in Chrome 87. And now, Webkit [sic] has joined the party, as Safari Technology Preview 123 adds Grid inspecting features as well.

You love to see it. DevTools have a massive impact on how front-end developers think about, build, and of course, debug websites. Stuff like seeing the numbered grid lines visually is a huge deal. I’ve done enough mentally counting what rows/columns I want to place things on, thank you very much.

Direct Link to ArticlePermalink

The post DevTools for CSS layouts 2021 edition appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

2021 Design Systems (Survey/Courses)

My friends at Sparkbox are doing a survey on design systems, as they do each year. Go ahead and fill it out if you please. Here are the results from last year. In both 2019 and 2020, the vibe was that design systems (both as an idea and as a real thing that real companies really use) are maturing. But still, it was only a quarter of folks who said their actual design system was mature. I wonder if that’ll go up this year.

In my circles, “design system” isn’t the buzzword it was a few years ago, but it doesn’t mean it’s less popular. If anything, they are more popular almost entering the territory of assumed, like responsive design is. I do feel like if you’re building a website from components, well, you’ve got a component library at least, which is how I think of design systems (as a dude who pretty much exclusively works on websites).

I’d better be careful though. I know design systems mean different things to different people. Speaking of which, I’d be remiss if I didn’t also shout out the fact that Ethan has a handful of totally free courses he’s created on design systems.

As you might have guessed from the titles, we’ve broadly organized the courses around roles: whether you’re a designer, a developer, or a product manager, we’ve got something for you. Each course focuses on what I think are the fundamentals of design systems work, so I’ve designed them to be both high-level and packed with information

The post 2021 Design Systems (Survey/Courses) appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

SVGator 3.0 Reshapes the Way You Create and Animate SVG With Extensive New Features

Building animations can get complicated, particularly compelling animations where you aren’t just rolling a ball across the screen, but building a scene where many things are moving and changing in concert. When compelling animation is the goal, a timeline GUI interface is a long-proven way to get there. That was true in the days of Flash (RIP), and still true today in every serious video editing tool.

But what do we have on the web for animating vector graphics? We have SVGator, an innovative and unique SVG animation creator that doesn’t require any coding skills! With the new SVGator 3.0 release it becomes a ground-breaking tool for building web animations in a visually intuitive way.

It’s worth just looking at the homepage to see all the amazing animations built using the tool itself.

A powerful tool right at your fingertips

I love a good browser-based design tool. All my projects in the cloud, waiting for me no matter where I roam. Here’s my SVGator project as I was working on a basic animation myself:

Users of any design tool should feel at home here. It comes with an intuitive interface that rises to the demands of a professional workflow with options that allow more control over your workspace. You will find a list of your elements and groups on the left side, along with the main editing tools right at the top. All the properties can be found on the right side and you can bring any elements to life by setting up keyframes on the timeline.

I’ll be honest: I’ve never really used SVGator before, I read zero documentation, and I was able to fiddle together these animated CSS-Tricks stars in just a few minutes:

See that animation above?

  • It was output as a single animation-stars.svg document I was able to upload to this blog post without any problem.
  • Uses only CSS inside the SVG for animation. There is a JavaScript-animation output option too for greater cross-browser compatibility and extra features (e.g. start animation only when it enters the view or start the animation on click!)
  • The final file is just 5 KB. Tiny!
  • The SVG, including the CSS animation bits, are pre-optimized and compressed.
  • And again, it only took me a couple minutes to figure out.

Imagine what you could do if you actually had design and animation talent.

Intuitive and useful export options

Optimized SVG output

SVGator is also an outstanding vector editor

With the 3.0 release, SVGator is no longer just an animation tool, but a full-featured SVG creator! This means that there’s no need for a third-party vector editing tool, you can start from scratch in SVGator or edit any pre-existing SVGs that you’ve ever created. A Pen tool with fast editing options, easily editable shapes, compound options, and lots of other amazing features will assist you in your work.

That’s particularly useful with morphing tweens, which SVGator totally supports!

Here’s my one word review:

Cool (animating in and out with scale and blur effects)

The all-new interface in SVGator has a sidebar of collapsible panels for controlling all aspects of the design. See here how easy this animation was to make by controlling a line of text, the transforms, and the filters, and then choosing keyframes for those things below.

Suitable plans for everyone

If you want to use SVGator as a vector editing tool, that’s absolutely free, which means that you can create and export an endless number of static vector graphics, regardless of your subscription plan In fact, even on the Free plan, you export three animations per month. It’s just when you want more advanced animation tooling or unlimited animated exports per month than that you have to upgrade to a paid plan.

The post SVGator 3.0 Reshapes the Way You Create and Animate SVG With Extensive New Features appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , ,

Advanced CSS Animation Using cubic-bezier()

When dealing with complex CSS animations, there is a tendency to create expansive @keyframes with lots of declarations. There are a couple of tricks though that I want to talk about that might help make things easier, while staying in vanilla CSS:

  1. Multiple animations
  2. Timing functions

The first one is more widely used and familiar but the second one is less common. There could be good reasons for that — chaining animations with commas is relatively easier than grokking the various timing functions that are available to us and what they do. There’s one especially neat timing function that gives us total control to create custom timing functions. That would be cubic-bezier() and in this post I will show you the power of it and how it can be used to create fancy animation without too much complexity.

Let’s start with a basic example showing how we can move a ball around in interesting directions, like an infinity (∞) shape:

As you can see, there is no complex code — only two keyframes and a cubic-bezier() function. And yet, a pretty complex-looking final infinity-shape animation is what we get.

Cool, right? Let’s dig into this!

The cubic-bezier() function

Let’s start with the official definition:

A cubic Bézier easing function is a type of easing function defined by four real numbers that specify the two control points, P1 and P2, of a cubic Bézier curve whose end points P0 and P3 are fixed at (0, 0) and (1, 1) respectively. The x coordinates of P1 and P2 are restricted to the range [0, 1].

The above curve defines how the output (y-axis) will behave based on the time (x-axis). Each axis has a range of [0, 1] (or [0% 100%] ). If we have an animation that lasts two-second (2s), then:

0 (0%) = 0s  1 (100%) = 2s

If we want to animate left from 5px to 20px, then:

0 (0%) = 5px  1 (100%) = 20px

X, the time, is always restricted to [0 1]; however, Y, the output, can go beyond [0 1].

My goal is to adjust P1 and P2 in order to create the following curves:

Parabolic curve

Sinusoidal curve

You may think this is impossible to achieve because, as stated in the definition, P0 and P3 are fixed at (0,0) and (1,1) meaning they cannot be on the same axis. That’s true, and we will use some math tricks to “approximate” them.

Parabolic curve

Let’s start with the following definition: cubic-bezier(0,1.5,1,1.5). That gives us the following curve:


Our goal is to move (1,1) and make it at (0,1) which isn’t technically possible. So we will try to fake it.

We previously said that our range is [0 1] (or [0% 100%]) so let’s imagine the case when 0% is very close to 100%. If, for example, we want to animate top from 20px (0%) to 20.1px (100%) then we can say that both the initial and final states are equal.

Hm, but our element will not move at all, right?

Well, it will move a little because the Y value exceeds 20.1px (100%). But that’s not enough to give us perceptible movement:

Let’s update the curve and use cubic-bezier(0,4,1,4) instead. Notice how our curve is way taller than before:


But yet, still no movement — even if the top value is crossing 3 (or 300%). Let’s try cubic-bezier(0,20,1,20):


Yes! it started to move a little. Did you notice the evolution of the curve each time we increase the value? It’s making our point (1,1) “visually” closer to (0,1) when we zoom out to see the full curve and this is the trick.

By using cubic-bezier(0,V,1,V) where V is some very big value and both the initial and final states are very close together (or almost equal), we can simulate the parabolic curve.

An example is worth a thousand words:

I applied the “magic” cubic-bezier function in there to the top animation, plus a linear one applied to left. This gives us the curve we want.

Digging into the math

For those of you math-minded folks out there, we can break that explanation down further. A cubic bezier can be defined using the following formula:

P = (1−t)³P0 + 3(1−t)²tP1 + 3(1−t)t²P2 + t³P3

Each point is defined as follows: P0 = (0,0), P1 = (0,V), P2 = (1,V), and P3 = (1,1).

This gives us the two functions for x and y coordinates:

  • X(t) = 3(1−t)t² + t³ = 3t² - 2t³
  • Y(t) = 3(1−t)²tV +3(1−t)t²V + t³ = t³ - 3Vt² + 3Vt

V is our big value and t is within the range [0 1]. If we consider our previous example, Y(t) will give us the value of top while X(t) is the time progress. The points (X(t),Y(t)) will then define our curve.

Let’s find the maximum value of Y(t). For this, we need to find the value of t that will give us Y'(t) = 0 (when the derivative is equal to 0):

Y'(t) = 3t² - 6Vt + 3V

Y'(t) = 0 is a quadratic equation. I will skip the boring part and will give you the result, which is t = V - sqrt(V² - V).

When V is a large value, t will be equal to 0.5. So, Y(0.5) = Max and X(0.5) will be equal to 0.5. That means we reach the maximum value at the halfway point in the animation, which conforms to the parabolic curve we want.

Also, Y(0.5) will give us (1 + 6V)/8 and this will allow us to find the max value based on V. And since we will always use a big value for V, we can simplify to 6V/8 = 0.75V.

We used V = 500 in the last example, so the max value there would come out to 375 (or 37500%) and we get the following:

  • Initial state (0): top: 200px
  • Final state (1): top: 199.5px

There’s a difference of -0.5px between 0 and 1. Let’s call it the increment. For 375 (or 37500%) we have an equation of 375*-0.5px = -187.5px. Our animated element is reaching top: 12.5px (200px - 187.5px) and gives us the following animation:

top: 200px (at 0% of the time ) → top: 12.5px (at 50% of the time) → top: 199.5px (at 100% of the time)  

Or, expressed another way:

top: 200px (at 0%) → top: 12.5px (at 50%) → top: 200px (at 100%)

Let’s do the opposite logic. What value of V should we use to make our element reach top: 0px? The animation will be 200px → 0px → 199.5px, so we need -200px to reach 0px. Our increment is always equal to -0.5px. The max value will be equal to 200/0.5 = 400, so 0.75V = 400 which means V = 533.33.

Our element is touching the top!

Here is a figure that sums up that math we just did:

Parabolic Curve using cubic-bezier(0,V,1,V)

Sinusoidal curve

We will use almost the exact same trick to create a sinusoidal curve but with a different formula. This time we will use cubic-bezier(0.5,V,0.5,-V)

Like we did before, let’s see how the curve will evolve when we increase the value:

Three graphs from left to right, showing how the sinusoidal curve gets narrower as the V value increases.

I think you probably get the idea by now. Using a big value for V gets us close to a sinusoidal curve.

Here’s another one with a continuous animation — a real sinusoidal animation!

The math

Let’s get in the math for this one! Folllowing the same formula as before, we will get the following functions:

  • X(t) = 3/2(1−t)²t + 3/2(1−t)t² + t³ = (3/2)t - (3/2)t² + t³
  • Y(t) = 3(1−t)²tV - 3(1−t)t²V + t³ = (6V + 1)t³ - 9Vt² + 3Vt

This time we need to find the minimum and maximum values for Y(t). Y'(t) = 0 will give us two solutions. After solving for this:

Y'(t) = 3(6V + 1)t² - 18Vt + 3V = 0

…we get:

  • t' = (3V + sqrt(3V² - V))/(6V + 1)
  • t''= (3V - sqrt(3V² - V))/(6V + 1)

For a big value of V, we have t'=0.211 and t"=0.789. That means that Y(0.211) = Max and Y(0.789) = Min. That also means that X(0.211)= 0.26 and X(0.789) = 0.74. In other words, we reach the Max at 26% of the time and Min at 74% of the time.

Y(0.211) is equal to 0.289V and Y(0.789) to -0.289V. We got those values with some rounding considering that V is very big.

Our sinusoidal curve should also cross the x-axis (or Y(t) = 0) at half the time (or X(t) = 0.5). In order to prove this, we use the second derivate of Y(t) — which should be equal to 0 — so Y''(t) = 0.

Y''(t) = 6(6V + 1)t - 18V = 0

The solution is 3V/(6V + 1), and for a big V value, the solution is 0.5. That give us Y(0.5) = 0 and X(0.5) = 0.5 which confirms that our curve crosses the (0.5,0) point.

Now let’s consider the previous example and try to find the value of V that gets us back to top: 0%. We have:

  • Initial state (0): top: 50%
  • Final state (1): top: 49.9%
  • Increment: -0.1%

We need -50% to reach top: 0%, so 0.289V*-0.1% = -50% which gives us V = 1730.10.

As you can see, our element is touching the top and disappearing at the bottom because we have the following animation:

top: 50% → top: 0% → top: 50% → top: 100% → top: 50% → and so on ... 

A figure to sum up the calculation:

Sinusoidal Curve using cubic-bezier(0.5,V,0.5,-V)

And an example to illustrate all curves together:

Yes, you see four curves! If you look closely, you will notice that I am using two different animations, one going to 49.9% (an increment of -0.01%) and another going to 50.1% (an increment of +0.01%). By changing the sign of the increment, we control the direction of the curve. We can also control the other parameters of the cubic bezier (not the V one that should remain a big value) to create more variations from the same curves.

And below, an interactive demo:

Getting back to our example

Let’s get back to our initial example of a ball moving around in the shape of an infinity symbol. I simply combined two sinusoidal animations to make it work.

If we combine what we did previously with the concept of multiple animations, we can get astonishing results. Here again is the initial example, this time as an interactive demo. Change the values and see the magic:

Let’s go further and add a little CSS Houdini to the mix. We can animate a complex transform declaration thanks to @property (but CSS Houdini is limited to Chrome and Edge support at the moment).

What kind of drawings can you make with that? Here is a few that I was able to make:

And here is a spirograph animation:

And a version without CSS Houdini:

There’s a few things to take away from these examples:

  • Each keyframe is defined using only one declaration that contain the increment.
  • The position of the element and the animation are independent. We can easily place the element anywhere without the need to adjust the animation.
  • We made no calculations. There isn’t a ton of angles or pixel values. We only need a tiny value within the keyframe and a big value within the cubic-bezier() function.
  • The whole animation can be controlled just by adjusting the duration value.

What about transition?

The same technique can also be used with the CSS transition property since it follows the same logic when it comes to timing functions. This is great because we’re able to avoid keyframes when creating some complex hover effect.

Here’s what I made without keyframes:

Mario is jumping thanks to the parabolic curve. We needed no keyframes at all to create that shake animation on hover. The sinusoidal curve is perfectly capable of doing all the work.

Here is another version of Mario, this time using CSS Houdini. And, yep, he’s still jumping thanks to the parabolic curve:

For good measure, here are more fancy hover effects without keyframes (again, Chrome and Edge only):

That’s it!

Now you have some magic cubic-bezier() curves and the math behind them. The benefit, of course, is that custom timing functions like this let us do fancy animations without the complex keyframes we generally reach for.

I understand that not everyone is math-minded and that’s okay. There are tools to help, like Matthew Lein’s Ceaser, which lets you drag the curve points around to get what you need. And, if you don’t already have it bookmarked, is another one. If you want to play with cubic-bezier outside the CSS world, I recommend desmos where you can see some math formulas.

Regardless of how you get your cubic-bezier() values, hopefully now you have a sense of their powers and how they can help make for nicer code in the process.

The post Advanced CSS Animation Using cubic-bezier() appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

Making Disabled Buttons More Inclusive

Let’s talk about disabled buttons. Specifically, let’s get into why we use them and how we can do better than the traditional disabled attribute in HTML (e.g. <button disabled> ) to mark a button as disabled.

There are lots of use cases where a disabled button makes a lot of sense, and we’ll get to those reasons in just a moment. But throughout this article, we’ll be looking at a form that allows us to add a number of tickets to a shopping cart.

This is a good baseline example because there’s a clear situation for disabling the “Add to cart” button: when there are no tickets to add to the cart.

But first, why disabled buttons?

Preventing people from doing an invalid or unavailable action is the most common reason we might reach for a disabled button. In the demo below, we can only add tickets if the number of tickets being added to the cart is greater than zero. Give it a try:

Allow me to skip the code explanation in this demo and focus our attention on what’s important: the “Add to cart” button.

<button type="submit" disabled="disabled">   Add to cart </button>

This button is disabled by the disabled attribute. (Note that this is a boolean attribute, which means that it can be written as disabled or disabled="disabled".)

Everything seems fine… so what’s wrong with it?

Well, to be honest, I could end the article right here asking you to not use disabled buttons because they suck, and instead use better patterns. But let’s be realistic: sometimes disabling a button is the solution that makes the most sense.

With that being said, for this demo purpose, we’ll pretend that disabling the “Add to cart” button is the best solution (spoiler alert: it’s not). We can still use it to learn how it works and improve its usability along the way.

Types of interactions

I’d like to clarify what I mean by disabled buttons being usable. You may think, If the button is disabled, it shouldn’t be usable, so… what’s the catch? Bear with me.

On the web, there are multiple ways to interact with a page. Using a mouse is one of the most common, but there are others, like sighted people who use the keyboard to navigate because of a motor impairment.

Try to navigate the demo above using only the Tab key to go forward and Tab + Shift to go backward. You’ll notice how the disabled button is skipped. The focus goes directly from the ticket input to the “dummy terms” link.

Using the Tab key, it changes the focus from the input to the link, skipping the “Add to cart” button.

Let’s pause for a second and recap the reason that lead us to disable the button in the first place versus what we had actually accomplished.

It’s common to associate “interacting” with “clicking” but they are two different concepts. Yes, click is a type of interaction, but it’s only one among others, like hover and focus.

In other words…

All clicks are interactions, but not all interactions are clicks.

Our goal is to prevent the click, but by using disabled, we are preventing not only the click, but also the focus, which means we might be doing as much harm as good. Sure, this behavior might seem harmless, but it causes confusion. People with a cognitive disability may struggle to understand why they are unable to focus the button.

In the following demo, we tweaked the layout a little. If you use a mouse, and hover over the submit button, a tooltip is shown explaining why the button is disabled. That’s great! But if you use only the keyboard, there’s no way of seeing that tooltip because the button cannot be focused with disabled. Ouch!

Using the mouse, the tooltip on the “Add to cart” button is visible on hover. But the tooltip is missing when using the Tab key.

Allow me to once again skip past the code explanation. I highly recommend reading “Inclusive Tooltips” by Haydon Pickering and “Tooltips in the time of WCAG 2.1” by Sarah Higley to fully understand the tooltip pattern.

ARIA to the rescue

The disabled attribute in a <button> is doing more than necessary. This is one of the few cases I know of where a native HTML attribute can do more harm than good. Using an ARIA attribute can do a better job, allowing us to add not only focus on the button, but do so consistently to create an inclusive experience for more people and use cases.

The disabled attribute correlates to aria-disabled="true". Give the following demo a try, again, using only the keyboard. Notice how the button, although marked disabled, is still accessible by focus and triggers the tooltip!

Using the Tab key, the “Add to cart” button is focused and it shows the tooltip.

Cool, huh? Such tiny tweak for a big improvement!

But we’re not done quite yet. The caveat here is that we still need to prevent the click programmatically, using JavaScript.

elForm.addEventListener('submit', function (event) {   event.preventDefault(); /* prevent native form submit */    const isDisabled = elButtonSubmit.getAttribute('aria-disabled') === 'true';    if (isDisabled || isSubmitting) {     // return early to prevent the ticket from being added     return;   }    isSubmitting = true;   // ... code to add to cart...   isSubmitting = false; })

You might be familiar with this pattern as a way to prevent double clicks from submitting a form twice. If you were using the disabled attribute for that reason, I’d prefer not to do it because that causes the temporary loss of the keyboard focus while the form is submitting.

The difference between disabled and aria-disabled

You might ask: if aria-disabled doesn’t prevent the click by default, what’s the point of using it? To answer that, we need to understand the difference between both attributes:

Feature / Attribute disabled aria-disabled="true"
Prevent click
Prevent hover
Prevent focus
Default CSS styles

The only overlap between the two is semantics. Both attributes will announce that the button is indeed disabled, and that’s a good thing.

Contrary to the disabled attribute, aria-disabled is all about semantics. ARIA attributes never change the application behavior or styles by default. Their only purpose is to help assistive technologies (e.g. screen readers) to announce the page content in a more meaningful and robust way.

So far, we’ve talked about two types of people, those who click and those who Tab. Now let’s talk about another type: those with visual impairments (e.g. blindness, low vision) who use screen readers to navigate the web.

People who use screen readers, often prefer to navigate form fields using the Tab key. Now look an how VoiceOver on macOS completely skips the disabled button.

The VoiceOver screen reader skips the “Add to cart” button when using the Tab key.

Once again, this is a very minimal form. In a longer one, looking for a submit button that isn’t there right away can be annoying. Imagine a form where the submit button is hidden and only visible when you completely fill out the form. That’s what some people feel like when the disabled attribute is used.

Fortunately, buttons with disabled are not totally unreachable by screen readers. You can still navigate each element individually, one by one, and eventually you’ll find the button.

The VoiceOver screen reader is able to find and announce the “Add to cart” button.

Although possible, this is an annoying experience. On the other hand, with aria-disabled, the screen reader will focus the button normally and properly announce its status. Note that the announcement is slightly different between screen readers. For example, NVDA and JWAS say “button, unavailable” but VoiceOver says “button, dimmed.”

The VoiceOver screen reader can find the “Add to cart” button using Tab key because of aria-disabled.

I’ve mapped out how both attributes create different user experiences based on the tools we just used:

Tool / Attribute disabled aria-disabled="true"
Mouse or tap Prevents a button click. Requires JS to prevent the click.
Tab Unable to focus the button. Able to focus the button.
Screen reader Button is difficult to locate. Button is easily located.

So, the main differences between both attributes are:

  • disabled might skip the button when using the Tab key, leading to confusion.
  • aria-disabled will still focus the button and announce that it exists, but that it isn’t enabled yet; the same way you might perceive it visually.

This is the case where it’s important to acknowledge the subtle difference between accessibility and usability. Accessibility is a measure of someone being able to use something. Usability is a measure of how easy something is to use.

Given that, is disabled accessible? Yes. Does it have a good usability? I don’t think so.

Can we do better?

I wouldn’t feel good with myself if I finished this article without showing you the real inclusive solution for our ticket form example. Whenever possible, don’t use disabled buttons. Let people click it at any time and, if necessary, show an error message as feedback. This approach also solves other problems:

  • Less cognitive friction: Allow people to submit the form at any time. This removes the uncertainty of whether the button is even disabled in the first place.
  • Color contrast: Although a disabled button doesn’t need to meet the WCAG 1.4.3 color contrast, I believe we should guarantee that an element is always properly visible regardless of its state. But that’s something we don’t have to worry about now because the button isn’t disabled anymore.

Final thoughts

The disabled attribute in <button> is a peculiar case where, although highly known by the community, it might not be the best approach to solve a particular problem. Don’t get me wrong because I’m not saying disabled is always bad. There are still some cases where it still makes sense to use it (e.g. pagination).

To be honest, I don’t see the disabled attribute exactly as an accessibility issue. What concerns me is more of a usability issue. By swapping the disabled attribute with aria-disabled, we can make someone’s experience much more enjoyable.

This is yet one more step into my journey on web accessibility. Over the years, I’ve discovered that accessibility is much more than complying with web standards. Dealing with user experiences is tricky and most situations require making trade-offs and compromises in how we approach a solution. There’s no silver bullet for perfect accessibility.

Our duty as web creators is to look for and understand the different solutions that are available. Only then we can make the best possible choice. There’s no sense in pretending the problems don’t exist.

At the end of the day, remember that there’s nothing preventing you from making the web a more inclusive place.


Still there? Let me mention two last things about this demo that I think are worthy:

1. Live Regions will announce dynamic content

In the demo, two parts of the content changed dynamically: the form submit button and the success confirmation (“Added [X] tickets!”).

These changes are visually perceived, however, for people with vision impairments using screen readers, that just ain’t the reality. To solve it, we need to turn those messages into Live Regions. Those allow assistive technologies to listen for changes and announce the updated messages when they happen.

There is a .sr-only class in the demo that hides a <span> containing a loading message, but allows it to be announced by screen readers. In this case, aria-live="assertive" is applied to the <span> and it holds a meaningful message after the form is submitting and is in the process of loading. This way, we can announce to the user that the form is working and to be patient as it loads. Additionally, we do the same to the form feedback element.

<button type="submit" aria-disabled="true">   Add to cart   <span aria-live="assertive" class="sr-only js-loadingMsg">      <!-- Use JavaScript to inject the the loading message -->   </span> </button>  <p aria-live="assertive" class="formStatus">   <!-- Use JavaScript to inject the success message --> </p>

Note that the aria-live attribute must be present in the DOM right from the beginning, even if the element doesn’t hold any message yet, otherwise, Assistive Technologies may not work properly.

Form submit feedback message being announced by the screen reader.

There’s much more to tell you about this little aria-live attribute and the big things it does. There are gotchas as well. For example, if it is applied incorrectly, the attribute can do more harm than good. It’s worth reading “Using aria-live” by Ire Aderinokun and Adrian Roselli’s “Loading Skeletons” to better understand how it works and how to use it.

2. Do not use pointer-events to prevent the click

This is an alternative (and incorrect) implementation that I’ve seen around the web. This uses pointer-events: none; in CSS to prevent the click (without any HTML attribute). Please, do not do this. Here’s an ugly Pen that will hopefully demonstrate why. I repeat, do not do this.

Although that CSS does indeed prevent a mouse click, remember that it won’t prevent focus and keyboard navigation, which can lead to unexpected outcomes or, even worse, bugs.

In other words, using this CSS rule as a strategy to prevent a click, is pointless (get it?). 😉

The post Making Disabled Buttons More Inclusive appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Street Artist NeSpoon Creates Gigantic Lace Pattern Art


CSS Pie Timer Re-Revisited

Kitty reflected on an ancient blog post here on CSS-Tricks on how to make an animated pie timer. The old technique is still clever. The new technique is equally clever and much easier. I particularly like the steps() animation function that “flips” the “mask” from side-to-side by rotating a pseudo-element half a turn, That’s just good CSS trickery by gosh.

I’d do the “CSS Pie Timer Re-Revisted” post a year or two early just to get ahead of things. It’s not a trick anymore — we just use a conic-gradient() and animate the percentage value as a custom property 0% to 100%.

@property --percentage {   initial-value: 0%;   inherits: false;   syntax: '<percentage>'; }  .chart {   background: conic-gradient(red var(--percentage), white 0);   animation: timer 4s infinite linear; }  @keyframes timer {   to {     --percentage: 100%;   } }

This should work in Chrome (but nothing else) for now:

The post CSS Pie Timer Re-Revisited appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.