Front End Web Development

Early Days of Container Style Queries

Css Tricks - Wed, 10/12/2022 - 3:06am

We’re still in suuuuuper early days with container queries. Too early for broad browser support, but Chromium already supports it, Safari started supporting it in version 16, and Firefox is presumably not far behind.

Most early days conversations revolving around container queries usually compare the syntax to media queries.

/* Stacked flex container */ .post { display: flex; flex-direction: column; } /* Change direction when viewport is 600px or wider */ @media(min-width: 600px) { .post { flex-direction: row; } }

/* Define the container */ .posts { container-name: posts; container-type: inline-size; } .post { display: flex; flex-direction: column; } /* Query the container's min-width */ @container posts (min-width: 600px) { /* Change styles when `posts` container is 600px or wider */ .post { flex-direction: row; } }

Both of these are making queries for min-width: 600. The difference is that the media query is looking at the viewport’s width to trigger those style changes while the container query is looking at the computed width of the .posts element. Sweet!

But after listening to CSS Podcast Episode 59, Una and Adam poked at the future of container queries: style queries! The current working draft of the CSS Containment Module Level 3 spec defines container style queries:

A container style query allows querying the computed values of the query container. It is a boolean combination of individual style features (<style-feature>) that each query a single, specific property of the query container.

But no examples on syntax just yet — only a brief description:

The syntax of a <style-feature> is the same as for a declaration, and its query is true if the computed value of the given property on the query container matches the given value (which is also computed with respect to the query container), unknown if the property or its value is invalid or unsupported, and false otherwise. The boolean syntax and logic combining style features into a style query is the same as for CSS feature queries. (See @supports.)

So, yes, given time we should expect to pull off something like this:

