Tag: Styles

Managing CSS Styles in a WordPress Block Theme

The way we write CSS for WordPress themes is in the midst of sweeping changes. I recently shared a technique for adding fluid type support in WordPress by way of theme.json, a new file that WordPress has been pushing hard to become a central source of truth for defining styles in WordPress themes that support full-site editing (FSE) features.

Wait, no style.css file? We still have that. In fact, style.css is still a required file in block themes, though its role is greatly reduced to meta information used for registering the theme. That said, the fact is that theme.json is still in active development, meaning we’re in a transitional period where you might find styles defined there, in styles.css or even at the block level.

So, what does styling actually look like in these WordPress FSE days? That’s what I want to cover in this article. There’s a lack of documentation for styling block themes in the WordPress Theme Developer Handbook, so everything we’re covering here is what I’ve gathered about where things currently are as well as discussions about the future of WordPress theming.

The evolution of WordPress styles

The new developmental features that are included in WordPress 6.1 get us closer to a system of styles that are completely defined in theme.json, but there is still be plenty of work to do before we can fully lean on it. One way we can get an idea of what’s coming in future releases is by using the Gutenberg plugin. This is where experimental features are often given a dry run.

Another way we can get a feel for where we are is by looking at the evolution of default WordPress themes. To date, there are three default themes that support full-site editing:

But don’t start trading the CSS in style.css for JSON property-value pairs in theme.json just yet. There are still CSS styling rules that need to be supported in theme.json before we think about doing that. The remaining significant issues are currently being discussed with an aim to fully move all the CSS style rules into theme.json and consolidate different sources of theme.json into a UI for for setting global styles directly in the WordPress Site Editor.

The Global Styles UI is integrated with the right panel in the Site Editor. (Credit: Learn WordPress)

That leaves us in a relatively tough spot. Not only is there no clear path for overriding theme styles, but it’s unclear where the source of those styles even come from — is it from different layers of theme.json files, style.css, the Gutenberg plugin, or somewhere else?

Why theme.json instead of style.css?

You might be wondering why WordPress is moving toward a JSON-based definition of styles instead of a traditional CSS file. Ben Dwyer from the Gutenberg development team eloquently articulates why the theme.json approach is a benefit for theme developers.

It’s worth reading Ben’s post, but the meat is in this quote:

Overriding CSS, whether layout, preset, or block styles, presents an obstacle to integration and interoperability: visual parity between the frontend and editor becomes more difficult to maintain, upgrades to block internals may conflict with overrides. Custom CSS is, furthermore, less portable across other block themes.

By encouraging theme authors to use theme.json API where possible, the hierarchy of “base > theme > user” defined styles can be resolved correctly.

One of the major benefits of moving CSS to JSON is that JSON is a machine-readable format, which means it can be exposed in the WordPress Site Editor UI by fetching an API, thus allowing users to modify default values and customize a site’s appearance without writing any CSS at all. It also provides a way to style blocks consistently, while providing a structure that creates layers of specificity such that the user settings take the highest priority over those defined in theme.json. That interplay between theme-level styles in theme.json and the user-defined styles in the Global Styles UI is what makes the JSON approach so appealing.

Developers maintain consistency in JSON, and users gain flexibility with code-less customizations. That’s a win-win.

There are other benefits, for sure, and Mike McAlister from WP Engine lists several in this Twitter thread. You can find even more benefits in this in-depth discussion over at the Make WordPress Core blog. And once you’ve given that a read, compare the benefits of the JSON approach with the available ways to define and override styles in classic themes.

Defining styles with JSON elements

We’ve already seen a lot of progress as far as what parts of a theme theme.json is capable of styling. Prior to WordPress 6.1, all we could really do was style headings and links. Now, with WordPress 6.1, we can add buttons, captions, citations, and headings to the mix.

And we do that by defining JSON elements. Think of elements as individual components that live in a WordPress block. Say we have a block that contains a heading, a paragraph, and a button. Those individual pieces are elements, and there’s an elements object in theme.json where we define their styles:

