Tag: Custom

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.

[waiting…]

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.

CSS-Tricks

, , , ,

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.

CSS-Tricks

, , , , ,
[Top]

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.

CSS-Tricks

, , , ,
[Top]

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.

CSS-Tricks

, , , ,
[Top]

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)!

CSS-Tricks

, , ,
[Top]

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

CSS-Tricks

, , , , ,
[Top]

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.

CSS-Tricks

, , , ,
[Top]

Open Props (and Custom Properties as a System)

Perhaps the most basic and obvious use of CSS custom properties is design tokens. Colors, fonts, spacings, timings, and other atomic bits of design that you can pull from as you design a site. If you pretty much only pull values from design tokens, you’ll be headed toward clean design and that consistent professional look that is typically the goal in web design. In fact, I’ve written that I think it’s exactly this that contributes to the popularity of utility class frameworks:

I’d argue some of that popularity is driven by the fact that if you choose from these pre-configured classes, that the design ends up fairly nice. You can’t go off the rails. You’re choosing from a limited selection of values that have been designed to look good.

I’m saying this (with a stylesheet that defines these classes as one-styling-job tokens):

<h1 class="color-primary size-large">Header<h1>

…is a similar value proposition as this:

html {   --color-primary: green;   --size-large: 3rem;   /* ... and a whole set of tokens */ }  h1 {   color: var(--color-primary);   font-size: var(--size-large); }

There are zero-build versions of both. For example, Tachyons is an it-is-what-it is stylesheet with a slew of utility classes you just use, while Windi is a whole fancy thing with a just-in-time compiler and such. Pollen is an it-is-what-it is library of custom properties you just use, while the brand new Open Props has a just-in-time compiler to only deliver the custom properties that are used.

Right, so, Open Props!

The entire thing is literally just a whole pile of CSS custom properties you can use to design stuff. It’s like a massive starting point for your styles. It’s saying custom property all the things, but in the way that we’re already used to with design tokens where they are a limited pre-determined number of choices.

The analogies are clear to people:

My guess is what will draw people to this is the beautiful defaults.

What it doesn’t do is prevent you from having to name things, which is something I know utility-class lovers really enjoy. Here, you’ll need to continue to use regular ol’ CSS selectors (like with named classes) to select things and style them as you “normally” would. But rather than hand-crafting your own values, you’re plucking values from these custom properties.

The whole base thing (you can view the source here) rolls in at 4.4kb across the wire (that’s what my DevTools showed, anyway). That doesn’t include the CSS you write to use the custom properties, but it’s a pretty tiny amount of overhead. There are additional PropPacks that increase the size (but thye are also super tiny), and if you’re worried about size, that’s what the whole just-in-time thing is about. You can play with that on StackBlitz.

Seems pretty sweet to me! I’d use it. I like that it’s ultimately just regular CSS, so there is nothing you can’t do. You’ll stay in good shape as CSS evolves.

CSS-Tricks

, , , ,
[Top]

Parallax Powered by CSS Custom Properties

Good friend Kent C. Dodds has recently dropped his new website which had a lot of work go into it. I was fortunate enough that Kent reached out a while back and asked if I could come up with some “whimsy” for the site. ✨

One of the first things that drew my attention was the large image of Kody (🐨) on the landing page. He’s surrounded by objects and that, to me, screamed, “Make me move!”

Life-like illustration of an animatronic panda in a warn jacket and riding a snowboard while surrounded by a bunch of objects, like leaves, skis, and other gadgets.

I have built parallax-style scenes before that respond to cursor movement, but not to this scale and not for a React application. The neat thing about this? We can power the whole thing with only two CSS custom properties.


Let’s start by grabbing our user’s cursor position. This is as straightforward as:

const UPDATE = ({ x, y }) => {   document.body.innerText = `x: $ {x}; y: $ {y}` } document.addEventListener('pointermove', UPDATE)

We want to map these values around a center point. For example, the left side of the viewport should be -1 for x, and 1 for the right side. We can reference an element and work out the value from its center using a mapping function. In this project, I was able to use GSAP and that meant using some of its utility functions. They already provide a mapRange() function for this purpose. Pass in two ranges and you’ll get a function you can use to get the mapped value.

const mapRange = (inputLower, inputUpper, outputLower, outputUpper) => {   const INPUT_RANGE = inputUpper - inputLower   const OUTPUT_RANGE = outputUpper - outputLower   return value => outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0) } // const MAPPER = mapRange(0, 100, 0, 10000) // MAPPER(50) === 5000