.posts { container-name: posts; } @container posts (background-color: #f8a100) { /* Change styles when `posts` container has an orange background */ .post { color: #fff; } }

That’s a pretty dumb example. One thing to note is that the container-type is no longer based on the container’s inline-size but by style. We could delcare that like so:

.posts { container-name: posts; container-type: style; /* unnecessary */ }

…but all container queries are style queries by default. Well. at least as it stands today. Miriam Suzanne has a nice outline of the possible issues that might pop up with that.

Where might querying a container’s styles come in handy? I don’t know yet! But my mind gos to a few places:

  • Custom property values: We’ve seen custom properties used like state indicators, such as the DRY-switching method Ana covered a while back. The value changes, and so do styles.
  • Alternate dark mode approach: Instead of basing it all on a body class change that re-assigns custom property values, maybe we can change an entire color palette if, say, the body background changes color.
  • More complex query conditions: Like, say, we want to apply styles when the size and style conditions for a container are met.

Una also mentioned in the CSS Podcast that container style queries could help prevent some awkward styling situations, like if we happen to have italicized text in an already italicized blockquote:

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

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

Rendering External API Data in WordPress Blocks on the Front End

Css Tricks - Tue, 10/11/2022 - 6:15am

There’ve been some new tutorials popping here on CSS-Tricks for working with WordPress blocks. One of them is an introduction to WordPress block development and it’s a good place to learn what blocks are and to register them in WordPress for use in pages and posts.

While the block basics are nicely covered in that post, I want to take it another step forward. You see, in that article, we learned the difference between rendering blocks in the back-end WordPress Block Editor and rendering them on the front-end theme. The example was a simple Pullquote Block that rendered different content and styles on each end.

Let’s go further and look at using dynamic content in a WordPress block. More specifically, let’s fetch data from an external API and render it on the front end when a particular block is dropped into the Block Editor.

We’re going to build a block that outputs data that shows soccer (er, football) rankings pulled from Api-Football.

This is what we’re working for together.

There’s more than one way to integrate an API with a WordPress block! Since the article on block basics has already walked through the process of making a block from scratch, we’re going to simplify things by using the @wordpress/create-block package to bootstrap our work and structure our project.

Initializing our block plugin

First things first: let’s spin up a new project from the command line:

npx @wordpress/create-block football-rankings

I normally would kick a project like this off by making the files from scratch, but kudos to the WordPress Core team for this handy utility!

Once the project folder has been created by the command, we technically have a fully-functional WordPress block registered as a plugin. So, let’s go ahead and drop the project folder into the wp-content/plugins directory where you have WordPress installed (probably best to be working in a local environment), then log into the WordPress admin and activate it from the Plugins screen.

Now that our block is initialized, installed, and activated, go ahead and open up the project folder from at /wp-content/plugins/football-rankings. You’re going to want to cd there from the command line as well to make sure we can continue development.

These are the only files we need to concentrate on at the moment:

  • edit.js
  • index.js
  • football-rankings.php

The other files in the project are important, of course, but are inessential at this point.

Reviewing the API source

We already know that we’re using Api-Football which comes to us courtesy of RapidAPI. Fortunately, RapidAPI has a dashboard that automatically generates the required scripts we need to fetch the API data for the 2021 Premier League Standings.

The RapidAPI dashboard

If you want to have a look on the JSON structure, you can generate visual representation with JSONCrack.

Fetching data from the edit.js file

I am going to wrap the RapidAPI code inside a React useEffect() hook with an empty dependency array so that it runs only once when the page is loaded. This way, we prevent WordPress from calling the API each time the Block Editor re-renders. You can check that using wp.data.subscribe() if you care to.

Here’s the code where I am importing useEffect(), then wrapping it around the fetch() code that RapidAPI provided:

/** * The edit function describes the structure of your block in the context of the * editor. This represents what the editor will render when the block is used. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit * * @return {WPElement} Element to render. */ import { useEffect } from "@wordpress/element"; export default function Edit(props) { const { attributes, setAttributes } = props; useEffect(() => { const options = { method: "GET", headers: { "X-RapidAPI-Key": "Your Rapid API key", "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com", }, }; fetch("https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39", options) .then( ( response ) => response.json() ) .then( ( response ) => { let newData = { ...response }; setAttributes( { data: newData } ); console.log( "Attributes", attributes ); }) .catch((err) => console.error(err)); }, []); return ( <p { ...useBlockProps() }> { __( "Standings loaded on the front end", "external-api-gutenberg" ) } </p> ); }

Notice that I have left the return function pretty much intact, but have included a note that confirms the football standings are rendered on the front end. Again, we’re only going to focus on the front end in this article — we could render the data in the Block Editor as well, but we’ll leave that for another article to keep things focused.

Storing API data in WordPress

Now that we are fetching data, we need to store it somewhere in WordPress. This is where the attributes.data object comes in handy. We are defining the data.type as an object since the data is fetched and formatted as JSON. Make sure you don’t have any other type or else WordPress won’t save the data, nor does it throw any error for you to debug.

We define all this in our index.js file:

registerBlockType( metadata.name, { edit: Edit, attributes: { data: { type: "object", }, }, save, } );

OK, so WordPress now knows that the RapidAPI data we’re fetching is an object. If we open a new post draft in the WordPress Block Editor and save the post, the data is now stored in the database. In fact, if we can see it in the wp_posts.post_content field if we open the site’s database in phpMyAdmin, Sequel Pro, Adminer, or whatever tool you use.

API output stored in the WordPress database Outputting JSON data in the front end

There are multiple ways to output the data on the front end. The way I’m going to show you takes the attributes that are stored in the database and passes them as a parameter through the render_callback function in our football-rankings.php file.

I like keeping a separation of concerns, so how I do this is to add two new files to the block plugin’s build folder: frontend.js and frontend.css (you can create a frontend.scss file in the src directory which compiled to CSS in the build directory). This way, the back-end and front-end codes are separate and the football-rankings.php file is a little easier to read.

Referring back to the introduction to WordPress block development, there are editor.css and style.css files for back-end and shared styles between the front and back end, respectively. By adding frontend.scss (which compiles to frontend.css, I can isolate styles that are only intended for the front end.

Before we worry about those new files, here’s how we call them in football-rankings.php:

/** * Registers the block using the metadata loaded from the `block.json` file. * Behind the scenes, it registers also all assets so they can be enqueued * through the block editor in the corresponding context. * * @see https://developer.wordpress.org/reference/functions/register_block_type/ */ function create_block_football_rankings_block_init() { register_block_type( __DIR__ . '/build', array( 'render_callback' => 'render_frontend' )); } add_action( 'init', 'create_block_football_rankings_block_init' ); function render_frontend($attributes) { if( !is_admin() ) { wp_enqueue_script( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.js'); wp_enqueue_style( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.css' ); // HIGHLIGHT 15,16,17,18 } ob_start(); ?> <div class="football-rankings-frontend" id="league-standings"> <div class="data"> <pre> <?php echo wp_json_encode( $attributes ) ?> </pre> </div> <div class="header"> <div class="position">Rank</div> <div class="team-logo">Logo</div> <div class="team-name">Team name</div> <div class="stats"> <div class="games-played">GP</div> <div class="games-won">GW</div> <div class="games-drawn">GD</div> <div class="games-lost">GL</div> <div class="goals-for">GF</div> <div class="goals-against">GA</div> <div class="points">Pts</div> </div> <div class="form-history">Last 5 games</div> </div> <div class="league-table"></div> </div> <?php return ob_get_clean(); }

Since I am using the render_callback() method for the attributes, I am going to handle the enqueue manually just like the Block Editor Handbook suggests. That’s contained in the !is_admin() condition, and is enqueueing the two files so that we avoid enqueuing them while using the editor screen.

Now that we have two new files we’re calling, we’ve gotta make sure we are telling npm to compile them. So, do that in package.json, in the scripts section:

"scripts": { "build": "wp-scripts build src/index.js src/frontend.js", "format": "wp-scripts format", "lint:css": "wp-scripts lint-style", "lint:js": "wp-scripts lint-js", "packages-update": "wp-scripts packages-update", "plugin-zip": "wp-scripts plugin-zip", "start": "wp-scripts start src/index.js src/frontend.js" },

Another way to include the files is to define them in the block metadata contained in our block.json file, as noted in the introduction to block development:

"viewScript": [ "file:./frontend.js", "example-shared-view-script" ], "style": [ "file:./frontend.css", "example-shared-style" ],

The only reason I’m going with the package.json method is because I am already making use of the render_callback() method.

Rendering the JSON data

In the rendering part, I am concentrating only on a single block. Generally speaking, you would want to target multiple blocks on the front end. In that case, you need to make use of document.querySelectorAll() with the block’s specific ID.

I’m basically going to wait for the window to load and grab data for a few key objects from JSON and apply them to some markup that renders them on the front end. I am also going to convert the attributes data to a JSON object so that it is easier to read through the JavaScript and set the details from JSON to HTML for things like the football league logo, team logos, and stats.

The “Last 5 games” column shows the result of a team’s last five matches. I have to manually alter the data for it since the API data is in a string format. Converting it to an array can help make use of it in HTML as a separate element for each of a team’s last five matches.

import "./frontend.scss"; // Wait for the window to load window.addEventListener( "load", () => { // The code output const dataEl = document.querySelector( ".data pre" ).innerHTML; // The parent rankings element const tableEl = document.querySelector( ".league-table" ); // The table headers const tableHeaderEl = document.querySelector( "#league-standings .header" ); // Parse JSON for the code output const dataJSON = JSON.parse( dataEl ); // Print a little note in the console console.log( "Data from the front end", dataJSON ); // All the teams let teams = dataJSON.data.response[ 0 ].league.standings[ 0 ]; // The league logo let leagueLogoURL = dataJSON.data.response[ 0 ].league.logo; // Apply the league logo as a background image inline style tableHeaderEl.style.backgroundImage = `url( ${ leagueLogoURL } )`; // Loop through the teams teams.forEach( ( team, index ) => { // Make a div for each team const teamDiv = document.createElement( "div" ); // Set up the columns for match results const { played, win, draw, lose, goals } = team.all; // Add a class to the parent rankings element teamDiv.classList.add( "team" ); // Insert the following markup and data in the parent element teamDiv.innerHTML = ` <div class="position"> ${ index + 1 } </div> <div class="team-logo"> <img src="${ team.team.logo }" /> </div> <div class="team-name">${ team.team.name }</div> <div class="stats"> <div class="games-played">${ played }</div> <div class="games-won">${ win }</div> <div class="games-drawn">${ draw }</div> <div class="games-lost">${ lose }</div> <div class="goals-for">${ goals.for }</div> <div class="goals-against">${ goals.against }</div> <div class="points">${ team.points }</div> </div> <div class="form-history"></div> `; // Stringify the last five match results for a team const form = team.form.split( "" ); // Loop through the match results form.forEach( ( result ) => { // Make a div for each result const resultEl = document.createElement( "div" ); // Add a class to the div resultEl.classList.add( "result" ); // Evaluate the results resultEl.innerText = result; // If the result a win if ( result === "W" ) { resultEl.classList.add( "win" ); // If the result is a draw } else if ( result === "D" ) { resultEl.classList.add( "draw" ); // If the result is a loss } else { resultEl.classList.add( "lost" ); } // Append the results to the column teamDiv.querySelector( ".form-history" ).append( resultEl ); }); tableEl.append( teamDiv ); }); });

As far as styling goes, you’re free to do whatever you want! If you want something to work with, I have a full set of styles you can use as a starting point.

I styled things in SCSS since the @wordpress/create-block package supports it out of the box. Run npm run start in the command line to watch the SCSS files and compile them to CSS on save. Alternately, you can use npm run build on each save to compile the SCSS and build the rest of the plugin bundle.

View SCSS body { background: linear-gradient(to right, #8f94fb, #4e54c8); } .data pre { display: none; } .header { display: grid; gap: 1em; padding: 10px; grid-template-columns: 1fr 1fr 3fr 4fr 3fr; align-items: center; color: white; font-size: 16px; font-weight: 600; background-repeat: no-repeat; background-size: contain; background-position: right; } .frontend#league-standings { width: 900px; margin: 60px 0; max-width: unset; font-size: 16px; .header { .stats { display: flex; gap: 15px; &amp; &gt; div { width: 30px; } } } } .league-table { background: white; box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; padding: 1em; .position { width: 20px; } .team { display: grid; gap: 1em; padding: 10px 0; grid-template-columns: 1fr 1fr 3fr 4fr 3fr; align-items: center; } .team:not(:last-child) { border-bottom: 1px solid lightgray; } .team-logo img { width: 30px; } .stats { display: flex; gap: 15px; } .stats &gt; div { width: 30px; text-align: center; } .form-history { display: flex; gap: 5px; } .form-history &gt; div { width: 25px; height: 25px; text-align: center; border-radius: 3px; font-size: 15px; } .form-history .win { background: #347d39; color: white; } .form-history .draw { background: gray; color: white; } .form-history .lost { background: lightcoral; color: white; } } Here’s the demo!

Check that out — we just made a block plugin that fetches data and renders it on the front end of a WordPress site.

Open demo

We found an API, fetch()-ed data from it, saved it to the WordPress database, parsed it, and applied it to some HTML markup to display on the front end. Not bad for a single tutorial, right?

Again, we can do the same sort of thing so that the rankings render in the Block Editor in addition to the theme’s front end. But hopefully keeping this focused on the front end shows you how fetching data works in a WordPress block, and how the data can be structured and rendered for display.

Rendering External API Data in WordPress Blocks on the Front End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Adding Fluid Typography Support to WordPress Block Themes

Css Tricks - Fri, 10/07/2022 - 3:19am

Fluid typography is a fancy way of “describing font properties, such as size or line height, that scale fluidly according to the size of the viewport”. It’s also known by other names, like responsive typography, flexible type, fluid type, viewport sized typography, fluid typography, and even responsive display text.

Here is an example of fluid typography that you can play live (courtesy of MDN documentation). CSS-Tricks has covered fluid typography extensively as well. But the point here is not to introduce you to fluid typography, but how to use it. More specifically, I want to show you how to implement fluid typography in WordPress 6.1 which recently introduced a fluid type feature directly in the WordPress Block Editor.

Open up your style.css file, slap in a style rule with fancy clamp()-ing on the font-size property, and good to go, right? Sure, that’ll give you fluid text, but to get Block Editor controls that make it possible to apply fluid type anywhere on your WordPress site? That requires a different approach in these block-ified days.

Fluid typography support in Gutenberg

Some WordPress theme developers have been using the clamp() function to define a fluid font-size, in their WordPress themes, even in newer “block” themes such as Twenty Twenty-Two, Twenty Twenty-Three, and others.

But the Gutenberg plugin — the one that contains experimental development for WordPress Block and Site Editor features — introduced support for fluid typography starting in version 13.8. That opened the door for implementing at a theme level so that fluid type can be applied to specific elements and blocks directly in the Block Editor. CSS-Tricks was even given a shout-out in the Pull Request that merged the feature.

That work became part of WordPress Core in WordPress 6.1. Rich Tabor, one of the earlier advocates of fluid typography in the Block Editor says:

[Fluid typography] is also a part of making WordPress more powerful, while not more complicated (which we all know is quite the challenge). […] Fluid typography just works. Actually, I think it works great.

This Make WordPress post highlights the approach taken to support the feature at the block level so that a fluid font size is applied to blocks dynamically by default. There are some benefits to this, of course:

  • It provides a way for theme authors to activate fluid typography without worrying about implementing it in code.
  • It applies fluid typography to specific typographical entities, such as elements or blocks in a maintainable and reusable way.
  • It allows flexibility in terms of font size units (e.g. px, rem, em, and %).

Now that this new feature is available in the WordPress Block Editor by default, theme authors can apply uniform fluid typography without writing additional code.

Blocks that support typography and spacing settings

Gutenberg 14.1 released on September 16, 2022, and introduced typographic settings on a bunch of blocks. That means the text settings for those blocks were set in CSS before and had to be changed in CSS as well. But those blocks now provide font and spacing controls in the Block Editor interface.

That work is currently slated to be added to WordPress 6.1, as detailed in this Make WordPress blog post. And with it is an expanded number of blocks that with typography support.

WordPress blocks that will support typography settings in the upcoming WordPress 6.1 release. Declaring fluid type in a WordPress block theme

So, how do we put this new fluid typography to use in WordPress? The answer is in theme.json, a new-ish file specific to block themes that contains a bunch of theme configurations in key:value pairs.

Let’s look at a rule for a large font in theme.json where contentSize: 768px and we’re working with a widesize: 1600px layout. This is how we can specify a CSS font-size using the clamp() function:

"settings": { "appearanceTools": true, "layout": { "contentSize": "768px", "wideSize": "1600px" }, "typography": { "fontSizes": [ { "name": "Large", "size": "clamp(2.25rem, 6vw, 3rem)", "slug": "large" } ] } }

As of WordPress 6.1, only rem, em and px units are supported.

That’s great and works, but with the new fluid type feature we would actually use a different approach. First, we opt into fluid typography on settings.typography, which has a new fluid property:

"settings": { "typography": { "fluid": true } }

Then we specify our settings.fontSizes like before, but with a new fluidSize property where we can set the min and max size values for our fluid type range.

"settings": { "appearanceTools": true, "layout": { "contentSize": "768px", "wideSize": "1600px" }, "typography": { "fontSizes": [ { "size": "2.25rem", "fluidSize": { "min": "2.25rem", "max": "3rem" }, "slug": "large", "name": "Large" } ] } }

That’s really it. We just added fluid type to a font size called “Large” with a range that starts at 2.25rem and scales up to 3rem. Now, we can apply the “Large” font to any block with font settings.

How does this works under the hood? Rich Tabor offers a nice explanation, as does this Pull Request in GitHub. In short, WordPress converts the theme.json properties into the following CSS rule:

.has-large-font-size { font-size: clamp(36px, calc(2.25rem + ((1vw - 7.68px) * 1.4423)), 48px); }

…which is applied to the element, say a Paragraph Block:

<p class="has-large-font-size">...</p>

Initially, I found it hard to understand and wrap around in my mind the concept of the CSS clamp() function without also learning about the min(), max(), and calc() functions. This calculator tool helped me quite a bit, especially for determining which values to use in my own theme projects.

For demonstration purposes, let’s use the calculator to define our font-size range so that the size is 36px at a 768px viewport width and 48px at a 1600px viewport width.

The calculator automatically generates the following CSS:

/* 36px @ 768px increasing to 48px @ 1600px */ font-size: clamp(36px, calc(2.25rem + ((1vw - 7.68px) * 1.4423)), 48px);

The calculator provide options to select input units as px, rem, and em. If we select rem unit, the calculator generates the following clamp() value:

/* 2.25rem @ 48rem increasing to 3rem @ 100rem */ font-size: clamp(2.25rem, calc(2.25rem + ((1vw - 0.48rem) * 1.4423)), 3rem);

So, those minimum and maximum values correspond to the the fluidSize.min and fluidSize.max values in theme.json. The min value is applied at viewports that are 768px wide and below. Then the font-size scales up as the viewport width grows. If the viewport is wider than 1600px, the max is applied and the font-size is capped there.

Examples

There are detailed testing instructions in the merged Pull Request that introduced the feature. There are even more testing instructions from Justin Tadlock’s pre-prelease post on Make WordPress.

Example 1: Setting a new fluid font setting

Let’s start with Justin’s set of instructions. I used in a modified version of the default Twenty Twenty-Three theme that is currently under development.

First, let’s make sure we’re running the Gutenberg plugin (13.8 and up) or WordPress 6.1, then opt into fluid type on the settings.typography.fluid property in the theme.json file:

{ "version": 2, "settings": { "appearanceTools": true, "layout": { "contentSize": "768px", "wideSize": "1600px" }, "typography": { "fluid": true } } }

Now, let’s drop the settings.typography.fontSizes examples in there:

{ "version": 2, "settings": { "appearanceTools": true, "layout": { "contentSize": "768px", "wideSize": "1600px" }, "typography": { "fluid": true "fontSizes": [ { "name": "Normal", "size": "1.125rem", "fluid": { "min": "1rem", "max": "1.5rem" }, "slug": "normal" } ] } } }

If everything is working correctly, we can now head into the WordPress Block Editor and apply the “Normal” font setting to our block:

Nice! And if we save and inspect that element on the front end, this is the markup:

Very good. Now let’s make sure the CSS is actually there:

Good, good. Let’s expose that CSS custom property to see if it’s really clampin’ things:

Looks like everything is working just as we want it! Let’s look at another example…

Example 2: Excluding a font setting from fluid type

This time, let’s follow the instructions from the merged Pull Request with a nod to this example by Carolina Nymark that shows how we can disable fluid type on a specific font setting.

I used the empty theme as advised in the instructions and opened up the theme.json file for testing. First, we opt into fluid type exactly as we did before:

{ "version": 2, "settings": { "appearanceTools": true, "layout": { "contentSize": "768px", "wideSize": "1000px" }, "typography": { "fluid": true } } }

This time, we’re working with smaller wideSize value of 1000px instead of 1600px. This should allow us to see fluid type working in an exact range.

OK, on to defining some custom font sizes under settings.typography.fontSizes:

{ "version": 2, "settings": { "typography": { "fluid": true, "fontSizes": [ { "size": ".875rem", "fluid": { "min": "0.875rem", "max": "1rem" }, "slug": "small", "name": "Small" }, { "size": "1rem", "fluid": { "min": "1rem", "max": "1.5rem" }, "slug": "normal", "name": "Normal" }, { "size": "1.5rem", "fluid": { "min": "1.5rem", "max": "2rem" }, "slug": "large", "name": "Large" }, { "size": "2.25rem", "fluid": false, "slug": "x-large", "name": "Extra large" } ] } } }

Notice that we’ve applied the fluid type feature only on the “Normal”, “Medium”, and “Large” font settings. “Extra Large” is the odd one out where the fluid object is set to false.

What WordPress does from here — via the Gutenberg style engine — is apply the properties we set into CSS clamp() functions for each font size setting that has opted into fluid type and a single size value for the Extra Large setting:

--wp--preset--font-size--small: clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 0.24), 1rem); --wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.48rem) * 0.962), 1.5rem); --wp--preset--font-size--large: clamp(1.5rem, 1.5rem + ((1vw - 0.48rem) * 0.962), 2rem); --wp--preset--font-size--x-large: 2.25rem;

Let’s check the markup on the front end:

Good start! Let’s confirm that the .has-x-large-font-size class is excluded from fluid type:

If we expose the --wp--preset--font-size--x-large variable, we’ll see it’s set to 2.25rem.

That’s exactly what we want!

Block themes that support fluid typography

Many WordPress themes already make use of the clamp() function for fluid type in both block and classic themes. A good example of fluid typography use is the recently released Twenty Twenty-Three default theme.

I’ve reviewed all the block themes from WordPress Block Theme directory, examining theme.json file of each theme and to see just how many block themes currently support fluid typography — not the new feature since it’s still in the Gutenberg plugin as of this writing — using the CSS clamp() function. Of the 146 themes I reviewed, the majority of them used a clamp() function to define spacing. A little more than half of them used clamp() to define font sizes. The Alara theme is the only one to use clamp() for defining the layout container sizes.

Understandably, only a few recently released themes contain the new fluid typography feature. But here are the ones I found that define it in theme.json:

And if you read my previous post here on CSS-Tricks, the TT2 Gopher Blocks theme I used for the demo has also been updated to support the fluid typography feature.

Selected reactions to the WordPress fluid typography features

Having fluid typography in WordPress at the settings level is super exciting! I thought I’d share some thoughts from folks in the WordPress developer community who have commented on it.

Matias Ventura, the lead architect of the Gutenberg project:

One of my favorite latest Gutenberg features is fluid type that — hopefully — "just works". It's available for testing in 13.8. pic.twitter.com/9SLUgEg4pF

— Matías Ventura (@matias_ventura) August 4, 2022

Rich Tabor:

As one of the bigger efforts towards making publishing beautifully rich pages in WordPress, fluid typography is a pretty big experience win for both the folks building with WordPress — and those consuming the content.

Automattic developer Ramon Dodd commented in the Pull Request:

Contrast that idea with font sizes that respond to specific viewport sizes, such as those defined by media queries, but do nothing in between those sizes. theme.json already allows authors to insert their own fluid font size values. This won’t change, but this PR offers it to folks who don’t want to worry about the implementation details.

Nick Croft, author of GenesisWP:

Not gonna lie, I think this is the feature I’m most excited about. Was using clamp to do this but the syntax looks easier for your average person.

— Nick Croft (@Nick_theGeek) September 1, 2022

Brian Garner, designer and principal developer advocate at WPEngine:

Fluid Typography is a huge improvement to #WordPress. FSE themes can experimentally opt into this via the Gutenberg plugin, but we’ll see it officially supported in 6.1 this fall. Here’s how it looks/works: pic.twitter.com/Jc7gHdyz5R

— Brian Gardner (@bgardner) September 1, 2022

A few developers think some features should be an opt-in. Jason Crist of Automattic says:

I love the power of fluid typography, however I also don’t believe that it should just be enabled by default. It’s usage (and the details of it) are important design decisions that should be made carefully.

You can also find a bunch more comments in the official testing instructions for the feature.

Wrapping up

The fluid typography feature in WordPress is still in active development at the time of this writing. So, right now, theme authors should proceed to use it, but with caution and expect some possible changes before it is officially released. Justin cautions theme authors using this feature and suggests to keep eye on the following two GitHub issues:

There is also still lots of ongoing work on typography and other design-related WordPress tools. If you’re interested, watch this typography tracking GitHub ticket and design tools related GitHub issues.

Resources

I used the following articles when researching fluid type and how WordPress is implementing it as a feature.

Tutorials and opinions CSS-Tricks calc() and related CSS functions

Adding Fluid Typography Support to WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

How to Safely Share Your Email Address on a Website

Css Tricks - Thu, 10/06/2022 - 2:54am

Spammers are a huge deal nowadays. If you want to share your contact information without getting overwhelmed by spam email you need a solution. I run into this problem a few months ago. While I was researching how to solve it, I found different interesting solutions. Only one of them was perfect for my needs.

In this article, I am going to show you how to easily protect your email address from spam bots with multiple solutions. It’s up to you to decide what technique fits your needs.

Table of contents The traditional case

Let’s say that you have a website. You want to share your contact details, and you don’t want to share only your social links. The email address must be there. Easy, right? You type something like this:

<a href="mailto:email@address.com">Send me an Email</a>

And then you style it according to your tastes.

Well, even if this solution works, it has a problem. It makes your email address available to literally everyone, including website crawlers and all sorts of spam bots. This means that your inbox can be flooded with tons of unwanted rubbish like promotional offers or even some phishing campaign.

We are looking for a compromise. We want to make it hard for bots to get our email addresses, but as simple as possible for normal users.

The solution is obfuscation.

Obfuscation is the practice of making something difficult to understand. This strategy is used with source code for multiple reasons. One of them is hiding the purpose of the source code to make tampering or reverse-engineering more difficult. We will first look at different solutions that are all based on the idea of obfuscation.

The HTML approach

We can think of bots as software that browse the web and crawl through web pages. Once a bot obtains an HTML document, it interprets the content in it and extracts information. This extraction process is called web scraping. If a bot is looking for a pattern that matches the email format, we can try to disguise it by using a different format. For example, we could use HTML comments:

<p>If you want to get in touch, please drop me an email at<!-- fhetydagzzzgjds --> email@<!-- sdfjsdhfkjypcs -->addr<!-- asjoxp -->ess.com</p>

It looks messy, but the user will see the email address like this:

If you want to get in touch, please drop me an email at email@address.com

Pros:

  • Easy to set up.
  • It works with JavaScript disabled.
  • It can be read by assistive technology.

Cons:

  • Spam bots can skip known sequences like comments.
  • It doesn’t work with a mailto: link.
The HTML & CSS approach

What if we use the styling power of CSS to remove some content placed only to fool spam bots? Let’s say that we have the same content as before, but this time we place a span element inside:

<p>If you want to get in touch, please drop me an email at <span class="blockspam" aria-hidden="true">PLEASE GO AWAY!</span> email@<!-- sdfjsdhfkjypcs -->address.com</p>.

Then, we use the following CSS style rule:

span.blockspam { display: none; }

The final user will only see this:

If you want to get in touch, please drop me an email at email@address.com.

…which is the content we truly care about.

Pros:

  • It works with JavaScript disabled.
  • It’s more difficult for bots to get the email address.
  • It can be read by assistive technology.

Con:

  • It doesn’t work with a mailto: link.
The JavaScript approach

In this example, we use JavaScript to make our email address unreadable. Then, when the page is loaded, JavaScript makes the email address readable again. This way, our users can get the email address.

The easiest solution uses the Base64 encoding algorithm to decode the email address. First, we need to encode the email address in Base64. We can use some websites like Base64Encode.org to do this. Type in your email address like this:

Then, click the button to encode. With these few lines of JavaScript we decode the email address and set the href attribute in the HTML link:

var encEmail = "ZW1haWxAYWRkcmVzcy5jb20="; const form = document.getElementById("contact"); form.setAttribute("href", "mailto:".concat(atob(encEmail)));

Then we have to make sure the email link includes id="contact" in the markup, like this:

<a id="contact" href="">Send me an Email</a>

We are using the atob method to decode a string of Base64-encoded data. An alternative is to use some basic encryption algorithm like the Caesar cipher, which is fairly straightforward to implement in JavaScript.

Pros:

  • It’s more complicated for bots to get the email address, especially if you use an encryption algorithm.
  • It works with a mailto: link.
  • It can be read by assistive technology.

Con:

  • JavaScript must be enabled on the browser, otherwise, the link will be empty.
The embedded form approach

Contact forms are everywhere. You certainly have used one of them at least once. If you want a way for people to directly contact you, one of the possible solutions is implementing a contact form service on your website.

Formspree is one example of service which provides you all the benefits of a contact form without worrying about server-side code. Wufoo is too. In fact, here is a bunch you can consider for handling contact form submissions for you.

The first step to using any form service is to sign up and create an account. Pricing varies, of course, as do the features offered between services. But one thing most of them do is provide you with an HTML snippet to embed a form you create into any website or app. Here’s an example I pulled straight from a form I created in my Formspring account

<form action="https://formspree.io/f/[my-key]" method="POST"> <label> Your email: <input type="email" name="email" /> </label> <label> Your message: <textarea name="message"></textarea> </label> <!-- honeypot spam filtering --> <input type="text" name="_gotcha" style="display:none" /> <button type="submit">Send</button> </form>

In the first line, you should customize action based on your endpoint. This form quite basic, but you can add as many fields as you wish.

Notice the hidden input tag on line 9. This input tag helps you filter the submissions made by regular users and bots. In fact, if Formspree’s back-end sees a submission with that input filled, it will discard it. A regular user wouldn’t do that, so it must be a bot.

Pros:

  • Your email address is safe since it is not public.
  • It works with Javascript disabled.

Con:

  • Relies on a third-party service (which may be a pro, depending on your needs)

There is one other disadvantage to this solution but I left it out of the list since it’s quite subjective and it depends on your use case. With this solution, you are not sharing your email address. You are giving people a way to contact you. What if people want to email you? What if people are looking for your email address, and they don’t want a contact form? A contact form may be a heavy-handed solution in that sort of situation.

Conclusion

We reached the end! In this tutorial, we talked about different solutions to the problem of online email sharing. We walked through different ideas, involving HTML code, JavaScript and even some online services like Formspree to build contact forms. At the end of this tutorial, you should be aware of all the pros and cons of the strategies shown. Now, it’s up to you to pick up the most suitable one for the your specific use case.

How to Safely Share Your Email Address on a Website originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Using Web Components With Next (or Any SSR Framework)

Css Tricks - Wed, 10/05/2022 - 3:05am

In my previous post we looked at Shoelace, which is a component library with a full suite of UX components that are beautiful, accessible, and — perhaps unexpectedly — built with Web Components. This means they can be used with any JavaScript framework. While React’s Web Component interoperability is, at present, less than ideal, there are workarounds.

But one serious shortcoming of Web Components is their current lack of support for server-side rendering (SSR). There is something called the Declarative Shadow DOM (DSD) in the works, but current support for it is pretty minimal, and it actually requires buy-in from your web server to emit special markup for the DSD. There’s currently work being done for Next.js that I look forward to seeing. But for this post, we’ll look at how to manage Web Components from any SSR framework, like Next.js, today.

We’ll wind up doing a non-trivial amount of manual work, and slightly hurting our page’s startup performance in the process. We’ll then look at how to minimize these performance costs. But make no mistake: this solution is not without tradeoffs, so don’t expect otherwise. Always measure and profile.

The problem

Before we dive in, let’s take a moment and actually explain the problem. Why don’t Web Components work well with server-side rendering?

Application frameworks like Next.js take React code and run it through an API to essentially “stringify” it, meaning it turns your components into plain HTML. So the React component tree will render on the server hosting the web app, and that HTML will be sent down with the rest of the web app’s HTML document to your user’s browser. Along with this HTML are some <script> tags that load React, along with the code for all your React components. When a browser processes these <script> tags, React will re-render the component tree, and match things up with the SSR’d HTML that was sent down. At this point, all of the effects will start running, the event handlers will wire up, and the state will actually… contain state. It’s at this point that the web app becomes interactive. The process of re-processing your component tree on the client, and wiring everything up is called hydration.

So, what does this have to do with Web Components? Well, when you render something, say the same Shoelace <sl-tab-group> component we visited last time:

<sl-tab-group ref="{tabsRef}"> <sl-tab slot="nav" panel="general"> General </sl-tab> <sl-tab slot="nav" panel="custom"> Custom </sl-tab> <sl-tab slot="nav" panel="advanced"> Advanced </sl-tab> <sl-tab slot="nav" panel="disabled" disabled> Disabled </sl-tab> <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> </sl-tab-group>

…React (or honestly any JavaScript framework) will see those tags and simply pass them along. React (or Svelte, or Solid) are not responsible for turning those tags into nicely-formatted tabs. The code for that is tucked away inside of whatever code you have that defines those Web Components. In our case, that code is in the Shoelace library, but the code can be anywhere. What’s important is when the code runs.

Normally, the code registering these Web Components will be pulled into your application’s normal code via a JavaScript import. That means this code will wind up in your JavaScript bundle and execute during hydration which means that, between your user first seeing the SSR’d HTML and hydration happening, these tabs (or any Web Component for that matter) will not render the correct content. Then, when hydration happens, the proper content will display, likely causing the content around these Web Components to move around and fit the properly formatted content. This is known as a flash of unstyled content, or FOUC. In theory, you could stick markup in between all of those <sl-tab-xyz> tags to match the finished output, but this is all but impossible in practice, especially for a third-party component library like Shoelace.

Moving our Web Component registration code

So the problem is that the code to make Web Components do what they need to do won’t actually run until hydration occurs. For this post, we’ll look at running that code sooner; immediately, in fact. We’ll look at custom bundling our Web Component code, and manually adding a script directly to our document’s <head> so it runs immediately, and blocks the rest of the document until it does. This is normally a terrible thing to do. The whole point of server-side rendering is to not block our page from processing until our JavaScript has processed. But once done, it means that, as the document is initially rendering our HTML from the server, the Web Components will be registered and will both immediately and synchronously emit the right content.

In our case, we’re just looking to run our Web Component registration code in a blocking script. This code isn’t huge, and we’ll look to significantly lessen the performance hit by adding some cache headers to help with subsequent visits. This isn’t a perfect solution. The first time a user browses your page will always block while that script file is loaded. Subsequent visits will cache nicely, but this tradeoff might not be feasible for you — e-commerce, anyone? Anyway, profile, measure, and make the right decision for your app. Besides, in the future it’s entirely possible Next.js will fully support DSD and Web Components.

Getting started

All of the code we’ll be looking at is in this GitHub repo and deployed here with Vercel. The web app renders some Shoelace components along with text that changes color and content upon hydration. You should be able to see the text change to “Hydrated,” with the Shoelace components already rendering properly.

Custom bundling Web Component code

Our first step is to create a single JavaScript module that imports all of our Web Component definitions. For the Shoelace components I’m using, my code looks like this:

import { setDefaultAnimation } from "@shoelace-style/shoelace/dist/utilities/animation-registry"; import "@shoelace-style/shoelace/dist/components/tab/tab.js"; import "@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js"; import "@shoelace-style/shoelace/dist/components/tab-group/tab-group.js"; import "@shoelace-style/shoelace/dist/components/dialog/dialog.js"; setDefaultAnimation("dialog.show", { keyframes: [ { opacity: 0, transform: "translate3d(0px, -20px, 0px)" }, { opacity: 1, transform: "translate3d(0px, 0px, 0px)" }, ], options: { duration: 250, easing: "cubic-bezier(0.785, 0.135, 0.150, 0.860)" }, }); setDefaultAnimation("dialog.hide", { keyframes: [ { opacity: 1, transform: "translate3d(0px, 0px, 0px)" }, { opacity: 0, transform: "translate3d(0px, 20px, 0px)" }, ], options: { duration: 250, easing: "cubic-bezier(0.785, 0.135, 0.150, 0.860)" }, });

It loads the definitions for the <sl-tab-group> and <sl-dialog> components, and overrides some default animations for the dialog. Simple enough. But the interesting piece here is getting this code into our application. We cannot simply import this module. If we did that, it’d get bundled into our normal JavaScript bundles and run during hydration. This would cause the FOUC we’re trying to avoid.

While Next.js does have a number of webpack hooks to custom bundle things, I’ll use Vite instead. First, install it with npm i vite and then create a vite.config.js file. Mine looks like this:

import { defineConfig } from "vite"; import path from "path"; export default defineConfig({ build: { outDir: path.join(__dirname, "./shoelace-dir"), lib: { name: "shoelace", entry: "./src/shoelace-bundle.js", formats: ["umd"], fileName: () => "shoelace-bundle.js", }, rollupOptions: { output: { entryFileNames: `[name]-[hash].js`, }, }, }, });

This will build a bundle file with our Web Component definitions in the shoelace-dir folder. Let’s move it over to the public folder so that Next.js will serve it. And we should also keep track of the exact name of the file, with the hash on the end of it. Here’s a Node script that moves the file and writes a JavaScript module that exports a simple constant with the name of the bundle file (this will come in handy shortly):

const fs = require("fs"); const path = require("path"); const shoelaceOutputPath = path.join(process.cwd(), "shoelace-dir"); const publicShoelacePath = path.join(process.cwd(), "public", "shoelace"); const files = fs.readdirSync(shoelaceOutputPath); const shoelaceBundleFile = files.find(name => /^shoelace-bundle/.test(name)); fs.rmSync(publicShoelacePath, { force: true, recursive: true }); fs.mkdirSync(publicShoelacePath, { recursive: true }); fs.renameSync(path.join(shoelaceOutputPath, shoelaceBundleFile), path.join(publicShoelacePath, shoelaceBundleFile)); fs.rmSync(shoelaceOutputPath, { force: true, recursive: true }); fs.writeFileSync(path.join(process.cwd(), "util", "shoelace-bundle-info.js"), `export const shoelacePath = "/shoelace/${shoelaceBundleFile}";`);

Here’s a companion npm script:

"bundle-shoelace": "vite build && node util/process-shoelace-bundle",

That should work. For me, util/shoelace-bundle-info.js now exists, and looks like this:

export const shoelacePath = "/shoelace/shoelace-bundle-a6f19317.js"; Loading the script

Let’s go into the Next.js \_document.js file and pull in the name of our Web Component bundle file:

import { shoelacePath } from "../util/shoelace-bundle-info";

Then we manually render a <script> tag in the <head>. Here’s what my entire _document.js file looks like:

import { Html, Head, Main, NextScript } from "next/document"; import { shoelacePath } from "../util/shoelace-bundle-info"; export default function Document() { return ( <Html> <Head> <script src={shoelacePath}></script> </Head> <body> <Main /> <NextScript /> </body> </Html> ); }

And that should work! Our Shoelace registration will load in a blocking script and be available immediately as our page processes the initial HTML.

Improving performance

We could leave things as they are but let’s add caching for our Shoelace bundle. We’ll tell Next.js to make these Shoelace bundles cacheable by adding the following entry to our Next.js config file:

async headers() { return [ { source: "/shoelace/shoelace-bundle-:hash.js", headers: [ { key: "Cache-Control", value: "public,max-age=31536000,immutable", }, ], }, ]; }

Now, on subsequent browses to our site, we see the Shoelace bundle caching nicely!

If our Shoelace bundle ever changes, the file name will change (via the :hash portion from the source property above), the browser will find that it does not have that file cached, and will simply request it fresh from the network.

Wrapping up

This may have seemed like a lot of manual work; and it was. It’s unfortunate Web Components don’t offer better out-of-the-box support for server-side rendering.

But we shouldn’t forget the benefits they provide: it’s nice being able to use quality UX components that aren’t tied to a specific framework. It’s aldo nice being able to experiment with brand new frameworks, like Solid, without needing to find (or hack together) some sort of tab, modal, autocomplete, or whatever component.

Using Web Components With Next (or Any SSR Framework) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Steven Heller’s Font of the Month: Slag

Typography - Tue, 10/04/2022 - 6:57pm

Read the book, Typographic Firsts

From the moment I started the engine in first gear with Slag Black, then accelerated to Slag Inline, Two Line, Three Line, and finally slid into Five Line, I felt an exhilarating jolt. There is so much color and texture in Slag’s linear geometric variations – and in the combination of large and small caps (which replaces lowercase letters) – that I was satisfied that the Slag family WAS the winnes in this month’s type road race.

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

State of CSS 2022 Survey Now Open

Css Tricks - Tue, 10/04/2022 - 8:35am

The State of CSS survey recently opened up. Last year, the survey confirmed everyone’s assumptions that TailwindCSS is super popular and CSS variables are mainstream. It also codified what many of us want from CSS, from Container Queries to a parent selector. (Spoiler alert, we now have both of ’em.)

While I wouldn’t say the results have been super surprising each year, this time I’m excited to start seeing more historical trends reveal themselves. The survey has been running since 2019, so that’s going to be four years (ancient in front-end years!) of data to see if certain frameworks came and went, specific features are gaining momentum, what general learning practices are out there, and just plain more context. It takes time for stuff to build up like this, so kudos to Sacha Greif for keeping this thing going.

And speaking of the team behind the survey, Lea Verou is new to the bunch and lead this year’s edition. Lea made some nice additions, including more open-ended comments, questions about browser inconsistencies, and a question that compares the amount of time you write CSS versus JavaScript.

Browsers actually use this stuff to help prioritize what features to work on — so definitely add your voice to the mix! The polls close on October 20.

To Shared LinkPermalink on CSS-Tricks

State of CSS 2022 Survey Now Open originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Introducing Shoelace, a Framework-Independent Component-Based UX Library

Css Tricks - Tue, 10/04/2022 - 3:01am

This is a post about Shoelace, a component library by Cory LaViska, but with a twist. It defines all your standard UX components: tabs, modals, accordions, auto-completes, and much, much more. They look beautiful out of the box, are accessible, and fully customizable. But rather than creating these components in React, or Solid, or Svelte, etc., it creates them with Web Components; this means you can use them with any framework.

Some preliminary things

Web Components are great, but there’s currently a few small hitches to be aware of.

React

I said they work in any JavaScript framework, but as I’ve written before, React’s support for Web Components is currently poor. To address this, Shoelace actually created wrappers just for React.

Another option, which I personally like, is to create a thin React component that accepts the tag name of a Web Component and all of its attributes and properties, then does the dirty work of handling React’s shortcomings. I talked about this option in a previous post. I like this solution because it’s designed to be deleted. The Web Component interoperability problem is currently fixed in React’s experimental branch, so once that’s shipped, any thin Web Component-interoperable component you’re using could be searched, and removed, leaving you with direct Web Component usages, without any React wrappers.

Server-Side Rendering (SSR)

Support for SSR is also poor at the time of this writing. In theory, there’s something called Declarative Shadow DOM (DSD) which would enable SSR. But browser support is minimal, and in any event, DSD actually requires server support to work right, which means Next, Remix, or whatever you happen to use on the server will need to become capable of some special handling.

That said, there are other ways to get Web Components to just work with a web app that’s SSR’d with something like Next. The short version is that the scripts registering your Web Components need to run in a blocking script before your markup is parsed. But that’s a topic for another post.

Of course, if you’re building any kind of client-rendered SPA, this is a non-issue. This is what we’ll work with in this post.

Let’s start

Since I want this post to focus on Shoelace and on its Web Component nature, I’ll be using Svelte for everything. I’ll also be using this Stackblitz project for demonstration. We’ll build this demo together, step-by-step, but feel free to open that REPL up anytime to see the end result.

I’ll show you how to use Shoelace, and more importantly, how to customize it. We’ll talk about Shadow DOMs and which styles they block from the outside world (as well as which ones they don’t). We’ll also talk about the ::part CSS selector — which may be entirely new to you — and we’ll even see how Shoelace allows us to override and customize its various animations.

If you find you like Shoelace after reading this post and want to try it in a React project, my advice is to use a wrapper like I mentioned in the introduction. This will allow you to use any of Shoelace’s components, and it can be removed altogether once React ships the Web Component fixes they already have (look for that in version 19).

Introducing Shoelace

Shoelace has fairly detailed installation instructions. At its most simple, you can dump <script> and <style> tags into your HTML doc, and that’s that. For any production app, though, you’ll probably want to selectively import only what you want, and there are instructions for that, too.

With Shoelace installed, let’s create a Svelte component to render some content, and then go through the steps to fully customize it. To pick something fairly non-trivial, I went with the tabs and a dialog (commonly referred to as a modal) components. Here’s some markup taken largely from the docs:

<sl-tab-group> <sl-tab slot="nav" panel="general">General</sl-tab> <sl-tab slot="nav" panel="custom">Custom</sl-tab> <sl-tab slot="nav" panel="advanced">Advanced</sl-tab> <sl-tab slot="nav" panel="disabled" disabled>Disabled</sl-tab> <sl-tab-panel name="general">This is the general tab panel.</sl-tab-panel> <sl-tab-panel name="custom">This is the custom tab panel.</sl-tab-panel> <sl-tab-panel name="advanced">This is the advanced tab panel.</sl-tab-panel> <sl-tab-panel name="disabled">This is a disabled tab panel.</sl-tab-panel> </sl-tab-group> <sl-dialog no-header label="Dialog"> Hello World! <button slot="footer" variant="primary">Close</button> </sl-dialog> <br /> <button>Open Dialog</button>

This renders some nice, styled tabs. The underline on the active tab even animates nicely, and slides from one active tab to the next.

Default tabs in Shoelace

I won’t waste your time running through every inch of the APIs that are already well-documented on the Shoelace website. Instead, let’s look into how best to interact with, and fully customize these Web Components.

Interacting with the API: methods and events

Calling methods and subscribing to events on a Web Component might be slightly different than what you’re used to with your normal framework of choice, but it’s not too complicated. Let’s see how.

Tabs

The tabs component (<sl-tab-group>) has a show method, which manually shows a particular tab. In order to call this, we need to get access to the underlying DOM element of our tabs. In Svelte, that means using bind:this. In React, it’d be a ref. And so on. Since we’re using Svelte, let’s declare a variable for our tabs instance:

<script> let tabs; </script>

…and bind it:

<sl-tab-group bind:this="{tabs}"></sl-tab-group>

Now we can add a button to call it:

<button on:click={() => tabs.show("custom")}>Show custom</button>

It’s the same idea for events. There’s a sl-tab-show event that fires when a new tab is shown. We could use addEventListener on our tabs variable, or we can use Svelte’s on:event-name shortcut.

<sl-tab-group bind:this={tabs} on:sl-tab-show={e => console.log(e)}>

That works and logs the event objects as you show different tabs.

Typically we render tabs and let the user click between them, so this work isn’t usually even necessary, but it’s there if you need it. Now let’s get the dialog component interactive.

Dialog

The dialog component (<sl-dialog>) takes an open prop which controls whether the dialog is… open. Let’s declare it in our Svelte component:

<script> let tabs; let open = false; </script>

It also has an sl-hide event for when the dialog is hidden. Let’s pass our open prop and bind to the hide event so we can reset it when the user clicks outside of the dialog content to close it. And let’s add a click handler to that close button to set our open prop to false, which would also close the dialog.

<sl-dialog no-header {open} label="Dialog" on:sl-hide={() => open = false}> Hello World! <button slot="footer" variant="primary" on:click={() => open = false}>Close</button> </sl-dialog>

Lastly, let’s wire up our open dialog button:

<button on:click={() => (open = true)}>Open Dialog</button>

And that’s that. Interacting with a component library’s API is more or less straightforward. If that’s all this post did, it would be pretty boring.

But Shoelace — being built with Web Components — means that some things, particularly styles, will work a bit differently than we might be used to.

Customize all the styles!

As of this writing, Shoelace is still in beta and the creator is considering changing some default styles, possibly even removing some defaults altogether so they’ll no longer override your host application’s styles. The concepts we’ll cover are relevant either way, but don’t be surprised if some of the Shoelace specifics I mention are different when you go to use it.

As nice as Shoelace’s default styles are, we might have our own designs in our web app, and we’ll want our UX components to match. Let’s see how we’d go about that in a Web Components world.

We won’t try to actually improve anything. The Shoelace creator is a far better designer than I’ll ever be. Instead, we’ll just look at how to change things, so you can adapt to your own web apps.

A quick tour of Shadow DOMs

Take a peek at one of those tab headers in your DevTools; it should look something like this:

Our tab element has created a div container with a .tab and .tab--active class, and a tabindex, while also displaying the text we entered for that tab. But notice that it’s sitting inside of a shadow root. This allows Web Component authors to add their own markup to the Web Component while also providing a place for the content we provide. Notice the <slot> element? That basically means “put whatever content the user rendered between the Web Component tags here.”

So the <sl-tab> component creates a shadow root, adds some content to it to render the nicely-styled tab header along with a placeholder (<slot>) that renders our content inside.

Encapsulated styles

One of the classic, more frustrating problems in web development has always been styles cascading to places where we don’t want them. You might worry that any style rules in our application which specify something like div.tab would interfere with these tabs. It turns out this isn’t a problem; shadow roots encapsulate styles. Styles from outside the shadow root do not affect what’s inside the shadow root (with some exceptions which we’ll talk about), and vice versa.

The exceptions to this are inheritable styles. You, of course, don’t need to apply a font-family style for every element in your web app. Instead, you can specify your font-family once, on :root or html and have it inherit everywhere beneath it. This inheritance will, in fact, pierce the shadow root as well.

CSS custom properties (often called “css variables”) are a related exception. A shadow root can absolutely read a CSS property that is defined outside the shadow root; this will become relevant in a moment.

The ::part selector

What about styles that don’t inherit. What if we want to customize something like cursor, which doesn’t inherit, on something inside of the shadow root. Are we out of luck? It turns out we’re not. Take another look at the tab element image above and its shadow root. Notice the part attribute on the div? That allows you to target and style that element from outside the shadow root using the ::part selector. We’ll walk through an example is a bit.

Overriding Shoelace styles

Let’s see each of these approaches in action. As of now, a lot of Shoelace styles, including fonts, receive default values from CSS custom properties. To align those fonts with your application’s styles, override the custom props in question. See the docs for info on which CSS variables Shoelace is using, or you can simply inspect the styles in any given element in DevTools.

Inheriting styles through the shadow root

Open the app.css file in the src directory of the StackBlitz project. In the :root section at the bottom, you should see a letter-spacing: normal; declaration. Since the letter-spacing property is inheritable, try setting a new value, like 2px. On save, all content, including the tab headers defined in the shadow root, will adjust accordingly.

Overwriting Shoelace CSS variables

The <sl-tab-group> component reads an --indicator-color CSS custom property for the active tab’s underline. We can override this with some basic CSS:

sl-tab-group { --indicator-color: green; }

And just like that, we now have a green indicator!

Querying parts

In the version of Shoelace I’m using right now (2.0.0-beta.83), any non-disabled tab has a pointer cursor. Let’s change that to a default cursor for the active (selected) tab. We already saw that the <sl-tab> element adds a part="base" attribute on the container for the tab header. Also, the currently selected tab receives an active attribute. Let’s use these facts to target the active tab, and change the cursor:

sl-tab[active]::part(base) { cursor: default; }

And that’s that!

Customizing animations

For some icing on the metaphorical cake, let’s see how Shoelace allows us to customize animations. Shoelace uses the Web Animations API, and exposes a setDefaultAnimation API to control how different elements animate their various interactions. See the docs for specifics, but as an example, here’s how you might change Shoelace’s default dialog animation from expanding outward, and shrinking inward, to instead animate in from the top, and drop down while hiding.

import { setDefaultAnimation } from "@shoelace-style/shoelace/dist/utilities/animation-registry"; setDefaultAnimation("dialog.show", { keyframes: [ { opacity: 0, transform: "translate3d(0px, -20px, 0px)" }, { opacity: 1, transform: "translate3d(0px, 0px, 0px)" }, ], options: { duration: 250, easing: "cubic-bezier(0.785, 0.135, 0.150, 0.860)" }, }); setDefaultAnimation("dialog.hide", { keyframes: [ { opacity: 1, transform: "translate3d(0px, 0px, 0px)" }, { opacity: 0, transform: "translate3d(0px, 20px, 0px)" }, ], options: { duration: 200, easing: "cubic-bezier(0.785, 0.135, 0.150, 0.860)" }, });

That code is in the App.svelte file. Comment it out to see the original, default animation.

Wrapping up

Shoelace is an incredibly ambitious component library that’s built with Web Components. Since Web Components are framework-independent, they can be used in any project, with any framework. With new frameworks starting to come out with both amazing performance characteristics, and also ease of use, the ability to use quality user experience widgets which aren’t tied to any one framework has never been more compelling.

Introducing Shoelace, a Framework-Independent Component-Based UX Library originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Comic Code — it’s no joke!

Typography - Tue, 10/04/2022 - 12:57am

Read the book, Typographic Firsts

Life is full of surprises. For example, Comic Code, designed by award-winning type designer Toshi Omagari, is one of our recent best-sellers, and our all-time best-selling coding font family!

The post Comic Code — it’s no joke! appeared first on I Love Typography.

Named Element IDs Can Be Referenced as JavaScript Globals

Css Tricks - Tue, 09/27/2022 - 2:58am

Did you know that DOM elements with IDs are accessible in JavaScript as global variables? It’s one of those things that’s been around, like, forever but I’m really digging into it for the first time.

If this is the first time you’re hearing about it, brace yourself! We can see it in action simply by adding an ID to an element in HTML:

<div id="cool"></div>

Normally, we’d define a new variable using querySelector("#cool") or getElementById("cool") to select that element:

var el = querySelector("#cool");

But we actually already have access to #cool without that rigamorale:

CodePen Embed Fallback

So, any id — or name attribute, for that matter — in the HTML can be accessed in JavaScript using window[ELEMENT_ID]. Again, this isn’t exactly “new” but it’s really uncommon to see.

As you may guess, accessing the global scope with named references isn’t the greatest idea. Some folks have come to call this the “global scope polluter.” We’ll get into why that is, but first…

Some context

This approach is outlined in the HTML specification, where it’s described as “named access on the Window object.”

Internet Explorer was the first to implement the feature. All other browsers added it as well. Gecko was the only browser at the time to not support it directly in standards mode, opting instead to make it an experimental feature. There was hesitation to implement it at all, but it moved ahead in the name of browser compatibility (Gecko even tried to convince WebKit to move it out of standards mode) and eventually made it to standards mode in Firefox 14.

One thing that might not be well known is that browsers had to put in place a few precautionary measures — with varying degrees of success — to ensure generated globals don’t break the webpage. One such measure is…

Variable shadowing

Probably the most interesting part of this feature is that named element references don’t shadow existing global variables. So, if a DOM element has an id that is already defined as a global, it won’t override the existing one. For example:

<head> <script> window.foo = "bar"; </script> </head> <body> <div id="foo">I won't override window.foo</div> <script> console.log(window.foo); // Prints "bar" </script> </body>

And the opposite is true as well:

<div id="foo">I will be overridden :(</div> <script> window.foo = "bar"; console.log(window.foo); // Prints "bar" </script>

This behavior is essential because it nullifies dangerous overrides such as <div id="alert" />, which would otherwise create a conflict by invalidating the alert API. This safeguarding technique may very well be the why you — if you’re like me — are learning about this for the first time.

The case against named globals

Earlier, I said that using global named elements as references might not be the greatest idea. There are lots of reasons for that, which TJ VanToll has covered nicely over at his blog and I will summarize here:

  • If the DOM changes, then so does the reference. That makes for some really “brittle” (the spec’s term for it) code where the separation of concerns between HTML and JavaScript might be too much.
  • Accidental references are far too easy. A simple typo may very well wind up referencing a named global and give you unexpected results.
  • It is implemented differently in browsers. For example, we should be able to access an anchor with an id — e.g. <a id="cool"> — but some browsers (namely Safari and Firefox) return a ReferenceError in the console.
  • It might not return what you think. According to the spec, when there are multiple instances of the same named element in the DOM — say, two instances of <div class="cool"> — the browser should return an HTMLCollection with an array of the instances. Firefox, however, only returns the first instance. Then again, the spec says we ought to use one instance of an id in an element’s tree anyway. But doing so won’t stop a page from working or anything like that.
  • Maybe there’s a performance cost? I mean, the browser’s gotta make that list of references and maintain it. A couple of folks ran tests in this StackOverflow thread, where named globals were actually more performant in one test and less performant in a more recent test.
Additional considerations

Let’s say we chuck the criticisms against using named globals and use them anyway. It’s all good. But there are some things you might want to consider as you do.

Polyfills

As edge-case-y as it may sound, these types of global checks are a typical setup requirement for polyfills. Check out the following example where we set a cookie using the new CookieStore API, polyfilling it on browsers that don’t support it yet:

<body> <img id="cookieStore"></img> <script> // Polyfill the CookieStore API if not yet implemented. // https://developer.mozilla.org/en-US/docs/Web/API/CookieStore if (!window.cookieStore) { window.cookieStore = myCookieStorePolyfill; } cookieStore.set("foo", "bar"); </script> </body>

This code works perfectly fine in Chrome, but throws the following error in Safari.:

TypeError: cookieStore.set is not a function

Safari lacks support for the CookieStore API as of this writing. As a result, the polyfill is not applied because the img element ID creates a global variable that clashes with the cookieStore global.

JavaScript API updates

We can flip the situation and find yet another issue where updates to the browser’s JavaScript engine can break a named element’s global references.

For example:

<body> <input id="BarcodeDetector"></input> <script> window.BarcodeDetector.focus(); </script> </body>

That script grabs a reference to the input element and invokes focus() on it. It works correctly. Still, we don’t know how long it will continue to work.

You see, the global variable we’re using to reference the input element will stop working as soon as browsers start supporting the BarcodeDetector API. At that point, the window.BarcodeDetector global will no longer be a reference to the input element and .focus() will throw a “window.BarcodeDetector.focus is not a function” error.

Conclusion

Let’s sum up how we got here:

  • All major browsers automatically create global references to each DOM element with an id (or, in some cases, a name attribute).
  • Accessing these elements through their global references is unreliable and potentially dangerous. Use querySelector or getElementById instead.
  • Since global references are generated automatically, they may have some side effects on your code. That’s a good reason to avoid using the id attribute unless you really need it.

At the end of the day, it’s probably a good idea to avoid using named globals in JavaScript. I quoted the spec earlier about how it leads to “brittle” code, but here’s the full text to drive the point home:

As a general rule, relying on this will lead to brittle code. Which IDs end up mapping to this API can vary over time, as new features are added to the web platform, for example. Instead of this, use document.getElementById() or document.querySelector().

I think the fact that the HTML spec itself recommends to staying away from this feature speaks for itself.

Named Element IDs Can Be Referenced as JavaScript Globals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

How to Create Wavy Shapes & Patterns in CSS

Css Tricks - Mon, 09/26/2022 - 3:13am

The wave is probably one of the most difficult shapes to make in CSS. We always try to approximate it with properties like border-radius and lots of magic numbers until we get something that feels kinda close. And that’s before we even get into wavy patterns, which are more difficult.

“SVG it!” you might say, and you are probably right that it’s a better way to go. But we will see that CSS can make nice waves and the code for it doesn’t have to be all crazy. And guess what? I have an online generator to make it even more trivial!

If you play with the generator, you can see that the CSS it spits out is only two gradients and a CSS mask property — just those two things and we can make any kind of wave shape or pattern. Not to mention that we can easily control the size and the curvature of the waves while we’re at it.

Some of the values may look like “magic numbers” but there’s actually logic behind them and we will dissect the code and discover all the secrets behind creating waves.

This article is a follow-up to a previous one where I built all kinds of different zig-zag, scoped, scalloped, and yes, wavy border borders. I highly recommend checking that article as it uses the same technique we will cover here, but in greater detail.

The math behind waves

Strictly speaking, there isn’t one magic formula behind wavy shapes. Any shape with curves that go up and down can be called a wave, so we are not going to restrict ourselves to complex math. Instead, we will reproduce a wave using the basics of geometry.

Let’s start with a simple example using two circle shapes:

We have two circles with the same radius next to each other. Do you see that red line? It covers the top half of the first circle and the bottom half of the second one. Now imagine you take that line and repeat it.

We already see the wave. Now let’s fill the bottom part (or the top one) to get the following:

Tada! We have a wavy shape, and one that we can control using one variable for the circle radii. This is one of the easiest waves we can make and it’s the one I showed off in this previous article

Let’s add a bit of complexity by taking the first illustration and moving the circles a little:

We still have two circles with the same radii but they are no longer horizontally aligned. In this case, the red line no longer covers half the area of each circle, but a smaller area instead. This area is limited by the dashed red line. That line crosses the point where both circles meet.

Now take that line and repeat it and you get another wave, a smoother one.

I think you get the idea. By controlling the position and size of the circles, we can create any wave we want. We can even create variables for them, which I will call P and S, respectively.

You have probably noticed that, in the online generator, we control the wave using two inputs. They map to the above variables. S is the “Size of the wave” and P is the “curvature of the wave”.

I am defining P as P = m*S where m is the variable you adjust when updating the curvature of the wave. This allows us to always have the same curvature, even if we update S.

m can be any value between 0 and 2. 0 will give us the first particular case where both circles are aligned horizontally. 2 is a kind of maximum value. We can go bigger, but after a few tests I found that anything above 2 produces bad, flat shapes.

Let’s not forget the radius of our circle! That can also be defined using S and P like this:

R = sqrt(P² + S²)/2

When P is equal to 0, we will have R = S/2.

We have everything to start converting all of this into gradients in CSS!

Creating gradients

Our waves use circles, and when talking about circles we talk about radial gradients. And since two circles define our wave, we will logically be using two radial gradients.

We will start with the particular case where P is equal to 0. Here is the illustration of the first gradient:

CodePen Embed Fallback

This gradient creates the first curvature while filling in the entire bottom area —the “water” of the wave so to speak.

.wave { --size: 50px; mask: radial-gradient(var(--size) at 50% 0%, #0000 99%, red 101%) 50% var(--size)/calc(4 * var(--size)) 100% repeat-x; }

The --size variable defines the radius and the size of the radial gradient. If we compare it with the S variable, then it’s equal to S/2.

Now let’s add the second gradient:

CodePen Embed Fallback

The second gradient is nothing but a circle to complete our wave:

radial-gradient(var(--size) at 50% var(--size), blue 99%, #0000 101%) calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%

If you check the previous article you will see that I am simply repeating what I already did there.

I followed both articles but the gradient configurations are not the same.

That’s because we can reach the same result using different gradient configurations. You will notice a slight difference in the alignment if you compare both configurations, but the trick is the same. This can be confusing if you are unfamiliar with gradients, but don’t worry. With some practice, you get used to them and you will find by yourself that different syntax can lead to the same result.

Here is the full code for our first wave:

.wave { --size: 50px; mask: radial-gradient(var(--size) at 50% var(--size),#000 99%, #0000 101%) calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%, radial-gradient(var(--size) at 50% 0px, #0000 99%, #000 101%) 50% var(--size)/calc(4 * var(--size)) 100% repeat-x; }

Now let’s take this code and adjust it to where we introduce a variable that makes this fully reusable for creating any wave we want. As we saw in the previous section, the main trick is to move the circles so they are no more aligned so let’s update the position of each one. We will move the first one up and the second down.

Our code will look like this:

.wave { --size: 50px; --p: 25px; mask: radial-gradient(var(--size) at 50% calc(var(--size) + var(--p)), #000 99%, #0000 101%) calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%, radial-gradient(var(--size) at 50% calc(-1*var(--p)), #0000 99%, #000 101%) 50% var(--size) / calc(4 * var(--size)) 100% repeat-x; }

I have introduced a new --p variable that’s used it to define the center position of each circle. The first gradient is using 50% calc(-1*var(--p)), so its center moves up while the second one is using calc(var(--size) + var(--p)) to move it down.

A demo is worth a thousand words:

CodePen Embed Fallback

The circles are neither aligned nor touch one another. We spaced them far apart without changing their radii, so we lost our wave. But we can fix things up by using the same math we used earlier to calculate the new radius. Remember that R = sqrt(P² + S²)/2. In our case, --size is equal to S/2; the same for --p which is also equal to P/2 since we are moving both circles. So, the distance between their center points is double the value of --p for this:

R = sqrt(var(--size) * var(--size) + var(--p) * var(--p))

That gives us a result of 55.9px.

CodePen Embed Fallback

Our wave is back! Let’s plug that equation into our CSS:

.wave { --size: 50px; --p: 25px; --R: sqrt(var(--p) * var(--p) + var(--size)*var(--size)); mask: radial-gradient(var(--R) at 50% calc(var(--size) + var(--p)), #000 99%, #0000 101%) calc(50% - 2*var(--size)) 0 / calc(4 * var(--size)) 100%, radial-gradient(var(--R) at 50% calc(-1*var(--p)), #0000 99%, #000 101%) 50% var(--size)/calc(4 * var(--size)) 100% repeat-x; }

This is valid CSS code. sqrt() is part of the specification, but at the time I’m writing this, there is no browser support for it. That means we need a sprinkle of JavaScript or Sass to calculate that value until we get broader sqrt() support.

This is pretty darn cool: all it takes is two gradients to get a cool wave that you can apply to any element using the mask property. No more trial and error — all you need is to update two variables and you’re good to go!

Reversing the wave

What if we want the waves going the other direction, where we’re filling in the “sky” instead of the “water”. Believe it or not, all we have to do is to update two values:

.wave { --size: 50px; --p: 25px; --R: sqrt(var(--p) * var(--p) + var(--size) * var(--size)); mask: radial-gradient(var(--R) at 50% calc(100% - (var(--size) + var(--p))), #000 99%, #0000 101%) calc(50% - 2 * var(--size)) 0/calc(4 * var(--size)) 100%, radial-gradient(var(--R) at 50% calc(100% + var(--p)), #0000 99%, #000 101%) 50% calc(100% - var(--size)) / calc(4 * var(--size)) 100% repeat-x; }

All I did there is add an offset equal to 100%, highlighted above. Here’s the result:

CodePen Embed Fallback

We can consider a more friendly syntax using keyword values to make it even easier:

.wave { --size: 50px; --p: 25px; --R: sqrt(var(--p)*var(--p) + var(--size) * var(--size)); mask: radial-gradient(var(--R) at left 50% bottom calc(var(--size) + var(--p)), #000 99%, #0000 101%) calc(50% - 2 * var(--size)) 0/calc(4 * var(--size)) 100%, radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), #0000 99%, #000 101%) left 50% bottom var(--size) / calc(4 * var(--size)) 100% repeat-x; }

We’re using the left and bottom keywords to specify the sides and the offset. By default, the browser defaults to left and top — that’s why we use 100% to move the element to the bottom. In reality, we are moving it from the top by 100%, so it’s really the same as saying bottom. Much easier to read than math!

With this updated syntax, all we have to do is to swap bottom for top — or vice versa — to change the direction of the wave.

CodePen Embed Fallback

And if you want to get both top and bottom waves, we combine all the gradients in a single declaration:

.wave { --size: 50px; --p: 25px; --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size)); mask: /* Gradient 1 */ radial-gradient(var(--R) at left 50% bottom calc(var(--size) + var(--p)), #000 99%, #0000 101%) left calc(50% - 2*var(--size)) bottom 0 / calc(4 * var(--size)) 51% repeat-x, /* Gradient 2 */ radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), #0000 99%, #000 101%) left 50% bottom var(--size) / calc(4 * var(--size)) calc(51% - var(--size)) repeat-x, /* Gradient 3 */ radial-gradient(var(--R) at left 50% top calc(var(--size) + var(--p)), #000 99%, #0000 101%) left calc(50% - 2 * var(--size)) top 0 / calc(4 * var(--size)) 51% repeat-x, /* Gradient 4 */ radial-gradient(var(--R) at left 50% top calc(-1 * var(--p)), #0000 99%, #000 101%) left 50% top var(--size) / calc(4 * var(--size)) calc(51% - var(--size)) repeat-x; } CodePen Embed Fallback

If you check the code, you will see that in addition to combining all the gradients, I have also reduced their height from 100% to 51% so that they both cover half of the element. Yes, 51%. We need that little extra percent for a small overlap that avoid gaps.

What about the left and right sides?

It’s your homework! Take what we did with the top and bottom sides and try to update the values to get the right and left values. Don’t worry, it’s easy and the only thing you need to do is to swap values.

If you have trouble, you can always use the online generator to check the code and visualize the result.

Wavy lines

Earlier, we made our first wave using a red line then filled the bottom portion of the element. How about that wavy line? That’s a wave too! Even better is if we can control its thickness with a variable so we can reuse it. Let’s do it!

We are not going to start from scratch but rather take the previous code and update it. The first thing to do is to update the color stops of the gradients. Both gradients start from a transparent color to an opaque one, or vice versa. To simulate a line or border, we need to start from transparent, go to opaque, then back to transparent again:

#0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%

I think you already guessed that the --b variable is what we’re using to control the line thickness. Let’s apply this to our gradients:

CodePen Embed Fallback

Yeah, the result is far from a wavy line. But looking closely, we can see that one gradient is correctly creating the bottom curvature. So, all we really need to do is rectify the second gradient. Instead of keeping a full circle, let’s make partial one like the other gradient.

CodePen Embed Fallback

Still far, but we have both curvatures we need! If you check the code, you will see that we have two identical gradients. The only difference is their positioning:

.wave { --size: 50px; --b: 10px; --p: 25px; --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size)); --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%; mask: radial-gradient(var(--R) at left 50% bottom calc(-1*var(--p)), var(--_g)) calc(50% - 2*var(--size)) 0/calc(4*var(--size)) 100%, radial-gradient(var(--R) at left 50% top calc(-1*var(--p)), var(--_g)) 50% var(--size)/calc(4*var(--size)) 100%; }

Now we need to adjust the size and position for the final shape. We no longer need the gradient to be full-height, so we can replace 100% with this:

/* Size plus thickness */ calc(var(--size) + var(--b))

There is no mathematical logic behind this value. It only needs to be big enough for the curvature. We will see its effect on the pattern in just a bit. In the meantime, let’s also update the position to vertically center the gradients:

.wave { --size: 50px; --b: 10px; --p: 25px; --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size)); --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%; mask: radial-gradient(var(--R) at left 50% bottom calc(-1*var(--p)), var(--_g)) calc(50% - 2*var(--size)) 50%/calc(4 * var(--size)) calc(var(--size) + var(--b)) no-repeat, radial-gradient(var(--R) at left 50% top calc(-1 * var(--p)), var(--_g)) 50% 50%/calc(4 * var(--size)) calc(var(--size) + var(--b)) no-repeat; }

Still not quite there:

CodePen Embed Fallback

One gradient needs to move a bit down and the other a bit up. Both need to move by half of their height.

CodePen Embed Fallback

We are almost there! We need a small fix for the radius to have a perfect overlap. Both lines need to offset by half the border (--b) thickness:

CodePen Embed Fallback

We got it! A perfect wavy line that we can easily adjust by controlling a few variables:

.wave { --size: 50px; --b: 10px; --p: 25px; --R: calc(sqrt(var(--p) * var(--p) + var(--size) * var(--size)) + var(--b) / 2); --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%; mask: radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), var(--_g)) calc(50% - 2*var(--size)) calc(50% - var(--size)/2 - var(--b)/2) / calc(4 * var(--size)) calc(var(--size) + var(--b)) repeat-x, radial-gradient(var(--R) at left 50% top calc(-1*var(--p)),var(--_g)) 50% calc(50% + var(--size)/2 + var(--b)/2) / calc(4 * var(--size)) calc(var(--size) + var(--b)) repeat-x; }

I know that the logic takes a bit to grasp. That’s fine and as I said, creating a wavy shape in CSS is not easy, not to mention the tricky math behind it. That’s why the online generator is a lifesaver — you can easily get the final code even if you don’t fully understand the logic behind it.

Wavy patterns

We can make a pattern from the wavy line we just created!

Oh no, the code of the pattern will be even more difficult to understand!

Not at all! We already have the code. All we need to do is to remove repeat-x from what we already have, and tada. &#x1f389;

CodePen Embed Fallback

A nice wavy pattern. Remember the equation I said we’d revisit?

/* Size plus thickness */ calc(var(--size) + var(--b))

Well, this is what controls the distance between the lines in the pattern. We can make a variable out of it, but there’s no need for more complexity. I’m not even using a variable for that in the generator. Maybe I’ll change that later.

Here is the same pattern going in a different direction:

CodePen Embed Fallback

I am providing you with the code in that demo, but I’d for you to dissect it and understand what changes I made to make that happen.

Simplifying the code

In all the previous demos, we always define the --size and --p independently. But do you recall how I mentioned earlier that the online generator evaluates P as equal to m*S, where m controls the curvature of the wave? By defining a fixed multiplier, we can work with one particular wave and the code can become easier. This is what we will need in most cases: a specific wavy shape and a variable to control its size.

Let’s update our code and introduce the m variable:

.wave { --size: 50px; --R: calc(var(--size) * sqrt(var(--m) * var(--m) + 1)); mask: radial-gradient(var(--R) at 50% calc(var(--size) * (1 + var(--m))), #000 99%, #0000 101%) calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%, radial-gradient(var(--R) at 50% calc(-1 * var(--size) * var(--m)), #0000 99%, #000 101%) 50% var(--size) / calc(4 * var(--size)) 100% repeat-x; }

As you can see, we no longer need the --p variable. I replaced it with var(--m)*var(--size), and optimized some of the math accordingly. Now, If we want to work with a particular wavy shape, we can omit the --m variable and replace it with a fixed value. Let’s try .8 for example.

--size: 50px; --R: calc(var(--size) * 1.28); mask: radial-gradient(var(--R) at 50% calc(1.8 * var(--size)), #000 99%, #0000 101%) calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%, radial-gradient(var(--R) at 50% calc(-.8 * var(--size)), #0000 99%, #000 101%) 50% var(--size) / calc(4 * var(--size)) 100% repeat-x;

See how the code is easier now? Only one variable to control your wave, plus you no more need to rely on sqrt() which has no browser support!

CodePen Embed Fallback

You can apply the same logic to all the demos we saw even for the wavy lines and the pattern. I started with a detailed mathmatical explanation and gave the generic code, but you may find yourself needing easier code in a real use case. This is what I am doing all the time. I rarely use the generic code, but I always consider a simplified version especially that, in most of the cases, I am using some known values that don’t need to be stored as variables. (Spoiler alert: I will be sharing a few examples at the end!)

Limitations to this approach

Mathematically, the code we made should give us perfect wavy shapes and patterns, but in reality, we will face some strange results. So, yes, this method has its limitations. For example, the online generator is capable of producing poor results, especially with wavy lines. Part of the issue is due to a particular combination of values where the result gets scrambled, like using a big value for the border thickness compared to the size:

For the other cases, it’s the issue related to some rounding that will results in misalignment and gaps between the waves:

That said, I still think the method we covered remains a good one because it produces smooth waves in most cases, and we can easily avoid the bad results by playing with different values until we get it perfect.

Wrapping up

I hope that after this article, you will no more to fumble around with trial and error to build a wavy shape or pattern. In addition to the online generator, you have all the math secrets behind creating any kind of wave you want!

The article ends here but now you have a powerful tool to create fancy designs that use wavy shapes. Here’s inspiration to get you started…

CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback CodePen Embed Fallback

What about you? Use my online generator (or write the code manually if you already learned all the math by heart) and show me your creations! Let’s have a good collection in the comment section.

How to Create Wavy Shapes & Patterns in CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

How To Customize WordPress Block Theme Cover Templates with Dynamic Post Feature Images

Css Tricks - Fri, 09/23/2022 - 6:15am

If we browse the WordPress theme directory, a majority of themes showcase cover images. It is a feature in popular demand. The cover page trend is true even in the block theme directory screenshots as well.

Let’s consider the following example from Twenty Twenty (a classic theme) which includes a cover template that can be used to display both in single post and page, where the post’s featured image displays at the top that stretches across the browser screen, with post title and other desired meta data below. Cover templates allow creating content that stands out from the traditional constraints of displaying content.

Screenshot showing a single post with Twenty Twenty cover template.

Creating cover templates currently requires writing PHP code as captured here in the Twenty Twenty default theme’s cover template. If we look at the template-parts/content-cover.php file, it contains the code for displaying content when the cover-template is used.

Thus, it is not possible to create a customized cover page if you do not possess a deep knowledge of PHP. For many ordinary WordPress users, the only option is to use plugin like Custom Post Type UI as described in this short video.

Cover sections in block themes

Since WordPress 5.8, theme authors could create custom templates (like single post, author, category, and others) with a top hero section using block editor cover block and bundled into their themes with minimal or no code.

Before diving into how top large cover sections are created in block themes templates, let’s briefly look at the two block themes Twenty Twenty-Two and Wabi by Rich Tabor (full review here).

Screenshot showing cover page thumbnails of Twenty Twenty-Two (left) and Wabi (right) themes.

Behind-the-scenes, Twenty Twenty-Two implements a large header by adding a hidden image stored as a pattern in the header-dark-large parts. Whereas, in the Wabi theme, the large header background color in a single post is implemented with accent background colors and a 50px height spacer block (lines: 5-9). The accent colors are managed by the assets/js/accent-colors.js file.

Many others chose to create a top cover section by using cover block, which allowed users to change the background color and add a static image from Media Library or upload from media devices – without writing any code. With this approach, images from the post featured image block had to be added manually to each single post if you wanted to have the post featured image as the background image in single posts.

Cover Blocks with dynamic post featured image

WordPress 6.0 made available another cool featured image cover blocks feature, which allows use of the featured image of any post or page as the background image in the cover block.

In the following short video, Automattic engineers discuss adding featured images to cover blocks with an example from Archeo theme:

The image block including post featured image block can be further customized using duotone color in theme.json as discussed in this short Connecting The Dots YouTube video (Automattic’s Anne McCarthy).

Use case examples (Wei, Bright Mode)

If we browse the thumbnail images in the block theme directory, we see a majority of them include large cover header sections. If we dig into their template files, they make use of cover blocks with static image background.

Some recently developed themes are using cover blocks with the dynamic post featured image background (e.g., Archeo, Wei, Frost, Bright Mode, etc.). A brief overview of the new feature is available in this short GitHub video.

Screenshot showing cover page thumbnails of Wei (left) and Bright-mode (right) themes.

Combining dynamic accent colors features of Wabi theme with cover and post featured image blocks, Rich Tabor further expands his creativity in his new Wei theme (full review available here) to display dynamic cover images from a single post.

In his Wei announcement post, Rich Tabor writes: “Behind-the-scenes, the single.html template is using a Cover block that leverages the post’s featured image. Then the duotone is applied by the color scheme assigned to the post. This way, just about any image will look fine”.

If you would like to dig deeper into the Wei theme’s header cover block and learn how to create your own, here is a short video from Fränk Klein (WP Development Courses) who explains step-by-step how it was created.

Similar to the Wei theme, Brian Gardner also makes use of cover block with post featured image block in his recent Bright Mode theme to display standout contents with vibrant colors.

Brian told WPTavern: “he loves most about the theme is the way the Cover Block is used on single pages. It pulls the featured image into the Cover block and also offers custom block styles for shadows and full-height options. […] I feel as though this really presents what’s possible with modern WordPress.”

For more detail, here is its demo site and full review of Brian’s Bright Mode theme.

Designing complex layouts with block editor

Recently, WordPress launched a new block editor designed landing homepage and a download page. The announcement attracted mixed reactions from its readers, including from Matt Mullenweg (Automattic) who commented on the 33-days taken to design and launch such a “simple page”. You can find additional behind the scene discussions here.

In response, Jamie Marsland of Pootlepress created this YouTube video where he reproduces a nearly identical homepage in nearly 20 minutes.

Commenting on Marsland video, Sarah Gooding of WP Travern writes: “He is what one might describe as a power user with the block editor. He can quickly shuffle rows, columns, and groups around, adjusting padding and margins as necessary, and assign each section the corresponding color for the design. At this point, this is not something most average WordPress users could do.”

Though the block editor has come a long way, there are still growing pain points to most theme developers and ordinary users to create and design complex layouts with it.

Adding enhancement to TT2 Gopher blocks

In this section, I will walk you through how I added enhancements to the TT2 Gopher Blocks theme that I referenced in my previous article. Inspired by cover blocks from themes that I described earlier, I wanted to add three cover templates (author, category, and single-cover) to the theme.

While browsing websites, we notice two types of cover headers. The mostly observed header is cover section blended with the site header (site title and top navigation) into the cover block (e.g., Twenty Twenty, Twenty Twenty-Two, Wei, Wabi, Frost, Bright Mode, etc.). We also find header cover section which is not blended with site header and positioned just underneath, such as this BBC Future website. For TT2 Gopher blocks theme, I opted for the latter.

Creating cover header patterns

First, let’s create cover header patterns for author, single, and others (categories, tags) templates using cover blocks. Then we will convert them into patterns (as described here previously) and call the respective header cover patterns into the templates.

If you are familiar to working with the block editor, design your header section using cover blocks in the site editor and then convert the cover header code into patterns. However, if you are not familiar with FSE editor, then the easiest way is to copy patterns from the patterns directory in a post, make necessary modification and convert it into a pattern.

In my previous CSS-Tricks article, I discussed in detail on creating and using block patterns. Here is a brief overview of the workflow that I am using to create the single post cover header pattern:

Single post cover header pattern

Step 1: Using FSE interface, let’s create a new blank file and start building block structure as shown on the left panel.

Screenshot of the WordPress UI with the Full Site Editor. A block is being assembled with post date, categories, and post title.

Alternatively, this could be done in a post or page first, and then copy and paste the markup into a pattern file, later.

Step 2: Next, to covert the above markup into a pattern, first we should copy its code markup and paste into a new /patterns/header-single-cover.php in our code editor. We should also add required pattern file header markup (e.g., title, slug, categories, inserter, etc.).

Here is the entire code of the /patterns/header-single-cover.php file:

<?php /** * Title: Header cover single * Slug: tt2gopher/header-cover-single * Categories: tt2gopher-header * Block Types: core/template-part/header * inserter: yes */ ?> <!-- wp:cover {"url":"https://pd.w.org/2022/08/15062ed5f5707b5c5.85694718-2048x1536.jpg","id":100,"dimRatio":0,"overlayColor":"foreground","focalPoint":{"x":"0.40","y":"0.37"},"minHeight":50,"minHeightUnit":"vh","isDark":false,"align":"full","style":{"color":{"duotone":["#000000","#00a5ff"]},"spacing":{"margin":{"top":"0px","bottom":"0px"}}}} --> <div class="wp-block-cover alignfull is-light" style="margin-top:0px;margin-bottom:0px;min-height:50vh"><span aria-hidden="true" class="wp-block-cover__background has-foreground-background-color has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-100" alt="" src="https://pd.w.org/2022/08/15062ed5f5707b5c5.85694718-2048x1536.jpg" style="object-position:40% 37%" data-object-fit="cover" data-object-position="40% 37%"/><div class="wp-block-cover__inner-container"><!-- wp:group {"style":{"elements":{"link":{"color":{"text":"var:preset|color|base"}}},"spacing":{"blockGap":"10px"}},"textColor":"base","layout":{"wideSize":"800px"}} --> <div class="wp-block-group has-base-color has-text-color has-link-color"><!-- wp:group {"style":{"spacing":{"blockGap":"10px"}},"textColor":"primary","layout":{"type":"flex","flexWrap":"nowrap","justifyContent":"center"},"fontSize":"small"} --> <div class="wp-block-group has-primary-color has-text-color has-small-font-size"><!-- wp:post-date {"textColor":"foreground"} /--> <!-- wp:paragraph --> <p>|</p> <!-- /wp:paragraph --> <!-- wp:post-terms {"term":"category","style":{"elements":{"link":{"color":{"text":"var:preset|color|foreground"}}}}} /--></div> <!-- /wp:group --> <!-- wp:post-title {"textAlign":"center","level":1,"style":{"typography":{"fontStyle":"normal","fontWeight":"400"}},"textColor":"foreground","fontSize":"max-60"} /--></div> <!-- /wp:group --></div></div> <!-- /wp:cover -->

Step 3: For this demo, I have used this image from photos directory as a filler background image, and applied the Midnight duotone color. To use post featured image dynamically, we should add "useFeaturedImage":true in the cover block by replacing the above filler image link just before the "dimRatio":50 such that the line 10 should look like the following:

<!-- wp:cover {"useFeaturedImage":true,"dimRatio":0,"overlayColor":"foreground","focalPoint":{"x":"0.40","y":"0.37"},"minHeight":50,"minHeightUnit":"vh","isDark":false,"align":"full","style":{"color":{"duotone":["#000000","#00a5ff"]},"spacing":{"margin":{"top":"0px","bottom":"0px"}}}} -->

Alternatively, the filler image could also be changed by clicking Replace and selecting Use featured image option:

Screenshot of the WordPress UI with ‘Replace’ and ‘Use featured image’ selected.

Now, the header cover patterns should be visible in the patterns inserter panel for use anywhere in the templates, posts, and pages.

Archive cover headers

Inspired by this WP Tavern post and a step-by-step walkthrough to create an author template header, I wanted to create a similar cover header and add to TT2 Gopher theme, too.

First, let’s create the archive cover header pattern for author.html the template as well, following the above workflow. In this case, I am creating this in a new blank page, by adding blocks (as shown below in list view):

Screenshot of the WordPress UI for an Author page using a single post header cover.

In the background for the cover, I used the same image used in the single post header cover.

Because we would like to display a short author biography on the author block, a biographical statement should also be added to the user profile page, or else a blank space will be displayed in the front-end.

The following is the markup code of the header-author-cover, that we will use pattern, in the next step:

<!-- wp:cover {"url":"https://pd.w.org/2022/03/8256241eff74ef542.61868565.jpeg","id":226,"dimRatio":10,"focalPoint":{"x":"0.50","y":"0.75"},"minHeight":200,"minHeightUnit":"px","isDark":false,"align":"full","style":{"color":{"duotone":["#000000","#00a5ff"]}}} --> <div class="wp-block-cover alignfull is-light" style="min-height:200px"><span aria-hidden="true" class="wp-block-cover__background has-background-dim-10 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-226" alt="" src="https://pd.w.org/2022/03/8256241eff74ef542.61868565.jpeg" style="object-position:50% 75%" data-object-fit="cover" data-object-position="50% 75%"/><div class="wp-block-cover__inner-container"><!-- wp:group {"layout":{"inherit":true}} --> <div class="wp-block-group"><!-- wp:group {"style":{"spacing":{"padding":{"top":"1rem","right":"2rem","bottom":"1rem","left":"2rem"}}},"layout":{"type":"flex","flexWrap":"nowrap"}} --> <div class="wp-block-group" style="padding-top:1rem;padding-right:2rem;padding-bottom:1rem;padding-left:2rem"><!-- wp:avatar {"size":70,"isLink":true,"align":"right","style":{"border":{"radius":"9999px"}}} /--> <!-- wp:group --> <div class="wp-block-group"><!-- wp:group {"style":{"spacing":{"blockGap":"6px"}},"layout":{"type":"flex"},"fontSize":"large"} --> <div class="wp-block-group has-large-font-size"><!-- wp:paragraph {"textColor":"foreground","fontSize":"large"} --> <p class="has-foreground-color has-text-color has-large-font-size">Published by:</p> <!-- /wp:paragraph --> <!-- wp:post-author-name {"isLink":true,"style":{"typography":{"fontStyle":"large","fontWeight":"600"},"elements":{"link":{"color":{"text":"var:preset|color|background"}}}},"textColor":"foreground"} /--></div> <!-- /wp:group --> <!-- wp:post-author-biography {"textColor":"foreground","fontSize":"small"} /--> <!-- wp:separator {"backgroundColor":"foreground"} --> <hr class="wp-block-separator has-text-color has-foreground-color has-alpha-channel-opacity has-foreground-background-color has-background"/> <!-- /wp:separator --></div> <!-- /wp:group --></div> <!-- /wp:group --></div> <!-- /wp:group --></div></div> <!-- /wp:cover -->

To covert the markup into a header-author-cover pattern, we should add the required pattern file header markup as described earlier. By editing the header-author-cover.php pattern, we can create similar header covers for tags, taxonomy, and other custom templates.

The header-category-cover.php pattern for my category.html template is available on GitHub.

Creating Templates with header cover blocks

WordPress 6.0 and the recent Gutenberg 13.7 extended template creating features into the block editor, thus making it possible for many WordPress users, without deep knowledge of coding, to create their customized templates.

For more detailed information and use cases, here is a thorough customization note by Justin Tadlock.

Block editor allows creating various types of templates, including cover templates. Let’s briefly overview how combining cover block and post featured image block with new template UI makes easy to create various types of cover custom templates even with no or low coding skills.

Screenshot of the WordPress UI displaying available templates provided by TT2 Gopher Blocks – Single, Page, Index, Home, 404, Blank, and Archive.

Creating templates has been made much easier with Gutenberg 13.7. How to create block templates with codes and in site editor is described in the Theme handbook and in my previous article.

Author template with cover block

Top (header section) markup of the author.html template is shown below (line 6):

<!-- wp:template-part {"slug":"header-small-dark","theme":"TT2-GOPHER-V2","tagName":"header"} /--> <!-- wp:group {"tagName":"main","style":{"spacing":{"margin":{"top":"0","bottom":"0px"},"padding":{"bottom":"80px"},"blockGap":"0px"}},"className":"site-content"} --> <main class="wp-block-group site-content" style="margin-top:0;margin-bottom:0px;padding-bottom:80px"> <!-- wp:pattern {"slug":"tt2gopher/header-author-cover"} /--> ... ... ... <!-- /wp:group --> ...

Here are screenshots of cover headers for the author.html and category.html templates:

Screenshot of Author Page header (left) with author name, avatar, and biography. And screenshot of Category Page header (right).

The entire code for both templates is available on GitHub.

Single post with cover block

To display cover block in our single post, we have to call the header-cover-single pattern below the header section (line 3):

<!-- wp:template-part {"slug":"header-small-dark","tagName":"header"} /--> <!-- wp:pattern {"slug":"tt2gopher/header-cover-single"} /--> <!-- wp:spacer {"height":32} --> <div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div> <!-- /wp:spacer --> .... .... ....

Here is a screen capture showing the front-end view of the single post with the header cover section:

Screenshot of TT2 Gopher Blocks Single Post with Header Cover Section Pattern.

The entire single-cover.html template is available on GitHub.

You can find additional step-by-step walkthrough tutorials on creating a hero header post section and using post featured image background cover blocks on WP Tavern and Full Site Editing website.

There you have it!

Helpful Resources Featured image cover block Blog posts

Even though the block themes, in general, are getting lots of pushback from WordPress community members, in my opinion, they are the future of WordPress, too. With block themes, amateur theme authors, without the deep coding skills and mastery of PHP and JavaScript languages, can now create themes with complex layouts with a hero cover section as described in this article combined with patterns and style variations.

As an early Gutenberg user, I couldn’t be more excited with the new theming tools like create block theme plugin and others which allow theme authors to achieve the following directly from block editor UI without writing any code:

  • (i) create
  • (ii) overwrite theme files and export
  • (iii) generate blank or a child theme, and
  • (iv) modify and save style variation of the current theme

Additionally, the recent iterations of the Gutenberg plugin allow enabling fluid typography and layout alignments and other stylistic controls using only theme.json file without JavaScript and a line of CSS rules.

Thank you for reading and share your comments and thoughts below!

How To Customize WordPress Block Theme Cover Templates with Dynamic Post Feature Images originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

The Web is Good Now

Css Tricks - Thu, 09/22/2022 - 12:07pm

The video of Chris Coyier’s talk at CascadiaJS 2022 is now available. It’s his first in-person talk in more than two years, so it’s great to see our good friend back on stage slinging gems on what makes the web good these days.

Container Queries! WAAPI! Scroll Timelines! offset-path! FLIP! Variable fonts! Fluid type! We really are all-powerful front-end developers these days.

Chris really packs a bunch into a 25-minute slot. It feels good to pause for that brief amount of time to reflect on the great new things for building websites and celebrate the fact that we get to use them.

And there’s nothing better than watching Chris greet the entire room as a bunch of “web nerds”. &#x1f913;

To Shared LinkPermalink on CSS-Tricks

The Web is Good Now originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

How I Made an Icon System Out of CSS Custom Properties

Css Tricks - Thu, 09/22/2022 - 5:17am

SVG is the best format for icons on a website, there is no doubt about that. It allows you to have sharp icons no matter the screen pixel density, you can change the styles of the SVG on hover and you can even animate the icons with CSS or JavaScript.

There are many ways to include an SVG on a page and each technique has its own advantages and disadvantages. For the last couple of years, I have been using a Sass function to import directly my icons in my CSS and avoid having to mess up my HTML markup.

I have a Sass list with all the source codes of my icons. Each icon is then encoded into a data URI with a Sass function and stored in a custom property on the root of the page.

TL;DR

What I have for you here is a Sass function that creates a SVG icon library directly in your CSS.

The SVG source code is compiled with the Sass function that encodes them in data URI and then stores the icons in CSS custom properties. You can then use any icon anywhere in your CSS like as if it was an external image.

This is an example pulled straight from the code of my personal site:

.c-filters__summary h2:after { content: var(--svg-down-arrow); position: relative; top: 2px; margin-left: auto; animation: closeSummary .25s ease-out; } Demo CodePen Embed Fallback Sass structure /* All the icons source codes */ $svg-icons: ( burger: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0...' ); /* Sass function to encode the icons */ @function svg($name) { @return url('data:image/svg+xml, #{$encodedSVG} '); } /* Store each icon into a custom property */ :root { @each $name, $code in $svg-icons { --svg-#{$name}: #{svg($name)}; } } /* Append a burger icon in my button */ .menu::after { content: var(--svg-burger); }

This technique has both pros and cons, so please take them into account before implementing this solution on your project:

Pros
  • There are no HTTP requests for the SVG files.
  • All of the icons are stored in one place.
  • If you need to update an icon, you don’t have to go over each HTML templates file.
  • The icons are cached along with your CSS.
  • You can manually edit the source code of the icons.
  • It does not pollute your HTML by adding extra markup.
  • You can still change the color or some aspect of the icon with CSS.
Cons
  • You cannot animate or update a specific part of the SVG with CSS.
  • The more icons you have, the heavier your CSS compiled file will be.

I mostly use this technique for icons rather than logos or illustrations. An encoded SVG is always going to be heavier than its original file, so I still load my complex SVG with an external file either with an <img> tag or in my CSS with url(path/to/file.svg).

Encoding SVG into data URI

Encoding your SVG as data URIs is not new. In fact Chris Coyier wrote a post about it over 10 years ago to explain how to use this technique and why you should (or should not) use it.

There are two ways to use an SVG in your CSS with data URI:

  • As an external image (using background-image,border-image,list-style-image,…)
  • As the content of a pseudo element (e.g. ::before or ::after)

Here is a basic example showing how you how to use those two methods:

CodePen Embed Fallback

The main issue with this particular implementation is that you have to convert the SVG manually every time you need a new icon and it is not really pleasant to have this long string of unreadable code in your CSS.

This is where Sass comes to the rescue!

Using a Sass function

By using Sass, we can make our life simpler by copying the source code of our SVG directly in our codebase, letting Sass encode them properly to avoid any browser error.

This solution is mostly inspired by an existing function developed by Threespot Media and available in their repository.

Here are the four steps of this technique:

  • Create a variable with all your SVG icons listed.
  • List all the characters that needs to be skipped for a data URI.
  • Implement a function to encode the SVGs to a data URI format.
  • Use your function in your code.
1. Icons list /** * Add all the icons of your project in this Sass list */ $svg-icons: ( burger: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24.8 18.92" width="24.8" height="18.92"><path d="M23.8,9.46H1m22.8,8.46H1M23.8,1H1" fill="none" stroke="#000" stroke-linecap="round" stroke-width="2"/></svg>' ); 2. List of escaped characters /** * Characters to escape from SVGs * This list allows you to have inline CSS in your SVG code as well */ $fs-escape-chars: ( ' ': '%20', '\'': '%22', '"': '%27', '#': '%23', '/': '%2F', ':': '%3A', '(': '%28', ')': '%29', '%': '%25', '<': '%3C', '>': '%3E', '\\': '%5C', '^': '%5E', '{': '%7B', '|': '%7C', '}': '%7D', ); 3. Encode function /** * You can call this function by using `svg(nameOfTheSVG)` */ @function svg($name) { // Check if icon exists @if not map-has-key($svg-icons, $name) { @error 'icon “#{$name}” does not exists in $svg-icons map'; @return false; } // Get icon data $icon-map: map-get($svg-icons, $name); $escaped-string: ''; $unquote-icon: unquote($icon-map); // Loop through each character in string @for $i from 1 through str-length($unquote-icon) { $char: str-slice($unquote-icon, $i, $i); // Check if character is in symbol map $char-lookup: map-get($fs-escape-chars, $char); // If it is, use escaped version @if $char-lookup != null { $char: $char-lookup; } // Append character to escaped string $escaped-string: $escaped-string + $char; } // Return inline SVG data @return url('data:image/svg+xml, #{$escaped-string} '); } 4. Add an SVG in your page button { &::after { /* Import inline SVG */ content: svg(burger); } }

If you have followed those steps, Sass should compile your code properly and output the following:

button::after { content: url("data:image/svg+xml, %3Csvg%20xmlns=%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox=%270%200%2024.8%2018.92%27%20width=%2724.8%27%20height=%2718.92%27%3E%3Cpath%20d=%27M23.8,9.46H1m22.8,8.46H1M23.8,1H1%27%20fill=%27none%27%20stroke=%27%23000%27%20stroke-linecap=%27round%27%20stroke-width=%272%27%2F%3E%3C%2Fsvg%3E "); } CodePen Embed Fallback Custom properties

The now-implemented Sass svg() function works great. But its biggest flaw is that an icon that is needed in multiple places in your code will be duplicated and could increase your compiled CSS file weight by a lot!

To avoid this, we can store all our icons into CSS variables and use a reference to the variable instead of outputting the encoded URI every time.

We will keep the same code we had before, but this time we will first output all the icons from the Sass list into the root of our webpage:

/** * Convert all icons into custom properties * They will be available to any HTML tag since they are attached to the :root */ :root { @each $name, $code in $svg-icons { --svg-#{$name}: #{svg($name)}; } }

Now, instead of calling the svg() function every time we need an icon, we have to use the variable that was created with the --svg prefix.

button::after { /* Import inline SVG */ content: var(--svg-burger); } Optimizing your SVGs

This technique does not provide any optimization on the source code of the SVG you are using. Make sure that you don’t leave unnecessary code; otherwise they will be encoded as well and will increase your CSS file size.

You can check this great list of tools and information on how to optimize properly your SVG. My favorite tool is Jake Archibald’s SVGOMG — simply drag your file in there and copy the outputted code.

Bonus: Updating the icon on hover

With this technique, we cannot select with CSS specific parts of the SVG. For example, there is no way to change the fill color of the icon when the user hovers the button. But there are a few tricks we can use with CSS to still be able to modify the look of our icon.

For example, if you have a black icon and you want to have it white on hover, you can use the invert() CSS filter. We can also play with the hue-rotate() filter.

CodePen Embed Fallback Bonus #2: Updating the icon using CSS mask-image property

Another trick to be able to change the color of your icon, is to use it as a mask on your pseudo-element with a background. Set your pseudo-element as inline-block with a background-color and define a width & height for the size needed.

Once you have a rectangle with the color needed, apply those four values to only keep the shape of the SVG needed:

  • mask-image: var(--svg-burger): The reference to our icon.
  • mask-repeat: no-repeat: To prevent the mask to be duplicated.
  • mask-size: contain: To make the icon fit perfectly in the rectangle.
  • mask-position: center: To center our icon in the pseudo-element.

Don’t forget that all CSS mask properties still need to be prefixed with -webkit- for most browsers as of September 2022.

CodePen Embed Fallback

Thanks to Christopher and Mike for letting me know about this trick in the comments!

That’s it!

I hope you find this little helper function handy in your own projects. Let me know what you think of the approach — I’d be interested to know how you’d make this better or tackle it differently!

How I Made an Icon System Out of CSS Custom Properties originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS Rules vs. CSS Rulesets

Css Tricks - Wed, 09/21/2022 - 11:53am

The latest spec:

A style rule is a qualified rule that associates a selector list with a list of property declarations and possibly a list of nested rules. They are also called rule sets in CSS2.

Louis Lazaris:

As the above quote from W3C indicates, it seems like the W3C considers “rule set” to be a bit of an outdated term, preferring the term “style rule” (or possibly “rule” for short).

I never noticed that! “Rule set” is so gosh darned branded on my brain that it’s gonan take losing a lot of muscle memory to start using “style rule” instead. I didn’t see a specific note in the spec’s Changes section, but you can see the change in the table of contents between versions:

Louis nicely sums up the parts of a style rule as well:

/* Everything below is a style rule (or rule set, or just rule) */ section { /* Everything between the braces is a declaration block */ margin: 0 20px; /* This line is an individual declaration */ color: #888; /* Another declaration */ }

I know nothing of the context and, at first, I was gonna poo-poo the change, but “style rule” really makes sense the more I sit with it. If the property:value pairs are declarations that sit in a declaration block, then we’ve got something less like a set of rules and more like one rule that defines the styles for a selector with a block of style declarations. &#x1f44c;

Once again, naming things is hard.

To Shared LinkPermalink on CSS-Tricks

CSS Rules vs. CSS Rulesets originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Featured Fonts

Typography - Wed, 09/21/2022 - 2:25am

Read the book, Typographic Firsts

These font families might have flown under your radar: Six must-have font families. All available from the ILT store.

The post Featured Fonts appeared first on I Love Typography.

Making a Real-Time Clock With a Conic Gradient Face

Css Tricks - Mon, 09/19/2022 - 2:58am

Gradients have been a part of the CSS spectrum for quite some time now. We see a lot of radial and linear gradients in a lot of projects, but there is one type of gradient that seems to be a bit lonely: the conic gradient. We’re going to make a watch face using this type of gradient.

Working with conic gradients

What we’re making consists of a gradient with color transitions rotated around a center point and can have multiple color values. For this clock to work, we will also be using the angle value of a conic gradient which defines the rotation or starting point. The angle is defined by using a from value.

background-image: conic-gradient(from 45deg, #6e7dab, #5762d5);

What is interesting about this, is that a starting angle can have a negative value in CSS, which will come in handy later.

A simple elegant example of a conical gradient:

CodePen Embed Fallback Building our basic clock

Let’s start by adding some HTML for the clock and the hands:

Let’s create some default styling for our clock. For this to work properly, we will update CSS variables with JavaScript later on, so let’s scope these variables inside our .clock selector. For easy tweaking, let’s add the colors of the hands as well.

.clock { /* general clock vars */ --hour-hand-color: #000; --hour-hand-degrees: 0deg; --minute-hand-color: #000; --minute-hand-degrees: 0deg; --second-hand-color: hotpink; --second-hand-degrees: 0deg; position: relative; min-width: 320px; width: 25vw; height: 25vw; min-height: 320px; border-radius: 50%; margin: 0 auto; border: 7px solid #000; } /* clock hands */ .hand { position: absolute; left: 50%; bottom: 50%; height: 45%; width: 4px; margin-left: -2px; background: var(--second-hand-color); border-radius: 6px; transform-origin: bottom center; transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1); } .second-hand { transform: rotate(var(--second-hand-degrees)); } .hour-hand { height: 35%; border-radius: 40px; background-color: var(--hour-hand-color); transform: rotate(var(--hour-hand-degrees)); } .minute-hand { height: 50%; background: var(--minute-hand-color); transform: rotate(var(--minute-hand-degrees)); }

This sets us up with the general styling we need for the clock. We’ve set transform-origin on the hands so that they properly rotate around the face of the clock. There are also a few custom properties in there to set angles on the hands that we’ll update with JavaScript to get the timing just right so that each hand maps to seconds, minutes, and hours accordingly.

Here’s what we have so far:

CodePen Embed Fallback

Alright, let’s move on to updating those custom properties!

Adding the JavaScript for our basic clock

First off, we’re going to target our clock and create a function:

const clock = document.getElementById("clock"); function setDate() { // Code to set the current time and hand angles. } setDate();

Inside of our function we’re going to fetch the current time using the Date() function to calculate the correct angle of the hands:

const now = new Date(); const secondsAngle = now.getSeconds() * 6; const minsAngle = now.getMinutes() * 6 + secondsAngle / 60; const hourAngle = ((now.getHours() % 12) / 12) * 360 + minsAngle / 12;

Here is how this calculation works:

  • Seconds: We take 60 seconds and multiply it by 6, which happens to be 360, the perfect number of angles in a full circle.
  • Minutes: Same as seconds, but now we add the seconds angle and divide it by 60 to increase the angle just a little bit within the minute for a more accurate result.
  • Hours: First, we calculate the remainder of the hour and divide it by 12. Then we divide that remainder by 12 again to get a decimal value we can multiply by 360. For example, when we’re at the 23rd hour, 23 / 12 = remain 11. Divide this by 12 and we get 0.916 which then gets multiplied by 360 for a grand total of 330. Here, we will do the same thing we did with the minutes and add the minutes angle, divided by 12, for a more accurate result.

Now that we have our angles, the only thing left to do is to update the variables of our clock by adding the following at the end of our function:

clock.style.setProperty("--second-hand-degrees", secondsAngle + "deg"); clock.style.setProperty("--minute-hand-degrees", minsAngle + "deg"); clock.style.setProperty("--hour-hand-degrees", hourAngle + "deg");

Last, but not least, we will trigger the function with an interval of a second to get a working clock:

const clock = document.getElementById("clock"); function setDate() { // etc. } // Tick tick tick setInterval(setDate, 1000); setDate();

See the working demo of our basic clock:

CodePen Embed Fallback Applying this to a conical gradient

OK, so the hands of our clock are working. What we really want is to map them to a conical gradient that updates as the time changes. You may have seen the same effect if you have an Apple Watch with the “Gradient” face active:

Credit: Macworld

To do this, let’s start by updating our .clock element with a conic gradient and two custom properties that control the starting and ending angles :

.clock { /* same as before */ /* conic gradient vars */ --start: 0deg; --end: 0deg; /* same as before */ background: conic-gradient( from var(--start), rgb(255 255 255) 2deg, rgb(0 0 0 / 0.5) var(--end), rgb(255 255 255) 2deg, rgb(0 0 0 / 0.7) ); }

You can play around with this a bit to style it just the way you like it. I added some extra colors in the gradient to my liking, but as long as you have a starting point and an ending point, you’re good to go.

CodePen Embed Fallback

Next up, we will update our setDate() function so that it updates the variables for our starting and ending points on the conic gradient. The starting point will be our seconds hand, which is easy to find because it will be the same as the angle of our minutes. To make this end at the hours hand, we should make our ending point the same as the hourAngle variable in the script, but subtract our starting point from it.

let startPosition = minsAngle; let endPosition = hourAngle - minsAngle;

Now we can update our variables with JavaScript again:

clock.style.setProperty("--start", startPosition + "deg"); clock.style.setProperty("--end", endPosition + "deg");

It looks like we could be done at this point, but there is a catch! This calculation works fine as long as the minutes hand has a smaller angle than the hours hand. Our conic gradient will get messy the moment when the minutes hand has moved past it. To fix this, we will use a negative value as a starting point. Luckily, it’s easy to spot when this happens. Before updating our variables we’ll add the following:

if (minsAngle > hourAngle) { startPosition = minsAngle - 360; endPosition = hourAngle - startPosition; }

By subtracting 360 from our minutes angle, we are able to set a negative value for our startposition variable. Because of this negative starting point, our end position should be updated by the hour angle, subtracted by the starting position.

There we go — now the hour and minute hands are set to gradient angles:

CodePen Embed Fallback

That’s it! But don’t let that stop you from taking this even further. Create your own styles and share them with me in the comments so I can check them out.. Here is a little inspiration to get you going:

CodePen Embed Fallback

Making a Real-Time Clock With a Conic Gradient Face originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

More Details on `details`

Css Tricks - Thu, 09/15/2022 - 3:12am

A lot of chatter around the ol’ <details> and <summary> elements lately! I saw Lea Verou recently tweet an observation about the element’s display behavior and that sorta splintered into more observations and usage notes from folks, including a revived discussion on whether <summary> should be allowed to contain interactive elements or not.

There are a lot of dots to connect and I’ll do my best here to do exactly that.

Can we change the display of elements nested in the <details> element?

In the app I’m building, I’m using <details> for panels but ran into some sizing weirdness.
Flexbox: https://t.co/noZvxAN35G
Grid: https://t.co/pis0lPjvXk
At first I thought it was a bug, but all three engines agree. Nothing in the UA stylesheet for <details> seems to explain it.

— Lea Verou (@LeaVerou) August 28, 2022

Super weird! If we crack open DevTools, the user agent stylesheet tells us <details> is a displayed as a block element.

Notice the required <summary> element and the two additional <div>s in there. We can override the display, right?

What we might expect is that <details> now has an explicit height of 40vh and three rows where the third row takes up the remaining space leftover from the first two. Like this:

Ugh, but the third row doesn’t… do… that.

Apparently what we’re dealing with is a grid container that is unable to apply grid behavior to its grid items. But the HTML spec tells us:

The details element is expected to render as a block box. The element is also expected to have an internal shadow tree with two slots.

(Emphasis mine)

And a little later:

The details element’s second slot is expected to have its style attribute set to “display: block; content-visibility: hidden;” when the details element does not have an open attribute. When it does have the open attribute, the style attribute is expected to be removed from the second slot.

(Emphasis mine, again)

So, the spec says the second slot — the two additional <div>s from the example — are only coerced into being block elements when <details> is closed. When it’s open — <details open> — they should conform to the grid display that overrides the user agent styling… right?

That’s the debate. I get that slots are set to display: contents by default, but jamming nested elements into slots and removing the ability to style them seems off. Is it a spec issue that the contents are slots, or a browser issue that we cannot override their display even though they are in the box tree? Smarter people can enlighten me but it seems like an incorrect implementation.

Is <details> a container or an interactive element?

Lots of folks are using <details> to toggle menus open and closed. It’s a practice popularized by GitHub.

Seems reasonable. The spec sure allows it:

The details element represents a disclosure widget from which the user can obtain additional information or controls.

(Emphasis mine)

Alright, so we might expect that <details> is the container (it has an implicit role=group) and <summary> is an interactive element that sets the container’s open state. Makes sense since <summary> has an implcit button role in some contexts (but no corresponding WAI-ARIA role).

But Melanie Sumner did some digging that not only seems to contradict that, but leads to the conclusion that using <details> as a menu probably ain’t the best thing. See what happens when <details> is rendered without the <summary> element:

CodePen Embed Fallback

It does exactly what the spec suggests when it’s missing a <summary> — it makes its own:

The first summary element child of the element, if anyrepresents the summary or legend of the details. If there is no child summary element, the user agent should provide its own legend (e.g. “Details”).

(Emphasis mine)

Melanie ran that through an HTML validator and — surprise! — it’s invalid:

So, <details> requires the <summary>. And when <summary> is missing, <details> creates it’s own, though it’s relayed as invalid markup. It’s all hunky-dory and valid when <summary> is there:

All of which leads to a new question: why is <summary> given an implcit button role when <details> is what appears to be the interactive element? Perhaps this is another case where the browser implementation is incorrect? Then again, the spec does categorize both as interactive elements. You can see how utterly confusing all of this becomes.

Either way, Melanie’s ultimate conclusion that we ought to avoid using <details> for menus is based on how assistive tech reads and announces <details> that contain interactive elements. The element is announced, but there is no mention of interactive controls beyond that until you, er, interact with <details>. Only then will something like a list of links be announced.

Besides, content inside a collapsed <details> is excluded from in-page searching (except in Chromium browsers, which can access the collapsed content at the time of writing), making things even more difficult to find.

Should <summary> allow interactive elements?

That’s the question posed in this open thread. The idea is that something like this would be invalid:

<details> <summary><a href="...">Link element</a></summary> </details> <!-- or --> <details> <summary><input></summary> </details>

Scott O’Hara sums up nicely why this is an issue:

The link is not discoverable at all to JAWS when navigating with its virtual cursor. If navigating to the summary element via the Tab key, JAWS announces “example text, button” as the name and role of the element. If hitting Tab key again, JAWS again announces “example text, button” even though keyboard focus is on the link.

[…]

There is more I could go on about with the various problems different AT have with the content model for summary… but that would just extend this comment out beyond what is necessary. tldr; the summary content model produces very inconsistent and sometimes just flat out broken experiences for people using AT.

Scott opened tickets to correct this behavior in Chromium and WebKit. Thanks, Scott!

Yet, it’s valid HTML:

Scott goes further in a separate blog post. For example, he explains how slapping role=button on <summary> might seem like a reasonable fix to ensure it is consistently announced by assistive tech. That would also settle the debate over whether <summary> should allow interactive elements because buttons cannot contain interactive elements. The only problem is that Safari then treats <summary> as a standard button, which loses its expanded and collapsed states. So, the correct role is announced, but now its state is not. &#x1f643;

Where do we go now?

Are you scared to use <details>/<summary> with all of these issues and inconsistencies? I sure am, but only insofar as to make sure that what’s in it provides the right sort of experience and expectations for users.

I’m just glad these conversations are happening and that they’re taking place in the open. Because of that, you can comment on Scott’s three proposed solutions for how the content model for <summary> is defined, upvote his tickets, and report your own issues and use cases while you’re at it. Hopefully, the better we understand how the elements are used and what we expect them to do, the better they are implemented.

More Details on `details` originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

When is it OK to Disable Text Selection?

Css Tricks - Wed, 09/14/2022 - 3:03am

Using CSS, it’s possible to prevent users from selecting text within an element using user-select: none. Now, it’s understandable why doing so might be considered “controversial”. I mean, should we be disabling standard user behaviors? Generally speaking, no, we shouldn’t be doing that. But does disabling text selection have some legitimate (albeit rare) use-cases? I think so.

In this article we’ll explore these use cases and take a look at how we can use user-select: none to improve (not hinder) user experiences. It’s also worth nothing that the user-select property has other values besides none that can be used to alter the behavior of text selection rather than disable it completely, and another value that even enforces text selection, so we’ll also take a look at those.

Possible user-select values

Let’s kick things off by running through the different user-select values and what they do.

Applying user-select: none; to an element means that its text content and nested text content won’t be functionally selectable or visually selectable (i.e. ::selection won’t work). If you were to make a selection that contained some non-selectable content, the non-selectable content would be omitted from the selection, so it’s fairly well implemented. And the support is great.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari4*2*10*12*3.1*Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari1051042.1*3.2*

Conversely, user-select: text makes the content selectable. You’d use this value to overwrite user-select: none.

user-select: contain is an interesting one. Applying it means that if a selection begins within the element then it must end within it too, containing it. This oddly doesn’t apply when the selection begins before the element, however, which is probably why no browser currently supports it. (Internet Explorer and earlier versions of Microsoft Edge previously supported it under the guise of user-select: element.)

With user-select: all, selecting part of the element’s content results in all of it being selected automatically. It’s all or nothing, which is very uncompromising but useful in circumstances where users are more likely to copy content to their clipboard (e.g. sharing and embedding links, code snippets, etc.). Instead of double-clicking, users will only need to click once for the content to auto-select.

Be careful, though, since this isn’t always the feature you think it is. What if users only want to select part of the content (e.g. only the font name part of a Google Fonts snippet or one part of a code snippet)? It’s still better to handle ”copy to clipboard” using JavaScript in many scenarios.

A better application of user-select: all is to ensure that quotes are copied entirely and accurately.

The behavior of user-select: auto (the initial value of user-select) depends on the element and how it’s used. You can find out more about this in our almanac.

Now let’s turn to exploring use cases for user-select: none…

Stripping non-text from the selection

When you’re copying content from a web page, it’s probably from an article or some other type of long-form content, right? You probably don’t want your selection to include images, emoji (which can sometimes copy as text, e.g. “:thinkingface:”), and other things that you might expect to find wrapped in an <aside> element (e.g. in-article calls to action, ads, or something else that’s not part of the main content).

To prevent something from being included in selections, make sure that it’s wrapped in an HTML element and then apply user-select: none to it:

<p>lorem <span style="user-select: none">&#x1f914;</span> ipsum</p> <aside style="user-select: none"> <h1>Heading</h1> <p>Paragraph</p> <a>Call to action</a> </aside>

In scenarios like this, we’re not disabling selection, but rather optimizing it. It’s also worth mentioning that selecting doesn’t necessarily mean copying — many readers (including myself) like to select content as they read it so that they can remember where they are (like a bookmark), another reason to optimize rather than disable completely.

Preventing accidental selection

Apply user-select: none to links that look like buttons (e.g. <a href="/whatever" class="button">Click Me!</a>).

It’s not possible to select the text content of a <button> or <input type="submit"> because, well, why would you? However, this behavior doesn’t apply to links because traditionally they form part of a paragraph that should be selectable.

Fair enough.

We could argue that making links look like buttons is an anti-pattern, but whatever. It’s not breaking the internet, is it? That ship has sailed anyway, so if you’re using links designed to look like buttons then they should mimic the behavior of buttons, not just for consistency but to prevent users from accidentally selecting the content instead of triggering the interaction.

I’m certainly prone to selecting things accidentally since I use my laptop in bed more than I care to admit. Plus, there are several medical conditions that can affect control and coordination, turning an intended click into an unintended drag/selection, so there are accessibility concerns that can be addressed with user-select too.

Interactions that require dragging (intentionally) do exist too of course (e.g. in browser games), but these are uncommon. Still, it just shows that user-select does in fact have quite a few use-cases.

Avoiding paywalled content theft

Paywalled content gets a lot of hate, but if you feel that you need to protect your content, it’s your content — nobody has the right steal it just because they don’t believe they should pay for it.

If you do want to go down this route, there are many ways to make it more difficult for users to bypass paywalls (or similarly, copy copyrighted content such as the published work of others).

Blurring the content with CSS:

article { filter: blur(<radius>); }

Disabling the keyboard shortcuts for DevTools:

document.addEventListener("keydown", function (e) { if (e.keyCode == 123) e.preventDefault(); else if ((e.ctrlKey || e.metaKey) && e.altKey && e.keyCode == 73) e.preventDefault(); else if ((e.ctrlKey || e.metaKey) && e.altKey && e.keyCode == 74) e.preventDefault(); else if ((e.ctrlKey || e.metaKey) && e.altKey && e.keyCode == 85) e.preventDefault(); });

Disabling access to DevTools via the context menu by disabling the context menu itself:

document.addEventListener("contextmenu", e => e.preventDefault())

And of course, to prevent users from copying the content when they’re not allowed to read it at the source, applying user-select: none:

<article style="user-select: none"> Any other use cases?

Those are the three use cases I could think of for preventing text selection. Several others crossed my mind, but they all seemed like a stretch. But what about you? Have you had to disable text selection on anything? I’d like to know!

When is it OK to Disable Text Selection? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

WebKit Features in Safari 16.0

Css Tricks - Tue, 09/13/2022 - 6:14am

Whew boy, Safari 16 is officially out in the wild and it packs in a bunch of features, some new and exciting (Subgrid! Container Queries! Font Palettes!) and others we’ve been waiting on for better cross-browser support (Motion Path! Overscroll Behavior! AVIF!). I imagine Jen Simmons typing cheerfully writing out all of the new goodies in the roundup announcement.

Source: WebKit.org

Just gonna drop in the new CSS features from the release notes:

  • Added size queries support for Container Queries. Chrome started supporting it in Version 105, so all we need is Firefox to join the party to get The Big Three™ covered.
  • Added support for Container Query Units. These units go hand-in-hand with Container Queries. Once again, we need Firefox.
  • Added support for Subgrid. Now it’s Safari and Firefox with support coverage. The good news is that Chrome is currently developing it as well.
  • Added support for animatable Grids. Very cool! Chrome has always had some implementation of this and Firefox started supporting it back in 2019.
  • Added support for Offset Path. This is also known as Motion Path, and we’ve had broad browser support since 2020. It’s nice to see Safari on board.
  • Added support for Overscroll Behavior. Now we can modify “scroll chaining” and overflow affordances with the overscroll-behavior property.
  • Added support for text-align-last. Now we’re all set with cross-browser support for this property!
  • Added support for the resolution media query. All set here as well!

There are quite a few nice updates to Safari’s developer tools, too. We’ve got a Flexbox inspector, a Timelines tab (with an experimental screenshots timeline), and Container Queries info, to name a few. There’s a full 32-minute video that walks through everything, too.

I thought Safari 15 was a pretty killer release, but 16 is pretty epic in comparison. I know there’s a “Safari is the new Internet Explorer” vibe in some circles, but I’m happy to see big jumps like this and appreciate all the forward momentum. Go Safari Team!

To Shared LinkPermalink on CSS-Tricks

WebKit Features in Safari 16.0 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Syndicate content
©2003 - Present Akamai Design & Development.