{   "version": 2,   "settings": {},   // etc.   "styles": {     // etc.     "elements": {         "button": { ... },         "h1": { ... },         "heading": { ... },     },   },   "templateParts": {} }

A better way to describe JSON elements is as low-level components for themes and blocks that do not need the complexity of blocks. They are representations of HTML primitives that are not defined in a block but can be used across blocks. How they are supported in WordPress (and the Gutenberg plugin) is described in the Block Editor Handbook and this Full Site Editing tutorial by Carolina Nymark.

For example, links are styled in the elements object but are not a block in their own right. But a link can be used in a block and it will inherit the styles defined on the elements.link object in theme.json. This doesn’t fully encapsulate the definition of an element, though, as some elements are also registered as blocks, such as the Heading and Button blocks — but those blocks can still be used within other blocks.

Here is a table of the elements that are currently available to style in theme.json, courtesy of Carolina:

Element Selector Where it’s supported
link <a> WordPress Core
h1 – h6 The HTML tag for each heading level: <h1><h2><h3><h4><h5> and <h6> WordPress Core
heading Styles all headings globally by individual HTML tag: <h1><h2><h3><h4><h5> and <h6> Gutenberg plugin
button .wp-element-button.wp-block-button__link Gutenberg plugin
caption .wp-element-caption,
.wp-block-audio figcaption,
.wp-block-embed figcaption,
.wp-block-gallery figcaption,
.wp-block-image figcaption,
.wp-block-table figcaption,
.wp-block-video figcaption
Gutenberg plugin
cite .wp-block-pullquote cite Gutenberg plugin

As you can see, it’s still early days and plenty still needs to move from the Gutenberg plugin into WordPress Core. But you can see how quick it would be to do something like style all headings in a theme globally without hunting for selectors in CSS files or DevTools.

Further, you can also start to see how the structure of theme.json sort of forms layers of specificity, going from global elements (e.g. headings) to individual elements (e.g. h1), and block-level styles (e.g. h1 contained in a block).

Additional information on JSON elements is available in this Make WordPress proposal and this open ticket in the Gutenberg plugin’s GitHub repo.

JSON and CSS specificity

Let’s keep talking about CSS specificity. I mentioned earlier that the JSON approach to styling establishes a hierarchy. And it’s true. Styles that are defined on JSON elements in theme.json are considered default theme styles. And anything that is set by the user in the Global Styles UI will override the defaults.

In other words: user styles carry more specificity than default theme styles. Let’s take a look at the Button block to get a feel for how this works.

I’m using Emptytheme, a blank WordPress theme with no CSS styling. I’m going to add a Button block on a new page.

The background color, text color, and rounded borders come from the block editor’s default settings.

OK, we know that WordPress Core ships with some light styling. Now, I’m going to switch to the default TT3 theme from WordPress 6.1 and activate it. If I refresh my page with the button, the button changes styles.

The background color, text color, and rounded corner styles have changed.

You can see exactly where those new styles are coming from in TT3’s theme.json file. This tells us that the JSON element styles take precedence over WordPress Core styles.

Now I am going to modify TT3 by overriding it with a theme.json file in a child theme, where the default background color of the Button block is set to red.

The default style for the Button block has been updated to red.

But notice the search button in that last screenshot. It should be red, too, right? That must mean it is styled at another level if the change I made is at the global level. If we want to change both buttons, we could do it at the user level using the Global Styles UI in the site editor.

We changed the background color of both buttons to blue and modified the text as well using the Global styles UI. Notice that the blue from there took precedence over the theme styles!

The Style Engine

That’s a very quick, but good, idea of how CSS specificity is managed in WordPress block themes. But it’s not the complete picture because it’s still unclear where those styles are generated. WordPress has its own default styles that come from somewhere, consumes the data in theme.json for more style rules, and overrides those with anything set in Global Styles.

Are those styles inline? Are they in a separate stylesheet? Maybe they’re injected on the page in a <script>?

That’s what the new Style Engine is hopefully going to solve. The Style Engine is a new API in WordPress 6.1 that is meant to bring consistency to how styles are generated and where styles are applied. In other words, it takes all of the possible sources of styling and is singularly responsible for properly generating block styles. I know, I know. Yet another abstraction on top of other abstractions just to author some styles. But having a centralized API for styles is probably the most elegant solution given that styles can come from a number of places.

We’re only getting a first look at the Style Engine. In fact, here’s what has been completed so far, according to the ticket:

  • Audit and consolidate where the code generates block support CSS in the back end so that they are delivered from the same place (as opposed to multiple places). This covers CSS rules such as margin, padding, typography, colors, and borders.
  • Remove repetitive layout-specific styles and generate semantic class names.
  • Reduce the number of inline style tags we print to the page for block, layout, and element support.

Basically, this is the foundation for establishing a single API that contains all the CSS style rules for a theme, wherever they come from. It cleans up the way WordPress would inject inline styles pre-6.1 and establishes a system for semantic class names.

Further details on the long-term and short-term goals of Style Engine can be found in this Make WordPress Core discussion. You can also follow the tracking issue and project board for more updates.

Working with JSON elements

We talked a bit about JSON elements in the theme.json file and how they are basically HTML primitives for defining default styles for things like headings, buttons, and links, among others. Now, let’s look at actually using a JSON element and how it behaves in various styling contexts.

JSON elements generally have two contexts: the global level and the block level. The global level styles are defined with less specificity than they are at the block level to ensure that block-specific styles take precedence for consistency wherever blocks are used.

Global styles for JSON elements

Let’s look at the new default TT3 theme and examine how its buttons are styled. The following is an abbreviated copy-paste of the TT3 theme.json file (here’s the full code) showing the global styles section, but you can find the original code here.

View code
{   "version": 2,   "settings": {},     // ...   "styles": {     // ...     "elements": {       "button": {         "border": {           "radius": "0"         },         "color": {           "background": "var(--wp--preset--color--primary)",           "text": "var(--wp--preset--color--contrast)"         },         ":hover": {           "color": {             "background": "var(--wp--preset--color--contrast)",             "text": "var(--wp--preset--color--base)"           }         },         ":focus": {           "color": {             "background": "var(--wp--preset--color--contrast)",             "text": "var(--wp--preset--color--base)"           }         },         ":active": {           "color": {             "background": "var(--wp--preset--color--secondary)",             "text": "var(--wp--preset--color--base)"           }         }       },       "h1": {         "typography": { }       },       // ...       "heading": {         "typography": {           "fontWeight": "400",           "lineHeight": "1.4"       }       },       "link": {         "color": {           "text": "var(--wp--preset--color--contrast)"         },         ":hover": {           "typography": {             "textDecoration": "none"           }         },         ":focus": {           "typography": {             "textDecoration": "underline dashed"           }         },         ":active": {           "color": {             "text": "var(--wp--preset--color--secondary)"           },           "typography": {             "textDecoration": "none"           }         },         "typography": {           "textDecoration": "underline"         }       }      },     // ...   },   "templateParts": {} }

All buttons are styled at the global level (styles.elements.button).

Every button in the Twenty Twenty-Three theme shares the same background color, which is set to the --wp--preset--color--primary CSS variable, or #B4FD55.

We can confirm this in DevTools as well. Notice that a class called .wp-element-button is the selector. The same class is used to style the interactive states as well.

Again, this styling is all happening at the global level, coming from theme.json. Whenever we use a button, it is going to have the same background because they share the same selector and no other style rules are overriding it.

As an aside, WordPress 6.1 added support for styling interactive states for certain elements, like buttons and links, using pseudo-classes in theme.json — including :hover, :focus, and :active — or the Global Styles UI. Automattic Engineer Dave Smith demonstrates this feature in a YouTube video.

We could override the button’s background color either in theme.json (preferably in a child theme since we’re using a default WordPress theme) or in the Global Styles settings in the site editor (no child theme needed since it does not require a code change).

But then the buttons will change all at once. What if we want to override the background color when the button is part of a certain block? That’s where block-level styles come into play.

Block-level styles for elements

To understand how we can use and customize styles at the block level, let’s change the background color of the button that is contained in the Search block. Remember, there is a Button block, but what we’re doing is overriding the background color at the block level of the Search block. That way, we’re only applying the new color there as opposed to applying it globally to all buttons.

To do that, we define the styles on the styles.blocks object in theme.json. That’s right, if we define the global styles for all buttons on styles.elements, we can define the block-specific styles for button elements on styles.block, which follows a similar structure:

{   "version": 2,   // ...   "styles": {     // Global-level syles     "elements": { },     // Block-level styles     "blocks": {       "core/search": {         "elements": {           "button": {             "color": {               "background": "var(--wp--preset--color--quaternary)",               "text": "var(--wp--preset--color--base)"             }           }         },         // ...       }     }   } }

See that? I set the background and text properties on styles.blocks.core/search.elements.button with two CSS variables that are preset in WordPress.

The result? The search button is now red (--wp--preset--color--quaternary), and the default Button block retains its bright green background.

We can see the change in DevTools as well.

The same is true if we want to style buttons that are included in other blocks. And buttons are merely one example, so let’s look at another one.

Example: Styling headings at each level

Let’s drive all this information home with an example. This time, we will:

  • Style all headings globally
  • Style all Heading 2 elements
  • Style Heading 2 elements in the Query Loop block

First, let’s start with the basic structure for theme.json:

{   "version": 2,   "styles": {     // Global-level syles     "elements": { },     // Block-level styles     "blocks": { }   } }

This establishes the outline for our global and block-level styles.

Style all headings globally

Let’s add the headings object to our global styles and apply some styles:

{   "version": 2,   "styles": {     // Global-level syles     "elements": {       "heading": {         "color": "var(--wp--preset--color--base)"       },     },     // Block-level styles     "blocks": { }   } }

That sets the color for all headings to the preset base color in WordPress. Let’s change the color and font size of Heading 2 elements at the global level as well:

{   "version": 2,   "styles": {     // Global-level syles     "elements": {       "heading": {         "color": "var(--wp--preset--color--base)"       },       "h2": {         "color": "var(--wp--preset--color--primary)",         "typography": {           "fontSize": "clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem)"         }       }     },     // Block-level styles     "blocks": { }   } }

Now, all Heading 2 elements are set to be the primary preset color with a fluid font size. But maybe we want a fixed fontSize for the Heading 2 element when it is used in the Query Look block:

{   "version": 2,   "styles": {     // Global-level syles     "elements": {       "heading": {         "color": "var(--wp--preset--color--base)"       },       "h2": {         "color": "var(--wp--preset--color--primary)",         "typography": {           "fontSize": "clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem)"         }       }     },     // Block-level styles     "blocks": {       "core/query": {         "elements": {           "h2": {             "typography": {               "fontSize": 3.25rem             }           }         }       }     }   } }

Now we have three levels of styles for Heading 2 elements: all headings, all Heading 2 elements, and Heading 2 elements that are used in the Query Loop block.

Existing theme examples

While we only looked at the styling examples for buttons and headings in this article, WordPress 6.1 supports styling additional elements. There’s a table outlining them in the “Defining styles with JSON elements” section.

You’re probably wondering which JSON elements support which CSS properties, not to mention how you would even declare those. While we wait for official documentation, the best resources we have are going to be the theme.json files for existing themes. I’m going to provide links to themes based on the elements they customize, and point out what properties are customized.

Theme What’s customized Theme JSON
Blockbase Buttons, headings, links, core blocks Source code
Block Canvas Buttons, headings, links, core blocks Source code
Disco Buttons, headings, core blocks Source code
Frost Buttons, headings, links, captions, cite, core blocks Source code
Pixl Buttons, headings, links, core blocks Source code
Rainfall Buttons, headings, links, core blocks Source code
Twenty Twenty-Three Buttons, headings, links, core blocks Source code
Vivre Buttons, headings, links, core blocks Source code

Be sure to give each theme.json file a good look because these themes include excellent examples of block-level styling on the styles.blocks object.

Wrapping up

Frequent changes to the full-site editor are becoming a major sources of irritation to many people, including tech-savvy Gutenberg users. Even very simple CSS rules, which work well with classic themes, don’t seem to work for block themes because of the new layers of specificity we covered earlier.

Regarding a GitHub proposal to re-design the site editor in a new browser mode, Sara Gooding writes in a WP Tavern post:

It’s easy to get lost while trying to get around the Site Editor unless you are working day and night inside the tool. The navigation is jumpy and confusing, especially when going from template browsing to template editing to modifying individual blocks.

Even as a keen early rider in the world of Gutenberg block editor and block-eye themes, I do have tons of my own frustrations. I’m optimistic, though, and anticipate that the site editor, once completed, will be a revolutionary tool for users and techno-savvy theme developers alike. This hopeful tweet already confirms that. In the meantime, it seems that we should be preparing for more changes, and perhaps even a bumpy ride.


I’m listing all of the resources I used while researching information for this article.

JSON elements

Global Styles

Style Engine

Thanks for reading! I’d love to hear your own reflections on using the block themes and how you managing your CSS.

Managing CSS Styles in a WordPress Block Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , ,

CSS Checkerboard Background… But With Rounded Corners and Hover Styles

On one hand, creating simple checkered backgrounds with CSS is easy. On the other hand, though, unless we are one of the CSS-gradient-ninjas, we are kind of stuck with basic patterns.

At least that’s what I thought while staring at the checkered background on my screen and trying to round those corners of the squares just a little…until I remembered my favorite bullet point glyph — — and figured that if only I could place it over every intersection in the pattern, I’ll surely get the design I want.

Turns out it’s possible! Here’s the proof.

Let’s start with the basic pattern:

div {  background:    repeating-linear-gradient(     to right, transparent,      transparent 50px,      white 50px,      white 55px   ),   repeating-linear-gradient(     to bottom, transparent,       transparent 50px,      white 50px,      white 55px   ),   linear-gradient(45deg, pink, skyblue);   /* more styles */ }

What that gives us is a repeating background of squares that go from pink to blue with 5px white gaps between them. Each square is fifty pixels wide and transparent. This is created using repeating-linear-gradient, which creates a linear gradient image where the gradient repeats throughout the containing area.

In other words, the first gradient in that sequence creates white horizontal stripes and the second gradient creates white vertical stripes. Layered together, they form the checkered pattern, and the third gradient fills in the rest of the space.

Now we add the star glyph I mentioned earlier, on top of the background pattern. We can do that by including it on the same background property as the gradients while using an encoded SVG for the shape:

div {   background:      repeat left -17px top -22px/55px 55px     url("data:image/svg+xml,     <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 35px 35px'>       <foreignObject width='35px' height='35px'>         <div xmlns='http://www.w3.org/1999/xhtml' style='color: white; font-size: 35px'>✦</div>       </foreignObject>     </svg>"     ),      repeating-linear-gradient(       to right, transparent,       transparent 50px,       white 50px,       white 55px     ),     repeating-linear-gradient(       to bottom, transparent,       transparent 50px,       white 50px,       white 55px     ),     linear-gradient(45deg, pink, skyblue);   /* more style */ }

Let’s break that down. The first keyword, repeat, denotes that this is a repeating background image. Followed by that is the position and size of each repeating unit, respectively (left -17px top -22px/55px 55px). This offset position is based on the glyph and pattern’s size. You’ll see below how the glyph size is given. The offset is added to re-position the repeating glyph exactly over each intersection in the checkered pattern.

The SVG has an HTML <div> carrying the glyph. Notice that I declared a font-size on it. That ultimately determines the border radius of the squares in the checkerboard pattern — the bigger the glyph, the more rounded the squares. The unrolled SVG from the data URL looks like this:

<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 35px 35px'>   <foreignObject width='35px' height='35px'>     <div xmlns='http://www.w3.org/1999/xhtml' style='color:white;font-size:35px'>✦</div>   </foreignObject> </svg>

Now that a CSS pattern is established, let’s add a :hover effect where the glyph is removed and the white lines are made slightly translucent by using rgb() color values with alpha transparency.

div:hover {   background:     repeating-linear-gradient(       to right, transparent,       transparent 50px,       rgb(255 255 255 / 0.5) 50px,       rgb(255 255 255 / 0.5) 55px     ),     repeating-linear-gradient(       to bottom, transparent,       transparent 50px,       rgb(255 255 255 / 0.5) 50px,       rgb(255 255 255 / 0.5) 55px     ),   linear-gradient(45deg, pink, skyblue);   box-shadow: 10px 10px 20px pink; }

There we go! Now, not only do we have our rounded corners, but we also have more control control over the pattern for effects like this:

Again, this whole exercise was an attempt to get a grid of squares in a checkerboard pattern that supports rounded corners, a background gradient that serves as an overlay across the pattern, and interactive styles. I think this accomplishes the task quite well, but I’m also interested in how you might’ve approached it. Let me know in the comments!

CSS Checkerboard Background… But With Rounded Corners and Hover Styles originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , , ,

Using CSS Cascade Layers to Manage Custom Styles in a Tailwind Project

If a utility class only does one thing, chances are you don’t want it to be overridden by any styles coming from elsewhere. One approach is to use !important to be 100% certain the style will be applied, regardless of specificity conflicts.

The Tailwind config file has an !important option that will automatically add !important to every utility class. There’s nothing wrong with using !important this way, but nowadays there are better ways to handle specificity. Using CSS Cascade Layers we can avoid the heavy-handed approach of using !important.

Cascade layers allow us to group styles into “layers”. The precedence of a layer always beats the specificity of a selector. Specificity only matters inside each layer. Establishing a sensible layer order helps avoid styling conflicts and specificity wars. That’s what makes CSS Cascade Layers a great tool for managing custom styles alongside styles from third-party frameworks, like Tailwind.

A Tailwind source .css file usually starts something like this:

@tailwind base; @tailwind components; @tailwind utilities; @tailwind variants;

Let’s take a look at the official Tailwind docs about directives:

Directives are custom Tailwind-specific at-rules you can use in your CSS that offer special functionality for Tailwind CSS projects. Use the @tailwind directive to insert Tailwind’s base, components, utilities and variants styles into your CSS.

In the output CSS file that gets built, Tailwind’s CSS reset — known as Preflight — is included first as part of the base styles. The rest of base consists of CSS variables needed for Tailwind to work. components is a place for you to add your own custom classes. Any utility classes you’ve used in your markup will appear next. Variants are styles for things like hover and focus states and responsive styles, which will appear last in the generated CSS file.

The Tailwind @layer directive

Confusingly, Tailwind has its own @layer syntax. This article is about the CSS standard, but let’s take a quick look at the Tailwind version (which gets compiled away and doesn’t end up in the output CSS). The Tailwind @layer directive is a way to inject your own extra styles into a specified part of the output CSS file.

For example, to append your own styles to the base styles, you would do the following:

@layer base {   h1 {     font-size: 30px;   } }

The components layer is empty by default — it’s just a place to put your own classes. If you were doing things the Tailwind way, you’d probably use @apply (although the creator of Tailwind recently advised against it), but you can also write classes the regular way:

@layer components {   .btn-blue {     background-color: blue;     color: white;   } }

The CSS standard is much more powerful. Let’s get back to that…

Using the CSS standard @layer

Here’s how we can rewrite this to use the CSS standard @layer:

@layer tailwind-base, my-custom-styles, tailwind-utilities;  @layer tailwind-base {   @tailwind base; }  @layer tailwind-utilities {   @tailwind utilities;   @tailwind variants; } 

Unlike the Tailwind directive, these don’t get compiled away. They’re understood by the browser. In fact, DevTools in Edge, Chrome, Safari, and Firefox will even show you any layers you’ve defined.

CSS Cascade Layers with Tailwind CSS layers in DevTools.

You can have as many layers as you want — and name them whatever you want — but in this example, all my custom styles are in a single layer (my-custom-styles). The first line establishes the layer order:

@layer tailwind-base, my-custom-styles, tailwind-utilities;

This needs to be provided upfront. Be sure to include this line before any other code that uses @layer. The first layer in the list will be the least powerful, and the last layer in the list will be the most powerful. That means tailwind-base is the least powerful layer and any code in it will be overridden by all the subsequent layers. That also means tailwind-utilities will always trump any other styles — regardless of source order or specificity. (Utilities and variants could go in separate layers, but the maintainers of Tailwind will ensure variants always trump utilities, so long as you include the variants below the utilities directive.)

Anything that isn’t in a layer will override anything that is in a layer (with the one exception being styles that use !important). So, you could also opt to leave utilities and variants outside of any layer:

@layer tailwind-base, tailwind-components, my-custom-styles;  @layer tailwind-base {   @tailwind base; }  @layer tailwind-components {   @tailwind components; }  @tailwind utilities; @tailwind variants;

What did this actually buy us? There are plenty of times when advanced CSS selectors come in pretty handy. Let’s create a version of :focus-within that only responds to keyboard focus rather than mouse clicks using the :has selector (which lands in Chrome 105). This will style a parent element when any of its children receive focus. Tailwind 3.1 introduced custom variants — e.g. <div class="[&:has(:focus-visible)]:outline-red-600"> — but sometimes it’s easier to just write CSS:

@layer tailwind-base, my-custom-styles; @layer tailwind-base {   @tailwind base; }  @tailwind utilities;  @layer my-custom-styles {   .radio-container {     padding: 4px 24px;     border: solid 2px rgb(230, 230, 230);   }   .radio-container:has(:focus-visible) {     outline: solid 2px blue;   } }

Let’s say in just one instance we want to override the outline-color from blue to something else. Let’s say the element we’re working with has both the Tailwind class .outline-red-600 and our own .radio-container:has(:focus-visible) class:

<div class="outline-red-600 radio-container"> ... </div>

Which outline-color will win?

Ordinarily, the higher specificity of .radio-container:has(:focus-visible) would mean the Tailwind class has no effect — even if it’s lower in the source order. But, unlike the Tailwind @layer directive that relies on source order, the CSS standard @layer overrules specificity.

As a result, we can use complex selectors in our own custom styles but still override them with Tailwind’s utility classes when we need to — without having to resort to heavy-handed !important usage to get what we want.

Using CSS Cascade Layers to Manage Custom Styles in a Tailwind Project originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.


, , , , , , ,

Should CSS Override Default Browser Styles?

CSS overrides can change the default look of almost anything:

  • You can use CSS to override what a checkbox or radio button looks like, but if you don’t, the checkbox will look like a default checkbox on your operating system and some would say that’s best for accessibility and usability.
  • You can use CSS to override what a select menu looks like, but if you don’t, the select will look like a default select menu on your operating system and some would say that’s best for accessibility and usability.
  • You can override what anchor links look like, but some would say they should be blue with underlines because that is the default and it’s best for accessibility and usability.
  • You can override what scrollbars look like, but if you don’t, the scrollbars will look (and behave) the way default scrollbars do on your operating system, and some would say that’s best for accessibility and usability.

It just goes on and on…

In my experience, everyone has a different line. Nearly everybody styles their buttons. Nearly everybody styles their links, but some might only customize the hue of blue and leave the underline, drawing the line at more elaborate changes. It’s fairly popular to style form elements like checkboxes, radio buttons, and selects, but some people draw the line before that.

Some people draw a line saying you should never change a default cursor, some push that line back to make the cursor into a pointer for created interactive elements, some push that line so far they are OK with custom images as cursors. Some people draw the line with scrollbars saying they should never be customized, while some people implement elaborate designs.

CSS is a language for changing the design of websites. Every ruleset you write likely changes the defaults of something. The lines are relatively fuzzy, but I’d say there is nothing in CSS that should be outright banned from use — it’s more about the styling choices you make. So when you do choose to style something, it remains usable and accessible. Heck, background-color can be terribly abused making for inaccessible and unusable areas of a site, but nobody raises pitchforks over that.

Should CSS Override Default Browser Styles? originally published on CSS-Tricks


, , , ,

Standardizing Focus Styles With CSS Custom Properties

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

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

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

Meeting WCAG Focus Style Criteria

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

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

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

Managing focus style with CSS custom properties

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

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

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

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

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

Improvements for outline appearance

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

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

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

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

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

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

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

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

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

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

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

Focus styles demo

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

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


, , , ,

Inline Styles as Classes (lol)

If you’re abhorred by using inline styles, just move that style to the class attribute! And then make sure you have CSS in place that, ya know, does what it says on the box.

OK lemme dig in and totally ruin the joke.

  • First off, it’s a joke, so don’t actually do this. I don’t even mind the occasional inline style for one-off stuff, but this is not that.
  • To me the weirdest part is that period (.) character. Escaping the more unusual characters with a backslash () feels normal, but what is that period about?
  • The little period trick there doesn’t work when the following character is a number (e.g. .padding:.1rem;).
  • You can avoid the escaping and trickery if you go with an attribute selector like [class*="display: flex;"].
  • This reminds me of Mathias Bynens’ research: CSS character escape sequences. But… that doesn’t seem to work anymore? I wonder if browsers changed or if the tool broke and doesn’t output what it should anymore (e.g. does .color3a #bada55; look right?).

Here’s all that playing around:

The post Inline Styles as Classes (lol) appeared first on CSS-Tricks.

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


, ,

List Markers and String Styles

Lists—we’ve all worked with them in one form or another. I’m talking about HTML’s <ol> and <ul>. Much of the time, because we desire styling control, we turn off the list’s markers completely with list-style-type: none, and start styling from there. Other times, we choose from a very limited set of unordered list markers, such as disc, circle, or square; or a (much) wider range of ordered list markers. We might even, from time to time, supply the URL of an image to be used.

But what if we want to style the markers differently than the contents of the list items? That’s always been difficult at best. Now, thanks to the ::marker pseudo-element, it’s a whole lot easier. You don’t get the full range of CSS to apply to the markers, but there’s still a great deal that can be done.

::marker is available in Firefox and, thanks to work by Igalia, Chrome as well.

Consider this list:

By default, that will yield an ordered list numbered from 1 to 5, using Arabic numerals (1, 2, 3, etc.), each followed by a dot (period), all of which will match the text contents in font face, size, style, color, and so on.

If you had a design direction that required making the numbers smaller or a different color, you’d have to manually create that effect by suppressing the markers and using the ::before pseudo-element and CSS counters and negative text indenting and… well, it would take a scientist to explain it all.

Enter ::marker. Add these styles to the above list, and you’ll get the result shown after.

That’s all you need!

Before you go tearing off to rewrite all your CSS, though, beware: the properties you can apply via ::marker are fairly limited at the moment. As of February 2021, the properties that markers should recognize are:

  • All font properties (font-face, font-size, etc.)
  • The white-space property
  • The color property
  • The internationalization properties text-combine-upright, unicode-bidi, and direction
  • The content property
  • All animation and transition properties

There are some additions in some browsers, but almost all of the additions relate to text styling, not the box model. So if you were thinking you could put all your list numbers into circles with shaded backgrounds, ::marker won’t get you there—you’ll have to return to the hackfest of ::before generated content. For now, anyway: the specification explicitly says more properties may be permitted for ::marker in the future.

There’s also a limitation around white-space, which has rendering bugs in varying browsers. Chrome, for example, treats all whitespace in markers as white-space: pre as the specification says, but won’t let you change it. This should be fixed when Chrome’s LayoutNG (Next Generation) ships, but not until then. Firefox, on the other hand, ignores any white-space values, and treats whitespace like normal-flow text by default.

With those limits in mind, you can still jazz up your markers with the content property. Instead of numbers followed by a period, you can put each number in brackets with a combination of counters and strings.

Note the space after the closing bracket in the content value. That’s included to provide a little bit of space between the marker and the list content. Ordinarily you might think to use a marking or padding, but as we saw earlier, those properties can’t be applied with ::marker. Which is frustrating! Also note the CSS counter list-item. That wasn’t defined anywhere else in the CSS—it’s a built-in counter that all browsers (that understand CSS counters) use to count list items, like those in ordered lists. You can use it in your CSS as well!

If all you want to do is change the text content of a list marker and don’t care about changing any of its styles, you can do that with ::marker, or you can do it with the new cross-browser support for string values on the list-style-type property.

li.warning {   list-style-type:"⚠"; }

So that’s what’s new in the world of list markers. It might not be something you need to do often, but if you ever do, it’s good to know that the capabilities in this area have increased, and stand to be even better in the future. Let us know if you come up with some clever markers!

The post List Markers and String Styles appeared first on CSS-Tricks.

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


, , ,

Custom Styles in GitHub Readme Files

Even though GitHub Readme files (typically ./readme.md) are Markdown, and although Markdown supports HTML, you can’t put <style> or <script> tags init. (Well, you can, they just get stripped.) So you can’t apply custom styles there. Or can you?

  1. You can use SVG as an <img src="./file.svg" alt="" /> (anywhere).
  2. When used that way, even stuff like animations within them play (wow).
  3. SVG has stuff like <text> for textual content, but also <foreignObject> for regular ol’ HTML content.
  4. SVG support <style> tags.
  5. Your readme.md file does support <img> with SVG sources.

Sindre Sorhus combined all that into an example.

That same SVG source will work here:

The post Custom Styles in GitHub Readme Files appeared first on CSS-Tricks.

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


, , , ,

Copy the Browser’s Native Focus Styles

Remy documented this the other day. Firefox supports a Highlight keyword and both Chrome and Safari support a -webkit-focus-ring-color keyword. So if you, for example, have removed focus from something and want to put it back in the same style as the browser default, or want to apply a focus style to an element when it isn’t directly in focus itself, this can be useful.

For example:

button:focus + span {   outline: 5px auto Highlight;   outline: 5px auto -webkit-focus-ring-color; }

Looks good to me. It’s especially helpful with the sorta weird new Chrome double-outline style that would be slightly tricky to replicate otherwise.

Chrome 84
Safari 13.1
Firefox 80

The post Copy the Browser’s Native Focus Styles appeared first on CSS-Tricks.

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


, , , ,

Chrome 83 Form Element Styles

There have been some aesthetic changes to what form elements look like as of Chrome 83. Anything with gradient colorization is gone (notably the extra-shiny <meter> stuff). The consistency across the board is nice, particularly between inputs and textareas. Not a big fan of the new <select> styling, but I hear a lot of accessibility research went into this, so it’s hard to complain there — plus you can always change it.

Hakim has a nice comparison tweet:

The Jetpack plugin for WordPress has a new comparison block and I’m going to try it out here. You can swipe between the items, just for fun (drag the slider in the middle):

This is not accompanied by new standardized ways to change the look of form elements with CSS, although browsers are well aware of that and seem to draw nearer and nearer all the time. I believe is was a step along that path.

I also see there is a new <input type="time"> as well. The old version looked like this and offered no UI controls:

Now we get this beast with controls:

There are no visual indicators or buttons, but you can scroll those columns.

Reddit notes that it uses the same pseudo element that date pickers use, so if you want it gone, you can scope it to these types of inputs (or not) and remove it.

input[type="time"]::-webkit-calendar-picker-indicator {   display: none; }

I’d call it an improvement (I like UI controls for things), but it does continue to highlight the need to be able to style these things, particularly if the goal is to have people actually use them and not (poorly) rebuild them.

The post Chrome 83 Form Element Styles appeared first on CSS-Tricks.


, , ,