What if we want to use the window as the container element? We can map the value to the width and height of it.

import gsap from 'https://cdn.skypack.dev/gsap'  const BOUNDS = 100  const UPDATE = ({ x, y }) => {   const boundX = gsap.utils.mapRange(0, window.innerWidth, -BOUNDS, BOUNDS, x)   const boundY = gsap.utils.mapRange(0, window.innerHeight, -BOUNDS, BOUNDS, y)   document.body.innerText = `x: $ {Math.floor(boundX) / 100}; y: $ {Math.floor(boundY) / 100};` }  document.addEventListener('pointermove', UPDATE)

That gives us a range of x and y values that we can plug into our CSS. Note how we are dividing the values by 100 to get a fractional value. This should make sense when we integrate these values with our CSS a little later.

Now, what if we have an element that we want to map that value against, and within a certain proximity? In other words, we want our handler to look up the position of the element, work out the proximity range, and then map the cursor position to that range. The ideal solution here is to create a function that generates our handler for us. Then we can reuse it. For the purpose of this article, though, we’re operating on a “happy path” where we are avoiding type checks or checking for the callback value, etc.

const CONTAINER = document.querySelector('.container')  const generateHandler = (element, proximity, cb) => ({x, y}) => {   const bounds = 100   const elementBounds = element.getBoundingClientRect()   const centerX = elementBounds.left + elementBounds.width / 2   const centerY = elementBounds.top + elementBounds.height / 2   const boundX = gsap.utils.mapRange(centerX - proximity, centerX + proximity, -bounds, bounds, x)   const boundY = gsap.utils.mapRange(centerY - proximity, centerY + proximity, -bounds, bounds, y)   cb(boundX / 100, boundY / 100) }  document.addEventListener('pointermove', generateHandler(CONTAINER, 100, (x, y) => {   CONTAINER.innerText = `x: $ {x.toFixed(1)}; y: $ {y.toFixed(1)};` }))

In this demo, our proximity is 100. We’ll style it with a blue background to make it obvious. We pass a callback that gets fired each time the values for x and y get mapped to the bounds. We can divide these values in the callback or do what we want with them.

But wait, there’s an issue with that demo. The values go outside the bounds of -1 and 1. We need to clamp those values. GreenSock has another utility method we can use for this. It’s the equal of using a combination of Math.min and Math.max. As we already have the dependency, there’s no point in reinventing the wheel! We could clamp the values in the function. But, choosing to do so in our callback will be more flexible as we’ll show coming up.

We could do this with CSS clamp() if we’d like. 😉

document.addEventListener('pointermove', generateHandler(CONTAINER, 100, (x, y) => {   CONTAINER.innerText = `     x: $ {gsap.utils.clamp(-1, 1, x.toFixed(1))};     y: $ {gsap.utils.clamp(-1, 1, y.toFixed(1))};   ` }))

Now we have clamped values!

In this demo, adjust the proximity and drag the container around to see how the handler holds up.

That’s the majority of JavaScript for this project! All that’s left to do is pass these values to CSS-land. And we can do that in our callback. Let’s use custom properties named ratio-x and ratio-y.

const UPDATE = (x, y) => {   const clampedX = gsap.utils.clamp(-1, 1, x.toFixed(1))   const clampedY = gsap.utils.clamp(-1, 1, y.toFixed(1))   CONTAINER.style.setProperty('--ratio-x', clampedX)   CONTAINER.style.setProperty('--ratio-y', clampedY)   CONTAINER.innerText = `x: $ {clampedX}; y: $ {clampedY};` }  document.addEventListener('pointermove', generateHandler(CONTAINER, 100, UPDATE))

Now that we have some values we can use in our CSS, we can combine them with calc() any way we like. For example, this demo changes the scale of the container element based on the y value. It then updates the hue of the container based on the x value.

The neat thing here is that the JavaScript doesn’t care about what you do with the values. It’s done its part. That’s the magic of using scoped custom properties.

.container {   --hue: calc(180 - (var(--ratio-x, 0) * 180));   background: hsl(var(--hue, 25), 100%, 80%);   transform: scale(calc(2 - var(--ratio-y, 0))); }

Another interesting point is considering whether you want to clamp the values or not. In this demo, if we didn’t clamp x, we could have the hue update wherever we are on the page.

Making a scene

We have the technique in place! Now we can do pretty much whatever we want with it. It’s kinda wherever your imagination takes you. I’ve used this same set up for a bunch of things.

Our demos so far have only made changes to the containing element. But, as we may as well mention again, the power of custom property scope is epic.

