Tag: Custom

Adding Custom GitHub Badges to Your Repo

If you’ve spent time looking at open-source repos on GitHub, you’ve probably noticed that most of them use badges in their README files. Take the official React repository, for instance. There are GitHub badges all over the README file that communicate important dynamic info, like the latest released version and whether the current build is passing.

Showing the header of React's repo displaying GitHub badges.

Badges like these provide a nice way to highlight key information about a repository. You can even use your own custom assets as badges, like Next.js does in its repo.

Showing the Next.js repo header with GitHub badges.

But the most useful thing about GitHub badges by far is that they update by themselves. Instead of hardcoding values into your README, badges in GitHub can automatically pick up changes from a remote server.

Let’s discuss how to add dynamic GitHub badges to the README file of your own project. We’ll start by using an online generator called badgen.net to create some basic badges. Then we’ll make our badges dynamic by hooking them up to our own serverless function via Napkin. Finally, we’ll take things one step further by using our own custom SVG files.

Showing three examples of custom GitHub badges including Apprentice, Intermediate, and wizard skill levels.

First off: How do badges work?

Before we start building some badges in GitHub, let’s quickly go over how they are implemented. It’s actually very simple: badges are just images. README files are written in Markdown, and Markdown supports images like so:

![alt text](path or URL to image)

The fact that we can include a URL to an image means that a Markdown page will request the image data from a server when the page is rendered. So, if we control the server that has the image, we can change what image is sent back using whatever logic we want!

Thankfully, we have a couple options to deploy our own server logic without the whole “setting up the server” part. For basic use cases, we can create our GitHub badge images with badgen.net using its predefined templates. And again, Napkin will let us quickly code a serverless function in our browser and then deploy it as an endpoint that our GitHub badges can talk to.

Making badges with Badgen

Let’s start off with the simplest badge solution: a static badge via badgen.net. The Badgen API uses URL patterns to create templated badges on the fly. The URL pattern is as follows:


There’s a full list of the options you have for colors, icons, and more on badgen.net. For this example, let’s use these values:

  • :subject : Hello
  • :status: : World
  • :color: : red
  • :icon: : twitter

Our final URL winds up looking like this:


Adding a GitHub badge to the README file

Now we need to embed this badge in the README file of our GitHub repo. We can do that in Markdown using the syntax we looked at earlier:

![my badge](https://badgen.net/badge/hello/world/red?icon=twitter)

Badgen provides a ton of different options, so I encourage you to check out their site and play around! For instance, one of the templates lets you show the number of times a given GitHub repo has been starred. Here’s a star GitHub badge for the Next.js repo as an example:


Pretty cool! But what if you want your badge to show some information that Badgen doesn’t natively support? Luckily, Badgen has a URL template for using your own HTTPS endpoints to get data:


For example, let’s say we want our badge to show the current price of Bitcoin in USD. All we need is a custom endpoint that returns this data as JSON like this:

{   "color": "blue",   "status": "$ 39,333.7",   "subject": "Bitcoin Price USD" }

Assuming our endpoint is available at https://some-endpoint.example.com/bitcoin, we can pass its data to Badgen using the following URL scheme:

GitHub badge. On the left is a gray label with white text. On the right is a blue label with white text showing the price of Bitcoin.
The data for the cost of Bitcoin is served right to the GitHub badge.

Even cooler now! But we still have to actually create the endpoint that provides the data for the GitHub badge. 🤔 Which brings us to…

Badgen + Napkin

There’s plenty of ways to get your own HTTPS endpoint. You could spin up a server with DigitalOcean or AWS EC2, or you could use a serverless option like Google Cloud Functions or AWS Lambda; however, those can all still become a bit complex and tedious for our simple use case. That’s why I’m suggesting Napkin’s in-browser function editor to code and deploy an endpoint without any installs or configuration.

Head over to Napkin’s Bitcoin badge example to see an example endpoint. You can see the code to retrieve the current Bitcoin price and return it as JSON in the editor. You can run the code yourself from the editor or directly use the endpoint.

To use the endpoint with Badgen, work with the same URL scheme from above, only this time with the Napkin endpoint:


More ways to customize GitHub badges

Next, let’s fork this function so we can add in our own custom code to it. Click the “Fork” button in the top-right to do so. You’ll be prompted to make an account with Napkin if you’re not already signed in.

Once we’ve successfully forked the function, we can add whatever code we want, using any npm modules we want. Let’s add the Moment.js npm package and update the endpoint response to show the time that the price of Bitcoin was last updated directly in our GitHub badge:

import fetch from 'node-fetch' import moment from 'moment'  const bitcoinPrice = async () => {   const res = await fetch("<https://blockchain.info/ticker>")   const json = await res.json()   const lastPrice = json.USD.last+""    const [ints, decimals] = lastPrice.split(".")    return ints.slice(0, -3) + "," + ints.slice(-3) + "." + decimals }  export default async (req, res) => {   const btc = await bitcoinPrice()    res.json({     icon: 'bitcoin',     subject: `Bitcoin Price USD ($ {moment().format('h:mma')})`,     color: 'blue',     status: `$ $ {btc}`   }) }
Deploy the function, update your URL, and now we get this.

You might notice that the badge takes some time to refresh the next time you load up the README file over at GitHub. That’s is because GitHub uses a proxy mechanism to serve badge images.

GitHub serves the badge images this way to prevent abuse, like high request volume or JavaScript code injection. We can’t control GitHub’s proxy, but fortunately, it doesn’t cache too aggressively (or else that would kind of defeat the purpose of badges). In my experience, the TTL is around 5-10 minutes.

OK, final boss time.

Custom SVG badges with Napkin

For our final trick, let’s use Napkin to send back a completely new SVG, so we can use custom images like we saw on the Next.js repo.

A common use case for GitHub badges is showing the current status for a website. Let’s do that. Here are the two states our badge will support:

Badgen doesn’t support custom SVGs, so instead, we’ll have our badge talk directly to our Napkin endpoint. Let’s create a new Napkin function for this called site-status-badge.

The code in this function makes a request to example.com. If the request status is 200, it returns the green badge as an SVG file; otherwise, it returns the red badge. You can check out the function, but I’ll also include the code here for reference:

import fetch from 'node-fetch'  const site_url = "<https://example.com>"  // full SVGs at <https://napkin.io/examples/site-status-badge> const customUpBadge = '' const customDownBadge = ''  const isSiteUp = async () => {   const res = await fetch(site_url)   return res.ok }  export default async (req, res) => {   const forceFail = req.path?.endsWith('/400')    const healthy = await isSiteUp()   res.set('content-type', 'image/svg+xml')   if (healthy && !forceFail) {     res.send(Buffer.from(customUpBadge).toString('base64'))   } else {     res.send(Buffer.from(customDownBadge).toString('base64'))   } }

Odds are pretty low that the example.com site will ever go down, so I added the forceFail case to simulate that scenario. Now we can add a /400 after the Napkin endpoint URL to try it:

![status up](https://napkin-examples.npkn.net/site-status-badge/) ![status down](https://napkin-examples.npkn.net/site-status-badge/400)

Very nice 😎

And there we have it! Your GitHub badge training is complete. But the journey is far from over. There’s a million different things where badges like this are super helpful. Have fun experimenting and go make that README sparkle! ✨

Adding Custom GitHub Badges to Your Repo originally published on CSS-Tricks. You should get the newsletter.


, , , ,

CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web

Styling ranges of text in software is a very useful thing to be able to do. Thankfully, we have the CSS Custom Highlight API to look forward to because it represents the future of styling text ranges on the web.

Animation screenshot of the CSS Custom Highlight API demo.

One example: if you’ve ever used text editing software like Google Docs, Word, or Dropbox Paper, you’ll see they detect spelling and grammar errors and displaying nice little squiggly underlines below them to attract attention. Code editors like VS Code do the same for code errors.

Another very common use case for highlighting text is search and highlight, where you’re given a text input box and typing in it searches matching results on the page, and highlights them. Try pressing Ctrl/+ F in your web browser right now and type in some text from this article.

The browser itself often handles these styling situations. Editable areas (like a <textarea>) get spelling squiggles automatically. The find command highlights found text automatically.

But what about when we want to do this type of styling ourselves? Doing this on the web has been a common problem for a long time. It has probably costed many people a lot more time than it should have.

This isn’t a simple problem to solve. We aren’t just wrapping text in a <span> with a class and applying some CSS. Indeed, this requires being able to correctly highlight multiple ranges of text across an arbitrarily complex DOM tree, and possibly crossing the boundaries of DOM elements.

There are two common solutions to this, including:

  1. styling text range pseudo-elements, and
  2. creating your own text highlighting system.

We’ll review them first and then take a look at the upcoming CSS Custom Highlight API that can change it all. but if you’re

Potential Solution #1: Style-able Text Ranges

Probably the most well-known style-able text range is the user selection. When you use your pointing device to select a piece of text in a web page, a Selection object is automatically created. In fact, try selecting text on this page right now, and then run document.getSelection() in the DevTools console. You should see location information about the selected text.

DevTools window showing the position of the current selection in the console.

It turns out that you can also create a text selection programmatically from JavaScript. Here is an example:

// First, create a Range object. const range = new Range();  // And set its start and end positions. range.setStart(parentNode, startOffset); range.setEnd(parentNode, endOffset);  // Then, set the current selection to this range. document.getSelection().removeAllRanges(); document.getSelection().addRange(range);

The last piece of the puzzle is to style this range. CSS has a pseudo-element called ::selection to do just that, and it’s supported across all browsers.

::selection {   background-color: #f06;   color: white; }

Here is an example using this technique to highlight all words in a page one after the other:

On top of the ::selection pseudo-element, there are a number of other pseudo-elements:

  • ::target-text selects the text that has been scrolled to in browsers that support the scroll-to-text feature. (MDN)
  • ::spelling-error selects text that is flagged by the browser as containing a spelling error. (MDN)
  • ::grammar-error selects text that is flagged by the browser as containing a grammar error. (MDN)

Unfortunately browser support isn’t great here and although these ranges are useful in each of their own right, they can’t be used to style custom pieces of text — only browser-predefined ones

So the user text selection is nice because it’s relatively simple to put in place and doesn’t change the DOM of the page. Indeed, Range objects are essentially coordinates of segments in the page, rather than HTML elements that need to be created to exist.

One major drawback, however, is that creating a selection resets whatever the user has already manually selected. Try selecting text in the demo above to test this. You’ll see how it goes away as soon as the code moves the selection somewhere else.

Potential Solution #2: Custom Highlighting System

This second solution is pretty much the only thing you can do if using the Selection object is insufficient for you. This solution revolves around doing everything yourself, using JavaScript to insert new HTML elements in the DOM where you want the highlighting to appear.

Unfortunately, this means way more JavaScript to write and maintain, not to mention it forces the browser to re-create the layout of the page whenever the highlighting changes. Plus, there are complicated edge cases, for example, when you want to highlight a piece of text that spans across multiple DOM elements.

Illustration showing a line of HTML with an emphasis element and a strong element with a bright yellow highlight running through them.

Interestingly, CodeMirror and Monaco (the JavaScript text editor library that powers VS Code) have their own highlighting logic. They use a slightly different approach where the highlights are contained in a separate part of the DOM tree. The lines of text and the highlighted segments are rendered in two different places in the DOM which are then positioned over each other. If you inspect the DOM sub-tree that contains the text, there are no highlights. This way, the highlights can be re-rendered without impacting the lines of text and having to introduce new elements within them.

Overall, it feels like a browser-powered highlighting feature is missing. Something that would help solve all of these drawbacks (no interference with user text selection, multi-selection support, simple code) and be faster than custom-made solutions.

Fortunately, that’s what we’re here to talk about!

Enter the CSS Custom Highlight API

The CSS Custom Highlight API is a new W3C specification (currently in Working Draft status) that makes it possible to style arbitrary text ranges from JavaScript! The approach here is very similar to the user text selection technique we reviewed earlier. It gives developers a way to create arbitrary ranges, from JavaScript, and then style them using CSS.

Creating Ranges of Text

The first step is to create the ranges of text that you want to highlight. which can be done using a Range in JavaScript. So, like we did when setting the current selection:

const range = new Range(); range.setStart(parentNode, startOffset); range.setEnd(parentNode, endOffset);

It’s worth noting that the setStart and setEnd methods work differently if the node passed as the first argument is a text node or not. For text nodes, the offset corresponds to the number of characters within the node. For other nodes, the offset corresponds to the number of child nodes within the parent node.

Also worth noting is that setStart and setEnd aren’t the only ways to describe where a range starts and ends. Take a look at the other methods available on the Range class to see other options.

Creating Highlights

The second step consists in creating Highlight objects for the ranges created in that last step. A Highlight object can receive one or more Ranges. So if you want to highlight a bunch of pieces of text in exactly the same way, you should probably create a single Highlight object and initialize it with all of the Ranges that correspond to these pieces of text.

const highlight = new Highlight(range1, range2, ..., rangeN);

But you can also create as many Highlight objects as you need. For example, if you are building a collaborative text editor where each user gets a different text color, then you can create one Highlight object per user. Each object can then be styled differently, as we’ll see next.

Registering Highlights

Now Highlight objects on their own don’t do anything. They first need to be registered in what is called the highlight registry. This is done by using the CSS Highlights API. The registry works like a map where you can register new highlights by giving them names, as well as remove highlights (or even clear the entire registry).

Here is how to register a single highlight.

CSS.highlights.set('my-custom-highlight', highlight);

Where my-custom-highlight is the name of your choosing and highlight is a Highlight object created in the last step.

Styling Highlights

The final step is to actually style the registered highlights. This is done with the new CSS ::highlight() pseudo-element, using the name you chose when registering the Highlight object (which is my-custom-highlight in our example above).

::highlight(my-custom-highlight) {   background-color: yellow;   color: black; }

It’s worth noting that, just like ::selection, a subset of CSS properties only can be used with the ::highlight() pseudo-element:

Updating Highlights

There are multiple ways to update highlighted text on the page.

For example, you can clear the highlight registry altogether with CSS.highlights.clear() and then start again from the beginning. Or, you can also update the underlying ranges without having to re-create any of the objects all. For this, use the range.setStart and range.setEnd methods again (or any of the other Range methods) and the highlights will be re-painted by the browser.

But, the Highlight object works like a JavaScript Set, so this means you also add new Range objects to an existing Highlight with highlight.add(newRange) or remove a Range with highlight.delete(existingRange).

Third, you can also add or remove specific Highlight objects from the CSS.highlights registry. Since this API works like a JavaScript Map, you can set and delete to update the currently registered Highlights.

Browser Support

The specification for the CSS Custom Highlight API is relatively new and its implementation in browsers is still incomplete. So, although this is going to be a very useful addition to the web platform, it’s not quite ready for production use.

The Microsoft Edge team is implementing the CSS Custom Highlight API in Chromium at the moment. In fact, the feature can already be used in Canary versions right now by enabling the Experimental Web Platform features flag (under about:flags). There is currently no firm plan as to when the feature will ship in Chrome, Edge, and other Chromium-based browsers, but it’s getting very close.

The API is also supported in Safari 99+ but behind an experiment flag (Develop → Experimental Features → Highlight API), and the interface is a little bit different in that it uses StaticRange objects instead.

Firefox does not support the API yet, though you can read Mozilla’s position about it for more information.


Speaking of Microsoft Edge, they have a demo set up where you can take the CSS Custom Highlight API for a test drive. But Before trying the demo, be sure you’re using either Chrome or Edge Canary with the Experimental Web Platform features flag in the about:flags page.

/button View the demo

The demo uses the Custom Highlight API to highlight ranges of text in the page based on what you type in the search field at the top of the page.

After the page loads, JavaScript code retrieves all the text nodes in the page (using a TreeWalker) and when the user types in the search field, the code iterates over these nodes until it finds matches. Those matches are then used to create Range objects, which are then highlighted with the Custom Highlight API.

Closing Thoughts

So, is this new browser-provided highlighting API really worth it? Totally!

For one, even if the CSS Custom Highlight API may seem a bit complicated at first (i.e. having to create ranges, then highlights, then registering them, and finally styling them), it’s still way simpler than having to create new DOM elements and insert them in the right places.

More importantly, browser engines can style these ranges very, very fast.

The reason only a subset of CSS properties is allowed to be used with the ::highlight() pseudo-element is that the subset only contains properties that can be applied by the browser very effectively without having to recreate the layout of the page. Highlighting ranges of text by inserting new DOM elements in the page around them requires the engine to do much more work.

But don’t take my word for it. Fernando Fiori, who worked on the API, created this nice performance comparison demo. On my computer, the CSS Custom Highlight API performs on average 5✕ as fast as the DOM-based highlighting.

With Chromium and Safari experimental support already here, we’re getting close to something that can be used in production. I can’t wait for browsers to support the Custom Highlight API consistently and see what features this will unlock!

CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web originally published on CSS-Tricks. You should get the newsletter.


, , , , ,

Multi-Value CSS Properties With Optional Custom Property Values

Imagine you have an element with a multi-value CSS property, such as transform: optional custom property values:

.el {   transform: translate(100px) scale(1.5) skew(5deg); }

Now imagine you don’t always want all the transform values to be applied, so some are optional. You might think of CSS optional custom property values:

.el {   /*         |-- default ---| |-- optional --| */   transform: translate(100px) var(--transform); }

But surprisingly using optional custom property values like this does not work as intended. If the --transform variable is not defined the whole property will not be applied. I’ve got a little “trick” to fix this and it looks like this:

.el {   transform: translate(100px) var(--transform, ); }

Notice the difference? There is a fallback defined in there that is set to an empty value: (, )

That’s the trick, and it’s very useful! Here’s what the specification has to say:

In an exception to the usual comma elision rules, which require commas to be omitted when they’re not separating values, a bare comma, with nothing following it, must be treated as valid in var(), indicating an empty fallback value.

This is somewhat spiritually related to the The CSS Custom Property Toggle Trick that takes advantage of a custom property having the value of an empty space.


Like I said, this is useful and works for any multi-value CSS property. The following demo shows it using text-shadow, background, and filter in addition to the transform example we just discussed.

See the Pen CSS var – Fallback To Nothing by Yair Even Or (@vsync) on CodePen.

Some properties that accept multiple values, like text-shadow, require special treatment because they only work with a comma delimiter. In those cases, when the CSS custom property is defined, you (as the code author) know it is only to be used in a situation where a value is already defined where the custom property is used. Then a comma should be inserted directly in the custom property before the first value, like this:

--text-shadow: ,0 0 5px black;

This, of course, inhibits the ability to use this variable in places where it’s the only value of some property. That can be solved, though, by creating “layers” of variables for abstraction purposes, i.e. the custom property is pointing to lower level custom properties.

Beware of Sass compiler

While exploring this trick, uncovered a bug in the Sass compiler that strips away the empty value (,) fallback, which goes against the spec. I’ve reported the bug and hope it will be fixed up soon.

As a temporary workaround, a fallback that causes no rendering can be used, such as:

transform: translate(100px) var(--transform, scale(1));

Multi-Value CSS Properties With Optional Custom Property Values originally published on CSS-Tricks. You should get the newsletter and become a supporter.


, , , , ,

How to Build Your First Custom Svelte Transition

The Svelte transition API provides a first-class way to animate your components when they enter or leave the document, including custom Svelte transitions. By default, the transition directive uses CSS animations, which generally offer better performance and allow the browser’s main thread to remain unblocked. The API is as simple as this: <element transition:transitionFunction />. You can also specify in or out directives which are uni-directional transitions, only running when the element is mounted or unmounted.

An animated example of a custom Svelte transition showing a to do list. An item is typed and animated into the list of items when entered. Clicking a done button animates the item out of view.
Example of a working Svelte transition (jump to demo)

Svelte offers a runtime svelte/transition package that ships with seven prepackaged Svelte transition functions, all of which can be dropped in and tweaked to your heart’s desire. Pairing this with the svelte/easing package, allows for a wide swath of interactions, without writing any of the transition code yourself. Play around with different transitions and easing functions to get a feel for what is possible.

Looking for instructions on how to get started with Svelte? We have a solid overview for you to check out.

The Svelte Custom Transition API

If you need even more control than what the Svelte Transition API offers out of the box, Svelte permits you to specify your own custom transition function, as long as you adhere to a few conventions. From the docs, here’s what the custom transition API looks like:

transition = (node: HTMLElement, params: any) => {   delay?: number,   duration?: number,   easing?: (t: number) => number,   css?: (t: number, u: number) => string,   tick?: (t: number, u: number) => void } 

Let’s break it down. A transition function takes a reference to the DOM node where the transition directive is used and returns an object with some parameters that control the animation and, most importantly, a css or tick function.

The css function’s job is to return a string of CSS that describes the animation, typically including some kind of transform or opacity change. Alternatively, you can opt to return a tick function, which lets you control every aspect of the animation with the power JavaScript, but pays a performance penalty since this type of transition does not use CSS animations.

Both the css and tick functions take two parameters called (t, u) by convention. t is a decimal number that travels from 0.00 to 1.00 while the element is entering the DOM and from 1.00 back to 0.00 when the element is leaving. The u parameter is the inverse of t or 1 - t at any given moment. For example, if you return a string of transform: scale($ {t}), your element would smoothly animate from 0 to 1 on enter, and vice versa on exit.

These concepts may seem a bit abstract, so let’s solidify them by building our own custom Svelte transition!

Building your first custom Svelte transition

First, let’s set up some boilerplate that allows us to toggle an element’s existence in the DOM using a Svelte #if block. Remember, Svelte transitions only run when an element is actually leaving or entering the DOM.

<script>   let showing = true </script>  <label for="showing">   Showing </label> <input id="showing" type="checkbox" bind:checked={showing} />  {#if showing}   <h1>Hello custom transition!</h1> {/if}

You should be able to toggle the checkbox and see our element starkly appear and disappear in place.

Next, let’s set up our custom Svelte transition function and get it wired up to our element.

<script>   let showing = true   // Custom transition function   function whoosh(node) {     console.log(node)   } </script>  <label for="showing">   Showing </label> <input id="showing" type="checkbox" bind:checked={showing} />  {#if showing}   <h1 transition:whoosh>Hello custom transition!</h1> {/if}

Now, if you toggle the checkbox, you will see the <h1> element logged to the console. This proves we have the custom transition connected properly! We won’t actually use the DOM node in our example, but it’s often useful to have access to the element to reference its current styles or dimensions.

For our element to do any animation at all, we need to return an object that contains a css (or tick) function. Let’s have our css function return a single line of CSS that scales our element. We’ll also return a duration property that controls how long the animation takes.

<script>   function swoop() {     return {       duration: 1000,       css: () => `transform: scale(.5)`     }   }   let showing = true </script>  <!-- markup -->

We’ve got something moving! You will notice our element jumps straight to .5 scale when toggling the checkbox. This is something, but it would feel much better if it smoothly transitioned. That’s where the (t, u) parameters come in.

<script>   function swoop() {     return {       duration: 1000,       css: (t) => `transform: scale($ {t})`     }   }   let showing = true </script>  <!-- markup -->

Now we are talking! Remember, t rolls smoothly from 0.00 to 1.00 when an element enters, and vice versa when it leaves. This allows us to achieve the smooth effect we want. In fact, what we just wrote is essentially the built-in scale transition from the svelte/transition package.

Let’s get a little bit fancier. To live up to our custom Svelte transition’s namesake, swoop, let’s add a translateX to our transform, so that our element zooms in and out from the side.

I want to challenge you to attempt the implementation first before we continue. Trust me, it will be fun! Assume that we want to translate to 100% when the element is leaving and back to 0% when it enters.


How did it go? Want to compare answers?

Here’s what I got:

css: (t, u) => `transform: scale($ {t}) translateX($ {u * 100}%);`

It’s okay if you have something different! Let me break down what I did.

The key thing here is the usage of the second parameter in the css function. If we think about our animation while the element is entering the screen, we want to end up at scale(1) translateX(0%), so we can’t use unaltered t for both the scale and the transform. This is the convenience behind the u parameter — it is the inverse of t at any given moment, so we know it will be 0 when t is 1! I then multiplied u by 100 to get the percentage value and tacked on the % sign at the end.

Learning the interplay between t and u is an important piece of the custom transition puzzle in Svelte. These two parameters enable a world of dynamism for your animations; they can be divided, multiplied, twisted, or contorted into whatever needs you have.

Let’s slap my favorite svelte/easing function on our transition and call it a day:

<script>   import { elasticOut } from 'svelte/easing'   function swoop() {     return {       duration: 1000,       easing: elasticOut,       css: (t, u) => `transform: scale($ {t}) translateX($ {u * 100}%)`     }   }   let showing = true </script>  <label for="showing">   Showing </label> <input id="showing" type="checkbox" bind:checked={showing} />  {#if showing}   <h1 transition:swoop>Hello custom transition!</h1> {/if}

Wrapping up

Congratulations! You can now build a custom Svelte transition function. We have only scratched the surface of what is possible but I hope you feel equipped with the tools to explore even further. I would highly recommend reading the docs and going through the official tutorial to gain even more familiarity.

How to Build Your First Custom Svelte Transition originally published on CSS-Tricks. You should get the newsletter and become a supporter.


, , , ,

How to Use Native Custom Fields in WordPress (and 5 Useful Examples)

Custom Fields in WordPress are arbitrary bits of data that you can apply to Posts, Pages, and Custom Post Types in WordPress. Metadata, as it were, in the form of key/value pairs. For example:

  • Key: subtitle / Value: They are more than they are cracked up to be
  • Key: header_color_override / Value: #e52e05
  • Key: property_url / Value: https://example.com/123

WordPress has their own documentation of this feature, so I’m not trying to replicate that. I’d just like to show you essentially what custom fields in WordPress are, how they work, how to use them, and some use cases from my own personal experience.

Table of Contents

How to Add/Edit/Remove Custom Fields in WordPress

The UI for Custom Fields in WordPress looks like this:

Showing that Custom Fields in WordPress appear below the content area of the block editor in the admin user interface.

If you don’t see it, it’s possible you may need to go under the three-dots menu, Preferences, and then find the toggle for Custom Fields and turn it on.

Showing the option to enable Custom Fields in WordPresss in the Block Editor Preferences settings. It is at the first toggle beneath the Additional settings.
The UI forces you to refresh the page when turning this feature on and off.

To add a Custom Field, type in the Key (labeled “Name”) and Value, then click Add Custom Field.

Showing a Custom Field in WordPress with a name of favorite_food and a value of burrito. There a button below the name input to add the custom field.

After it’s added, you can delete or update it from buttons below the Key/Name:

Showing a Custom Field in WordPress with a name of favorite_food and a value of burrito. There are two buttons below the name to delete or update the custom field.

After you have used Custom Fields, the keys will form into a dropdown menu for easier selection.

Showing the dropdown menu that opens when clocking on the Name field of a custom field in WordPress, allowing you to select an existing custom field.

Why use Custom Fields?

Custom Fields, along with Custom Post Types, are what make WordPress a CMS out-of-the-box rather than being limited to a simple blogging platform.

Here on CSS-Tricks, believe it or not, we use over 100 Custom Fields to do different things on this site. We tend to reach for them for relatively simple things, and it’s nice as it’s a core feature of WordPress that will continue to work forever without too much worry about compatibility or awkward technical debt.

The big idea is to open up templating possibilities. Imagine you have a page for real estate listings that has:

  • Address
  • Listing price
  • Bedrooms
  • Bathrooms
  • etc.

With custom fields, you have all that information available as discreet little chunks of data that you can echo (i.e. display) into a page template wherever you need to. That’s much more flexible than having all that data in the post content itself, even with the Block Editor.

WordPress Custom Fields use case examples

Custom Fields in WordPress can be used for so many different things! But let’s look at a five practical use cases that we have implemented here on CSS-Tricks.

1. Display additional information

Say you are publishing a video and want to have the running time of the video available to display. That’s as easy as saving the running_time as a Custom Field and displaying it wherever you’d like:

A side-by-side showing a published post on the left with the running time of a video circled in red, and the WordPress admin on the right with the running time custom field circled in the block editor showing the exact same information that is published in the post.
Note other Custom Fields in use here, like the youtube field, which we have so that we can output where the

2. Hide/Show Different Content/Features

Let’s say you want to be able to collapse the Comments area sometimes on different blog posts. You could set a custom field called should_toggle_comments and set a value of true. That’s what we do here on CSS-Tricks. In our comments.php template, we output a <ol> of all the comments, but if this custom field is there, we wrap the whole thing in a <details> element, collapsing it by default:

<?php if (get_post_meta($ post->ID, 'should_toggle_comments', true)) { ?> <details class="open-all-comments">   <summary>Toggle All Comments (there are a lot!)</summary>   <?php } ?>      <ol class="commentlist" id="commentlist">       <?php wp_list_comments('type=comment&avatar_size=512&callback=csstricks_comment'); ?>     </ol>    <?php if (get_post_meta($ post->ID, 'should_toggle_comments', true)) { ?>   </details> <?php } ?>

3. Special pull quotes

Say you have a special Category archive that displays a group of posts that contain the same category, then use a custom template for that category, like category-fancypants.php. Maybe you yank out a custom quote from each article as a custom field called main-pullquote:

<blockquote>   <?php     echo get_post_meta($ post->ID, 'main-pullquote', true);   ?> </blockquote>

That’s what we do for our annual end-of-year series:

A side by side showing the the main pull quote custom field in WordPress circled in red, and the category archive on the right with a red arrow pointing to the corresponding pull-quote that displays on the page.

4. Customize an RSS feed

We build a couple of totally custom RSS feeds here on CSS-Tricks that are different from what WordPress offers out of the box — one for videos and one for newsletters. The video feed in particular relies on some WordPress Custom Fields to output special data that is required to make the feed work as a feed for our video podcast.

Side by side showing the rss videos template in code on the left with the custom field part circled in red, and the RSS feed open in the browser on the right with an arrow pointing to where the corresponding code renders as the video enclosure.
The location of the video and the duration are both kept in custom fields

5. Hide/Show Author

Our sponsored posts here on CSS-Tricks are sometimes written to sound largely like an announcement from a company. They were written like that on purpose and likely have been written by multiple people by the time its actually published. A post like that doesn’t really need to be “by” someone. But sometimes sponsored posts are definitely authored by a specific person, even sometimes in the first person, which would be weird without showing a byline. That’s why we use a showSponsorAuthor custom field, to show that author if we need it.

<div class="sponsored-post-byline">   ❥ Sponsored   <?php if (get_post_meta($ post->ID, 'showSponsorAuthor', true)) { ?>     (Written by <?php the_author(); ?>)   <?php } ?> </div>

Above is a part of a template. We always mark a sponsored post as sponsored in the byline (example), but only optionally do we visually show the author (example), controlled by a custom field.

The APIs for displaying Custom Fields in WordPress

Most commonly, you’re looking to display the value of a single field:

<?php echo get_post_meta($ post->ID, 'mood', true); ?>

That true at the end there means “give me a single value,” meaning that even if there are multiple custom fields with the same name, you’ll only get one. To get multiple of the same name, use false, like:

<?php $ songs = get_post_meta($ post->ID, 'songs', false); ?> <h3>This post inspired by:</h3> <ul>   <?php foreach($ songs as $ song) {     echo '<li>'.$ song.'</li>';   } ?> </ul>

If you want to just dump them all out (probably mostly useful for debugging), you can do that like this:

<?php the_meta(); ?>

Although, note that this skips custom fields that start with an underscore (_), so you might consider this approach instead.

Querying for Custom Fields in WordPress

Say you wanted to query for all posts that have some particular custom field. That’s possible!

<?php $ the_query = new WP_Query(array(   'meta_key' => 'example_field_name'   'meta_value' => 'example_field_value' // as a string!  ));  if ($ the_query->have_posts()) {   while ($ the_query->have_posts()) {     $ the_query->the_post();     echo '<div>' . get_the_title() . '</div>';   } }  wp_reset_postdata();

The example above will run a query for posts that have both a custom field of example_field_name and where that field has a value of example_field_value. You could do either/or.

There is a lot more you can do here. You can use comparisons, you can get the values as numbers, and even query for multiple fields at once. We detail all that in Custom Loop/Query Based on Custom Fields.

Limiting Custom Fields in the Name dropdown

The UI dropdown for existing Custom Fields in WordPress is capped at something like 30 fields. So, if you have more than 100 different keys, the dropdown menu will look arbitrarily cut off. You can increase that number with a filter in functions.php or a plugin:

function customfield_limit_increase( $ limit ) {   $ limit = 150;   return $ limit; } add_filter( 'postmeta_form_limit', 'customfield_limit_increase' );

Any other Block Editor concerns?

The main concern is when you can’t see the custom fields UI at all. We covered how to turn it back on (because it might default to off), so always check that. The Advanced Custom Fields plugin also turns it off, so if you’re using that plugin, note there is a line below to help turn it back on (in the case you use both, as we do).

I’m not sure there is a standard way to show the value of a custom field within a block in the block editor either. If you know of a clear way, leave a comment!

Relationship to Advanced Custom Fields

The UI for native Custom Fields in WordPress is pretty… underserved. It’s not fancy, it’s got rough edges (we find that Custom Fields have a weird way of duplicating themselves on multiple post saves, for example). It doesn’t seem like Custom Fields, while native, are a particularly first-class feature of WordPress.

Advanced Custom Fields (ACF) changes that in a big way. The spirit remains the same: attach data to content. But rather than the simple string-based key-value interface that we’ve detailed, you essentially model the data with different types and it builds really nice custom UI for you to use to input that data, even integrating directly with the Block Editor.

Imagine a podcast website where each post is an individual episode. The Block Editor might be nice for written content about the episode, but probably not a good idea for all of the metadata that goes with it. The list of guests, the duration, the location of the MP3 file, the sponsor, time jump links, etc. Custom Fields are great for that, but since there are so many, you’ll be well served by Advanced Custom Fields here instead of using native Custom Fields in WordPress. Here’s a setup example of what you get as we do on the ShopTalk Show podcast:

Side by side showing the settings for custom fields in the Advanced Custom Fields plugin on the left, and those custom fields displayed on the right in the  WordPress Block Editor of a new post.

ACF, probably in an attempt to encourage using it directly and not confusing people with the native Custom Fields interface, removes the native Custom Fields interface. If you’re like us and use both types of fields, you’ll need to bring the native Custom Fields UI back to the post editor with a filter that ACF provides:

add_filter('acf/settings/remove_wp_meta_box', '__return_false');

If you use native Custom Fields in WordPress at all, you’ll want that in your functions.php file or a functionality plugin.

Note for plugin developers

Use the underscore hiding technique.

Some plugins use the Custom Fields API as a place to store post-specific data. I think that’s OK, but I’d like to implore plugin developers to always use underscore-and-plugin-prefixed custom field names when doing so.

When custom fields start with an underscore, they aren’t shown in the UI. Meaning for those of us who use the Custom Fields UI directly, it’s not cluttered with fields created by other plugins. The exception, of course, is if you intend users to be able to control what the plugin does with the Custom Field values. In that case, fine, leave those few non-underscore-prefixed fields.

_bobs_plugin_internal_value_1 // Hidden in UI _bobs_plugin_internal_value_2 // Hidden in UI bobs_plugin_config  // Shows in UI  _adrians_plugin_internal_value_1  // Hidden in UI _adrians_plugin_internal_value_2 // Hidden in UI

More examples using Custom Fields in WordPress

What do you use them for?

Do you use Custom Fields in WordPress? I’m particularly curious about native custom field usage.

How to Use Native Custom Fields in WordPress (and 5 Useful Examples) originally published on CSS-Tricks. You should get the newsletter and become a supporter.


, , , , ,

Notes on Josh Comeau’s Custom CSS Reset

We recently talked with Elad Shechter on his new CSS reset, and shortly after that Josh Comeau blogged his.

We’re in something of a new era of CSS resets where… you kind of don’t need one? There isn’t that many major differences between browsers on default styling, and by the time you’re off and running styling stuff, you’ve probably steamrolled things into place. And so perhaps “modern” CSS resets are more of a collection of opinionated default styles that do useful things that you want on all your new projects because, well, that’s how you roll.

Looking through Josh’s choices, that’s what it seems like to me: a collection of things that aren’t particularly opinionated about design, but assist the design by being things that pretty much any project will want.

I’m gonna go through it and toss out 🔥 flamin’ hot opinions.

*, *::before, *::after {   box-sizing: border-box; }

Heck yes. We used to consider this a global holiday ’round here. Although, with more and more layout being handled by grid and flexbox, I’m feeling like this is slightly less useful these days. When you’re setting up a layout with fr units and flexin’ stuff, the box-sizing model doesn’t come into play all that much, even when padding and border are involved. But hey, I still prefer it to be in place. I do think if it goes into a CSS reset it should use the inheritance model though, as it’s easier to undo on a component that way.

* {   margin: 0; }

This is basically why the CSS-Tricks logo “star” exists. I used to love this little snippet in my CSS resets. There was a period where it started to feel heavy-handed, but I think I’m back to liking it. I like how explicit you have to be when applying any margin at all. Personally, I’d rock padding: 0; too, as list elements tend to have some padding pushing them around. If you’re nuking spacing stuff, may as well nuke it all.

html, body {   height: 100%; }

Probably a good plan. Josh says “Allow percentage-based heights in the application,” which I can’t say comes up much in my day-today, but what it does is stuff like the body background not filling the space the way you might expect it to.

Too bad body { height: 100vh; } isn’t enough here, but I feel like that’s not as reliable for some reason I can’t think of right now. Maybe something to do with the footer navigation in iOS Safari?

body {   line-height: 1.5;   -webkit-font-smoothing: antialiased; }

I can’t get into the -webkit-font-smoothing: antialiased; thing. I think it tends to make type dramatically thin and I don’t love it. I don’t mind it as a tool, but I wouldn’t globally apply it on all my projects.

I also generally like to put global typographic sizing stuff on the html selector instead, just because the “root” part of rem implies the <html> element — not the <body> — and I like sizing stuff in rem and then adjusting the root font-size at the root level in media queries.

That 1.5 value feels like a good default line-height (more of a 1.4 guy myself, but I’d rather go up than down). But as soon as it’s set, I feel magnetically pulled into reducing it for heading elements where it’s always too much. That could happen via h1, h2, h3 kinda selectors (maybe h4h6 don’t need it), but Josh has some CSS trickery at work with this snippet that didn’t make it into the final reset:

* {   line-height: calc(1em + 0.5rem); }

That’s clever in how the 0.5rem goes a long way for small type, but isn’t as big of an influence for large type. I could see trying that on a greenfield project. Prior art here is by Jesús Ricarte in “Using calc to figure out optimal line-height.”

img, picture, video, canvas, svg {   display: block;   max-width: 100%; }

Good move for a CSS reset. The block display type there prevents those annoying line-height gaps that always kill me. And you almost never want any of these media blocks to be wider than the parent. I somehow don’t think picture is necessary, though, as it’s not really a style-able block? Could be wrong. I’d probably toss iframe and object in there as well.

p, h1, h2, h3, h4, h5, h6 {   overflow-wrap: break-word; }

Good move for sure. It’s bad news when a long word (like a URL) forces an element wide and borks a layout. I tend to chuck this on something — like article or .text-content or something — and let it cascade into that whole area (which would also catch text that happens to be contained in an improper element), but I don’t mind seeing it on specific text elements.

If doing that, you probably wanna chuck li, dl, dt, blockquote on that chain. Despite having attempted to research this several times (here’s a playground), I still don’t 100% know what the right cocktail of line-wrapping properties is best to use. There is word-break: break-word; that I think is basically the same thing. And I think it’s generally best to use hyphens: auto; too… right??

#root, #__next {   isolation: isolate; }

I don’t quite understand what’s happening here. I get that this is a React/Next thing where you mount the app to these roots, and I get that it makes a stacking context, I just don’t get why it’s specifically useful to have that stacking context at this level. At the same time, I also don’t see any particular problem with it.

All in all — pretty cool! I always enjoy seeing what other people use (and go so far as to suggest) for CSS resets.

Notes on Josh Comeau’s Custom CSS Reset originally published on CSS-Tricks. You should get the newsletter and become a supporter.


, , , ,

Honor prefers-color-scheme in the CSS Paint API with Custom Properties

One of the coolest things I’ve been messing with in the last couple years is the CSS Paint API. I love it. I did a talk on it, and made a little gallery of my own paint worklets. The other cool thing is the prefers-color-scheme media query and how you can use it to adapt to a user’s preference for light or dark modes.

Recently, I found out that I can combine both of these really cool things with CSS custom properties in such a way that a paint worklet’s appearance can be tailored to fit the user’s preferred color scheme!

Setting the stage

I’ve been overdue for a website overhaul, and I decided to go with a Final Fantasy II theme. My first order of business was to make a paint worklet that was a randomly generated Final Fantasy-style landscape I named overworld.js:

An 8-bit illustration landscape of a forest with scattered pine trees and a jagged river running through the green land.
A randomly generated 8-bit style landscape, made possible by the CSS Paint API!

It could use a bit more dressing up—and that’s certainly on the agenda—but this here is a damn good start!

After I finished the paint worklet, I went on to work on other parts of the website, such as a theme switcher for light and dark modes. It was then that I realized that the paint worklet wasn’t adapting to these preferences. This might normally be a huge pain, but with CSS custom properties, I realized I could adapt the paint worklet’s rendering logic to a user’s preferred color scheme with relative ease!

Setting up the custom properties for the paint worklet

The state of CSS these days is pretty dope, and CSS custom properties are one such example of aforementioned dopeness. To make sure both the Paint API and custom properties features are supported, you do a little feature check like this:

const paintAPISupported = "registerProperty" in window.CSS && "paintWorklet" in window.CSS`

The first step is to define your custom properties, which involves the CSS.registerProperty method. That looks something like this:

CSS.registerProperty({   name,             // The name of the property   syntax,           // The syntax (e.g., <number>, <color>, etc.)   inherits,         // Whether the value can be inherited by other properties   initialValue      // The default value });

Custom properties are the best part of using the Paint API, as these values are specified in CSS, but readable in the paint worklet context. This gives developers a super convenient way to control how a paint worklet is rendered—entirely in CSS.

For the overworld.js paint worklet, the custom properties are used to define the colors for various parts of the randomly generated landscape—the grass and trees, the river, the river banks, and so on. Those color defaults are for the light mode color scheme.

The way I register these properties is to set up everything in an object that I call with Object.entries and then loop over the entries. In the case of my overworld.js paint worklet, that looked like this:

// Specify the paint worklet's custom properties const properties = {   "--overworld-grass-green-color": {     syntax: "<color>",     initialValue: "#58ab1d"   },   "--overworld-dark-rock-color": {     syntax: "<color>",     initialValue: "#a15d14"   },   "--overworld-light-rock-color": {     syntax: "<color>",     initialValue: "#eba640"   },   "--overworld-river-blue-color": {     syntax: "<color>",     initialValue: "#75b9fd"   },   "--overworld-light-river-blue-color": {     syntax: "<color>",     initialValue: "#c8e3fe"   } };  // Register the properties Object.entries(properties).forEach(([name, { syntax, initialValue }]) => {   CSS.registerProperty({     name,     syntax,     inherits: false,     initialValue   }); });  // Register the paint worklet CSS.paintWorklet.addModule("/worklets/overworld.js");

Because every property sets an initial value, you don’t have to specify any custom properties when you call the paint worklet later. However, because the default values for these properties can be overridden, they can be adjusted when users express a preference for a color scheme.

Adapting to a user’s preferred color scheme

The website refresh I’m working on has a settings menu that’s accessible from the site’s main navigation. From there, users can adjust a number of preferences, including their preferred color scheme:

The color scheme setting cycles through three options:

  • System
  • Light
  • Dark

“System” defaults to whatever the user has specified in their operating system’s settings. The last two options override the user’s operating system-level setting by setting a light or dark class on the <html> element, but in the absence of an explicit, the “System” setting relies on whatever is specified in the prefers-color-scheme media queries.

The hinge for this override depends on CSS variables:

/* Kicks in if the user's site-level setting is dark mode */ html.dark {    /* (I'm so good at naming colors) */   --pink: #cb86fc;   --firion-red: #bb4135;   --firion-blue: #5357fb;   --grass-green: #3a6b1a;   --light-rock: #ce9141;   --dark-rock: #784517;   --river-blue: #69a3dc;   --light-river-blue: #b1c7dd;   --menu-blue: #1c1f82;   --black: #000;   --white: #dedede;   --true-black: #000;   --grey: #959595; }  /* Kicks in if the user's system setting is dark mode */ @media screen and (prefers-color-scheme: dark) {   html {     --pink: #cb86fc;     --firion-red: #bb4135;     --firion-blue: #5357fb;     --grass-green: #3a6b1a;     --light-rock: #ce9141;     --dark-rock: #784517;     --river-blue: #69a3dc;     --light-river-blue: #b1c7dd;     --menu-blue: #1c1f82;     --black: #000;     --white: #dedede;     --true-black: #000;     --grey: #959595;   } }  /* Kicks in if the user's site-level setting is light mode */ html.light {   --pink: #fd7ed0;   --firion-red: #bb4135;   --firion-blue: #5357fb;   --grass-green: #58ab1d;   --dark-rock: #a15d14;   --light-rock: #eba640;   --river-blue: #75b9fd;   --light-river-blue: #c8e3fe;   --menu-blue: #252aad;   --black: #0d1b2a;   --white: #fff;   --true-black: #000;   --grey: #959595; }  /* Kicks in if the user's system setting is light mode */ @media screen and (prefers-color-scheme: light) {   html {     --pink: #fd7ed0;     --firion-red: #bb4135;     --firion-blue: #5357fb;     --grass-green: #58ab1d;     --dark-rock: #a15d14;     --light-rock: #eba640;     --river-blue: #75b9fd;     --light-river-blue: #c8e3fe;     --menu-blue: #252aad;     --black: #0d1b2a;     --white: #fff;     --true-black: #000;     --grey: #959595;   } }

It’s repetitive—and I’m sure someone out there knows a better way—but it gets the job done. Regardless of the user’s explicit site-level preference, or their underlying system preference, the page ends up being reliably rendered in the appropriate color scheme.

Setting custom properties on the paint worklet

If the Paint API is supported, a tiny inline script in the document <head> applies a paint-api class to the <html> element.

/* The main content backdrop rendered at a max-width of 64rem.    We don't want to waste CPU time if users can't see the    background behind the content area, so we only allow it to    render when the screen is 64rem (1024px) or wider. */ @media screen and (min-width: 64rem) {   .paint-api .backdrop {     background-image: paint(overworld);     position: fixed;     top: 0;     left: 0;     width: 100%;     height: 100%;     z-index: -1;      /* These oh-so-well-chosen property names refer to the        theme-driven CSS variables that vary according to        the user's preferred color scheme! */     --overworld-grass-green-color: var(--grass-green);     --overworld-dark-rock-color: var(--dark-rock);     --overworld-light-rock-color: var(--light-rock);     --overworld-river-blue-color: var(--river-blue);     --overworld-light-river-blue-color: var(--light-river-blue);   } }

There’s some weirdness here for sure. For some reason, that may or may not be the case later on—but is at least the case as I write this—you can’t render a paint worklet’s output directly on the <body> element.

Plus, because some pages can be quite tall, I don’t want the entire page’s background to be filled with randomly generated (and thus potentially expensive) artwork. To get around this, I render the paint worklet in an element that uses fixed positioning that follows the user as they scroll down, and occupies the entire viewport.

All quirks aside, the magic here is that the custom properties for the paint worklet are based on the user’s system—or site-level—color scheme preference because the CSS variables align with that preference. In the case of the overworld paint worklet, that means I can adjust its output to align with the user’s preferred color scheme!

Not bad! But this isn’t even that inventive of a way to control how paint worklets render. If I wanted, I could add some extra details that would only appear in a specific color scheme, or do other things to radically change the rendering or add little easter eggs. While I learned a lot this year, I think this intersection of APIs was one of my favorites.


, , , ,

Strut Your Stuff With a Custom Scrollbar

The first time I had my breath taken away by a humble scrollbar was on this very site. When CSS-Tricks v17 rolled out with its FAT CHONKY BOI, my jaw dropped.

I didn’t know you could do that on a professional site. And it would look… good?!

I appreciated so much about it—the gentle gradient, the reckless rounding, the blended background, the sheer satisfying CHONKINESS that dares you to click and wiggle it up and down just to marvel in its tactile heft. How bold! How avant-garde! What sheer, accessible, gracefully degrading delight!

Of course, because fun doesn’t last, the current CSS Tricks scrollbar is more grown-up and muted, light gray on black. Still on brand, still flexing subtle gradient muscle, but not so distracting that it detracts from the reading experience. In our ultra-functional world of MVPs and 80/20 rules, maximizing efficiency and hacking productivity, custom scrollbars evince something about craftsmanship. It says with no words what you can’t in a hundred.

Thanks to some standardization (with more on the way), the API is simple: seven pseudo-elements and eleven pseudo-classes that target (almost) every imaginable component and state of the trusty (and often overlooked) scrollbar. Sounds like a lot, but you can go very far with just three of them:

body::-webkit-scrollbar {   /* required - the "base" of the bar - mostly for setting width */ }   body::-webkit-scrollbar-track {   /* the "track" of the bar - great for customizing "background" colors */ }   body::-webkit-scrollbar-thumb {   /* the actual draggable element, the star of the show! */ }

From here, it works like any other selected element, so bring your full bag of single div CSS tricks! Media queries work! Background gradients work! Transparency works! Margins with all manner of CSS units work! (Not everything works… I’d love to style cursor on my scrollbars for that authentic GeoCities look). I tried it out on my site with Lea Verou’s stash of CSS background gradients (my stash of stashes is here) and ended up with an atrocious combo of stripy barber pole (💈) for the thumb element and transparent hearts for the track. But it was most definitely mine—so much so that people have taken to calling it the “swyxbar” when I implemented a subtler version at work.

Every front-end developer should take this too far at least once in their careers. Live dangerously! Break the rules! Rage against the user agent! And maybe don’t ship scrollbars that break user expectations on a mass-market product (like Google Wave did back in the day)!


, , ,

The surprising behavior of !important in CSS custom property values

Huh! I did not realize that CSS custom properties had their own resolution behavior for how !important works in their values. Uh, despite writing a guide about them. 😬 But hey it’s now updated.

Stefan Judis documents it clearly. The deal is that !important is not at all used in the final value. So:

div {   --color: red !important;   color: var(--color);   color: yellow; }

It kinda feels like red should win, but because !important is ultimately stripped from the custom property value, yellow wins it out. And it’s not because the color declaration comes last — if color: red !important; was the first declaration, then red would win.

But it’s not like !important is just stripped and ignored; it’s used in a scoped way, affecting which custom property value wins. But then !important is gone when that value is applied. Stefan’s example:

div {   /*     `!important` overrules the     other `--color` definitions   */   --color: red !important;   color: var(--color); }  .class {   --color: blue; }  #id {   --color: yellow; }

This feels weird (to me) as you’d think yellow declared on #id would win because it has the highest specificity. But that’s the deal with the scoped behavior — !important makes red the winner, then is applied to the color as such.

To Shared LinkPermalink on CSS-Tricks


, , , , ,

Standardizing Focus Styles With CSS Custom Properties

Take two minutes right now and visit your current project in a browser. Then, using only the Tab key, you should be able to navigate between interactive elements including buttons, links, and form elements.

If you are sighted, you should be able to visually follow the focus as it jumps between elements in the DOM. But if you do not see any visual change, or only a barely noticeable visual change, then you’ve found the one thing you can do to make a big difference for your visitors.

We’re going to look at a technique to make your focus styles more manageable across your project by using CSS custom properties and learn about a modern CSS focus selector. But first, let’s learn more about why visible focus styles are important.

Meeting WCAG Focus Style Criteria

Visible focus states are covered in the Web Content Accessibility Guidelines (WCAG) Success Criterion 2.4.7 – Focus Visible. The Understanding doc for 2.4.7 states the following in the intent of this criteria:

The purpose of this success criterion is to help a person know which element has the keyboard focus. It must be possible for a person to know which element among multiple elements has the keyboard focus.

In the upcoming WCAG 2.2, a new criterion is being added to clarify “how visible the focus indicator should be.” While currently in draft, getting familiar with and applying the guidelines in 2.4.11 – Focus Appearance (Minimum) is definitely a positive step you can take today to improve your focus styles.

Managing focus style with CSS custom properties

A technique I’ve started using this year is to include the following setup early in my cascade on the primary base interactive elements:

:is(a, button, input, textarea, summary) {   --outline-size: max(2px, 0.08em);   --outline-style: solid;   --outline-color: currentColor; }  :is(a, button, input, textarea, summary):focus {   outline: var(--outline-size) var(--outline-style) var(--outline-color);   outline-offset: var(--outline-offset, var(--outline-size)); }

This attaches custom properties that allow you the flexibility to customize just parts of the outline style as needed to ensure the focus remains visible as the element’s context changes.

For --outline-size, we’re using max() to ensure at least a value of 2px, while allowing the possibility of scaling relative to the component (ex. a large button or link within a headline) based on 0.08em.

A property you might not be familiar with here is outline-offset which defines the space between the element and the outline. You can even provide a negative number to inset the outline, which can be very useful for ensuring contrast of the focus style. In our rule set, we’ve set that property to accept an optional custom property of --outline-offset so that it can be customized if needed, but otherwise it has the fallback to match the --outline-size.

Improvements for outline appearance

Over my career, I’ve both been asked to remove outlines and removed them myself because they were considered “ugly”.

There are now two reasons outline should absolutely never have cause to be removed (in addition to the accessibility impact):

  1. outline now follows border-radius in Chromium and Firefox! 🎉 This means you can considering removing any hacks you may have used, such as faking it with a box-shadow (which has another positive accessibility impact of ensuring focus styles aren’t removed for Windows High Contrast Theme users).
  2. Using :focus-visible we can ask the browser to use heuristics to only show focus styles when it detects input modalities that require visible focus. Simplified, that means mouse users won’t see them on click, keyboard users will still have them on tab.

It’s important to note that form elements always show a focus style — they are exempt from the behavior of :focus-visible.

So let’s enhance our rule set to add the following to include :focus-visible. We’ll keep the initial :focus style we already defined for older browsers so that it’s not lost just in case.

:is(a, button, input, textarea, summary):focus-visible {   outline: var(--outline-size) var(--outline-style) var(--outline-color);   outline-offset: var(--outline-offset, var(--outline-size)); }

Due to the way browsers throw out selectors they don’t understand, we do need to make these separate rules and not combine them even though they define the same outline properties.

Finally, we also need this kind of funny-looking :focus:not(:focus-visible) rule that removes the regular focus styles for browsers that support :focus-visible:

:is(a, button, input, textarea, summary):focus:not(:focus-visible) {   outline: none; }

Of note is that the latest versions of Chromium and Firefox have switched to using :focus-visible as the default way to apply focus styles on interactive elements, and just recently was enabled as default in webkit so it should be in Safari stable soon! Our rules are still valid since we’re customizing the outline appearance.

For more guidance on visible focus styles, I recommend Sara Soueidan’s amazing and thorough guide to focus indicators because it considers the upcoming 2.4.11 criterion.

Focus styles demo

This Pen shows examples of each of these interactive elements and how to apply customizations using the custom properties, including a few swaps for dark mode. Depending on your browser support, you may not see a focus style due to :focus-visible unless you use the tab key.

One final note: button is a unique interactive element when it comes to focus styles because it has additional considerations across its states, particularly if you are relying on color alone. For help with that, try out the palette generator from my project ButtonBuddy.dev.


, , , ,