My task was to make things move on Kent’s site. When I first saw the image of Kody with a bunch of objects, I could see all the individual pieces doing their own thing—all powered by those two custom properties that we pass in. How might that look though? The key is inline custom properties for each child of our container.

For now, we could update our markup to include some children:

<div class="container">   <div class="container__item"></div>   <div class="container__item"></div>   <div class="container__item"></div> </div>

Then we update the styles to include some scoped styles for container__item:

.container__item {   position: absolute;   top: calc(var(--y, 0) * 1%);   left: calc(var(--x, 0) * 1%);   height: calc(var(--size, 20) * 1px);   width: calc(var(--size, 20) * 1px);   background: hsl(var(--hue, 0), 80%, 80%);   transition: transform 0.1s;   transform:      translate(-50%, -50%)     translate(       calc(var(--move-x, 0) * var(--ratio-x, 0) * 100%),       calc(var(--move-y, 0) * var(--ratio-y, 0) * 100%)     )     rotate(calc(var(--rotate, 0) * var(--ratio-x, 0) * 1deg))   ; }

The important part there is how we’re making use of --ratio-x and --ratio-y inside the transform. Each item declares its own level of movement and rotation via --move-x, etc. Each item is also positioned with scoped custom properties, --x and --y.

That’s the key to these CSS powered parallax scenes. It’s all about bouncing coefficients against each other!

If we update our markup with some inline values for those properties, here’s what we get:

<div class="container">   <div class="container__item" style="--move-x: -1; --rotate: 90; --x: 10; --y: 60; --size: 30; --hue: 220;"></div>   <div class="container__item" style="--move-x: 1.6; --move-y: -2; --rotate: -45; --x: 75; --y: 20; --size: 50; --hue: 240;"></div>   <div class="container__item" style="--move-x: -3; --move-y: 1; --rotate: 360; --x: 75; --y: 80; --size: 40; --hue: 260;"></div> </div>

Leveraging that scope, we can get something like this! That’s pretty neat. It almost looks like a shield.

But, how do you take a static image and turn it into a responsive parallax scene? First, we’re going to have to create all those child elements and position them. And to do this we can use the “tracing” technique we use with CSS art.

This next demo shows the image we’re using inside a parallax container with children. To explain this part, we’ve created three children and given them a red background. The image is fixed with a reduced opacity and lines up with our parallax container.

Each parallax item gets created from a CONFIG object. For this demo, I’m using Pug to generate these in HTML for brevity. In the final project, I’m using React which we can show later. Using Pug here saves me writing out all the inline CSS custom properties individually.

-   const CONFIG = [     {       positionX: 50,       positionY: 55,       height: 59,       width: 55,     },     {       positionX: 74,       positionY: 15,       height: 17,       width: 17,     },     {       positionX: 12,       positionY: 51,       height: 24,       width: 19,     }   ]  img(src='https://assets.codepen.io/605876/kody-flying_blue.png') .parallax   - for (const ITEM of CONFIG)     .parallax__item(style=`--width: $ {ITEM.width}; --height: $ {ITEM.height}; --x: $ {ITEM.positionX}; --y: $ {ITEM.positionY};`)

How do we get those values? It’s a lot of trial and error and is definitely time consuming. To make it responsive, the positioning and sizing use percentage values.

.parallax {   height: 50vmin;   width: calc(50 * (484 / 479) * 1vmin); // Maintain aspect ratio where 'aspect-ratio' doesn't work to that scale.   background: hsla(180, 50%, 50%, 0.25);   position: relative; }  .parallax__item {   position: absolute;   left: calc(var(--x, 50) * 1%);   top: calc(var(--y, 50) * 1%);   height: calc(var(--height, auto) * 1%);   width: calc(var(--width, auto) * 1%);   background: hsla(0, 50%, 50%, 0.5);   transform: translate(-50%, -50%); }

Once we’ve made elements for all the items, we get something like the following demo. This uses the config object from the final work:

Don’t worry if things aren’t perfectly lined up. Everything is going to be moving anyway! That’s the joy of using a config object—we get tweak it how we like.

How do we get the image into those items? Well, it’s tempting to create separate images for each item. But, that would result in a lot of network requests for each image which is bad for performance. Instead, we can create an image sprite. In fact, that’s exactly what I did.

An image sprite of the original Kody image, showing each object and and Kody lined up from left to right.

Then to keep things responsive, we can use a percentage value for the background-size and background-position properties in the CSS. We make this part of the config and then inline those values, too. The config structure can be anything.

-   const ITEMS = [     {       identifier: 'kody-blue',       backgroundPositionX: 84.4,       backgroundPositionY: 50,       size: 739,       config: {         positionX: 50,         positionY: 54,         height: 58,         width: 55,       },     },   ]  .parallax   - for (const ITEM of ITEMS)     .parallax__item(style=`--pos-x: $ {ITEM.backgroundPositionX}; --pos-y: $ {ITEM.backgroundPositionY}; --size: $ {ITEM.size}; --width: $ {ITEM.config.width}; --height: $ {ITEM.config.height}; --x: $ {ITEM.config.positionX}; --y: $ {ITEM.config.positionY};`)

Updating our CSS to account for this:

.parallax__item {   position: absolute;   left: calc(var(--x, 50) * 1%);   top: calc(var(--y, 50) * 1%);   height: calc(var(--height, auto) * 1%);   width: calc(var(--width, auto) * 1%);   transform: translate(-50%, -50%);   background-image: url('kody-sprite.png');   background-position: calc(var(--pos-x, 0) * 1%) calc(var(--pos-y, 0) * 1%);   background-size: calc(var(--size, 0) * 1%); }

And now we have a responsive traced scene with parallax items!

All that’s left to do is remove the tracing image and the background colors, and apply transforms.

In the first version, I used the values in a different way. I had the handler return values between -60 and 60. We can do that with our handler by manipulating the return values.

const UPDATE = (x, y) => {   CONTAINER.style.setProperty(     '--ratio-x',     Math.floor(gsap.utils.clamp(-60, 60, x * 100))   )   CONTAINER.style.setProperty(     '--ratio-y',     Math.floor(gsap.utils.clamp(-60, 60, y * 100))   ) }

Then, each item can be configured for:

  • the x, y, and z positions,
  • movement on the x and y axis, and
  • rotation and translation on the x and y axis.

The CSS transforms are quite long. This is what they look like:

.parallax {   transform: rotateX(calc(((var(--rx, 0) * var(--range-y, 0)) * var(--allow-motion)) * 1deg))     rotateY(calc(((var(--ry, 0) * var(--range-x, 0)) * var(--allow-motion)) * 1deg))     rotate(calc(((var(--r, 0) * var(--range-x, 0)) * var(--allow-motion)) * 1deg));   transform-style: preserve-3d;   transition: transform 0.25s; }  .parallax__item {   transform: translate(-50%, -50%)     translate3d(       calc(((var(--mx, 0) * var(--range-x, 0)) * var(--allow-motion)) * 1%),       calc(((var(--my, 0) * var(--range-y, 0)) * var(--allow-motion)) * 1%),       calc(var(--z, 0) * 1vmin)     )     rotateX(calc(((var(--rx, 0) * var(--range-y, 0)) * var(--allow-motion)) * 1deg))     rotateY(calc(((var(--ry, 0) * var(--range-x, 0)) * var(--allow-motion)) * 1deg))     rotate(calc(((var(--r, 0) * var(--range-x, 0)) * var(--allow-motion)) * 1deg));   transform-style: preserve-3d;   transition: transform 0.25s; }

What’s that --allow-motion thing doing? That’s not in the demo! True. This is a little trick for applying reduced motion. If we have users who prefer “reduced” motion, we can cater for that with a coefficient. The word “reduced” doesn’t have to mean “none” after all!

@media (prefers-reduced-motion: reduce) {   .parallax {     --allow-motion: 0.1;   } } @media (hover: none) {   .parallax {     --allow-motion: 0;   } }

This “final” demo shows how the --allow-motion value affects the scene. Move the slider to see how you can reduce the motion.

This demo also shows off another feature: the ability to choose a “team” that changes Kody’s color. The neat part here is that all that requires is pointing to a different part of our image sprite.

And that’s it for creating a CSS custom property powered parallax! But, I did mention this was something I built in React. And yes, that last demo uses React. In fact, this worked quite well in a component-based environment. We have an array of configuration objects and we can pass them into a <Parallax> component as children along with any transform coefficients.

const Parallax = ({   config,   children, }: {   config: ParallaxConfig   children: React.ReactNode | React.ReactNode[] }) => {   const containerRef = React.useRef<HTMLDivElement>(null)   useParallax(     (x, y) => {       containerRef.current.style.setProperty(         '--range-x', Math.floor(gsap.utils.clamp(-60, 60, x * 100))       )       containerRef.current.style.setProperty(         '--range-y', Math.floor(gsap.utils.clamp(-60, 60, y * 100))       )     },     containerRef,     () => window.innerWidth * 0.5, )    return (     <div       ref={containerRef}       className='parallax'       style={         {           '--r': config.rotate,           '--rx': config.rotateX,           '--ry': config.rotateY,         } as ContainerCSS       }     >       {children}     </div>   ) } 

Then, if you spotted it, there’s a hook in there called useParallax. We pass a callback into this that receives the x and y value. We also pass in the proximity which can be a function, and the element to use.

const useParallax = (callback, elementRef, proximityArg = 100) => {   React.useEffect(() => {     if (!elementRef.current || !callback) return     const UPDATE = ({ x, y }) => {       const bounds = 100       const proximity = typeof proximityArg === 'function' ? proximityArg() : proximityArg       const elementBounds = elementRef.current.getBoundingClientRect()       const centerX = elementBounds.left + elementBounds.width / 2       const centerY = elementBounds.top + elementBounds.height / 2       const boundX = gsap.utils.mapRange(centerX - proximity, centerX + proximity, -bounds, bounds, x)       const boundY = gsap.utils.mapRange(centerY - proximity, centerY + proximity, -bounds, bounds, y)       callback(boundX / 100, boundY / 100)     }     window.addEventListener('pointermove', UPDATE)     return () => {       window.removeEventListener('pointermove', UPDATE)     }   }, [elementRef, callback]) }

Spinning this into a custom hook means I can reuse it elsewhere. In fact, removing the use of GSAP makes it a nice micro-package opportunity.

Lastly, the <ParallaxItem>. This is pretty straightforward. It’s a component that maps the props into inline CSS custom properties. In the project, I opted to map the background properties to a child of the ParallaxItem.

const ParallaxItem = ({   children,   config, }: {   config: ParallaxItemConfig   children: React.ReactNode | React.ReactNode[] }) => {   const params = {...DEFAULT_CONFIG, ...config}   return (     <div       className='parallax__item absolute'       style={         {           '--x': params.positionX,           '--y': params.positionY,           '--z': params.positionZ,           '--r': params.rotate,           '--rx': params.rotateX,           '--ry': params.rotateY,           '--mx': params.moveX,           '--my': params.moveY,           '--height': params.height,           '--width': params.width,         } as ItemCSS       }     >       {children}     </div>   ) }

Tie all that together and you could end up with something like this:

const ITEMS = [   {     identifier: 'kody-blue',     backgroundPositionX: 84.4,     backgroundPositionY: 50,     size: 739,     config: {       positionX: 50,       positionY: 54,       moveX: 0.15,       moveY: -0.25,       height: 58,       width: 55,       rotate: 0.01,     },   },   ...otherItems ]  const KodyParallax = () => (   <Parallax config={{     rotate: 0.01,     rotateX: 0.1,     rotateY: 0.25,   }}>     {ITEMS.map(item => (       <ParallaxItem key={item.identifier} config={item.config} />     ))}   </Parallax> )

Which gives us our parallax scene!

That’s it!

We just took a static image and turned it into a slick parallax scene powered by CSS custom properties! It’s funny because image sprites have been around a long time, but they still have a lot of use today!

Stay Awesome! ʕ •ᴥ•ʔ


The post Parallax Powered by CSS Custom Properties appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Zero Trickery Custom Radios and Checkboxes

I feel like half of all “custom-designed radio buttons and checkboxes” do two things:

  1. Make them bigger
  2. Colorize them

I always think of SurveyMonkey for having big chunky radios and checkboxes. And indeed, just poking at their interface quickly, even internally, the app uses has those all over the place:

SurveyMonkey’s implementation appears to be pseudo-elements on a <label> element with icon fonts and such.

I think it’s worth noting that if that’s all you are doing, you might be able to do that with zero CSS trickery. Just… make them bigger and colorize them.

Like…

input[type="radio"], input[type="checkbox"] {   width: 3em;   height: 3rem;   accent-color: green; }

That’ll chunk those suckers up and colorize them pretty good!

Firefox

Chrome

Except (obligatory sad trombone) in Safari:

Safari

We’re so close to having some very simple CSS to accomplish the main use-case for custom checkboxes and radios. But no cigar, that is, unless you can bring yourself to just not care about the Safari UI (it is still perfectly functional, after all).

If you do need to give up and go for a completely custom design, Stephanie Eckles has got you covered:

In related news, I always think of a “toggle” UI control as a set of 2 radio buttons progressively enhanced, but it turns out <button> is the way, as Michelle Barker covers here. No native UI for slider toggle thingies, alas.


The post Zero Trickery Custom Radios and Checkboxes appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]