Tag: Deep

React Hooks: The Deep Cuts

Hooks are reusable functions. They allow you to use state and other features (e.g. lifecycle methods and so on) without writing a class. Hook functions let us “hook into” the React state lifecycle using functional components, allowing us to manipulate the state of our functional components without needing to convert them to class components.

React introduced hooks back in version 16.8 and has been adding more ever since. Some are more used and popular than others, like useEffect, useState, and useContext hooks. I have no doubt that you’ve reached for those if you work with React.

But what I’m interested in are the lesser-known React hooks. While all React hooks are interesting in their own way, there are five of them that I really want to show you because they may not pop up in your everyday work — or maybe they do and knowing them gives you some extra superpowers.

useReducer

The useReducer hook is a state management tool like other hooks. Specifically, it is an alternative to the useState hook.

If you use the useReducer hook to change two or more states (or actions), you won’t have to manipulate those states individually. The hook keeps track of all the states and collectively manages them. In other words: it manages and re-renders state changes. Unlike the useState hook, useReducer is easier when it comes to handling many states in complex projects.

Use cases

useReducer can help reduce the complexity of working with multiple states. Use it when you find yourself needing to track multiple states collectively, as it allows you to treat state management and the rendering logic of a component as separate concerns.

Syntax

useReducer accepts three arguments, one of which is optional:

  • a reducer function
  • initialState
  • an init function (optional)
const [state, dispatch] = useReducer(reducer, initialState) const [state, dispatch] = useReducer(reducer, initialState initFunction) // in the case where you initialize with the optional 3rd argument

Example

The following example is an interface that contains a text input, counter, and button. Interacting with each element updates the state. Notice how useReducer allows us to define multiple cases at once rather than setting them up individually.

import { useReducer } from 'react'; const reducer = (state, action) => {   switch (action.type) {     case 'INCREMENT':       return { ...state, count: state.count + 1 };     case 'DECREMENT':       return { ...state, count: state.count - 1 };     case 'USER_INPUT':       return { ...state, userInput: action.payload };     case 'TOGGLE_COLOR':       return { ...state, color: !state.color };     default:       throw new Error();   } }  function App() {   const [state, dispatch] = useReducer(reducer, { count: 0, userInput: '', color: false })    return (     <main className="App, App-header" style={{ color: state.color ? '#000' : '#FF07FF'}}>       <input style={{margin: '2rem'}}         type="text"         value={state.userInput}         onChange={(e) => dispatch({ type: 'USER_INPUT', payload: e.target.value })}       />       <br /><br />       <p style={{margin: '2rem'}} >{state.count}</p>       <section style={{margin: '2rem'}}>         <button  onClick={(() => dispatch({ type: 'DECREMENT' }))}>-</button>         <button onClick={(() => dispatch({ type: 'INCREMENT' }))}>+</button>         <button onClick={(() => dispatch({ type: 'TOGGLE_COLOR' }))}>Color</button>       </section>       <br /><br />       <p style={{margin: '2rem'}}>{state.userInput}</p>     </main>   ); } export default App;

From the code above, noticed how we are able to easily managed several states in the reducer (switch-case), this shows the benefit of the useReducer. This is the power it gives when working in complex applications with multiple states.

useRef

The useRef hook is used to create refs on elements in order to access the DOM. But more than that, it returns an object with a .current property that can be used throughout a component’s entire lifecycle, allowing data to persist without causing a re-render. So, the useRef value stays the same between renders; updating the reference does not trigger a re-render.

Use cases

Reach for the useRef hook when you want to:

  • Manipulate the DOM with stored mutable information.
  • Access information from child components (nested elements).
  • Set focus on an element.

It’s most useful when storing mutatable data in your app without causing a re-render.

Syntax

useRef only accepts one argument, which is the initial value.

const newRefComponent = useRef(initialValue);

Example

Here I used the useRef and useState hook to show the amount of times an application renders an updated state when typing in a text input.

import './App.css'  function App() {   const [anyInput, setAnyInput] = useState(" ");   const showRender = useRef(0);   const randomInput = useRef();   const toggleChange = (e) => {     setAnyInput (e.target.value);     showRender.current++;      }   const focusRandomInput = () => {     randomInput.current.focus();   }    return (     <div className="App">       <input className="TextBox"          ref ={randomInput} type="text" value={anyInput} onChange={toggleChange}       />       <h3>Amount Of Renders: {showRender.current}</h3>       <button onClick={focusRandomInput}>Click To Focus On Input </button>     </div>   ); }  export default App;

Notice how typing each character in the text field updates the app’s state, but never triggers a complete re-render.

useImperativeHandle

You know how a child component has access to call functions passed down to them from the parent component? Parents pass those down via props, but that transfer is “unidirectional” in the sense that the parent is unable to call a function that’s in the child.

Well, useImperativeHandle makes it possible for a parent to access a child component’s functions.

How does that work?

  • A function is defined in the child component.
  • A ref is added in the parent.
  • We use forwardRef, allowing the ref that was defined to be passed to the child.
  • useImperativeHandle exposes the child’s functions via the ref.

Use cases

useImperativeHandle works well when you want a parent component to be affected by changes in the child. So, things like a changed focus, incrementing and decrementing, and blurred elements may be situations where you find yourself reaching for this hook so the parent can be updated accordingly.

Syntax

useImperativeHandle (ref, createHandle, [dependencies])

Example

In this example, we have two buttons, one that’s in a parent component and one that’s in a child. Clicking on the parent button retrieves data from the child, allowing us to manipulate the parent component. It’s set up so that clicking the child button does not pass anything from the parent component to the child to help illustrate how we are passing things in the opposite direction.

// Parent component import React, { useRef } from "react"; import ChildComponent from "./childComponent"; import './App.css';  function ImperativeHandle() {   const controlRef = useRef(null);   return (     onClick={       () => {         controlRef.current.controlPrint();       }     }     >     Parent Box   ); } export default ImperativeHandle;
// Child component import React, { forwardRef, useImperativeHandle, useState } from "react";  const ChildComponent = forwardRef((props, ref) => {   const [print, setPrint] = useState(false);   useImperativeHandle(ref, () => ({     controlPrint()      { setPrint(!print); },   })   );    return (     <>     Child Box     { print && I am from the child component }   ); });  export default ChildComponent;

Output

useMemo

useMemo is one of the least-used but most interesting React hooks. It can improve performance and decrease latency, particularly on large computations in your app. How so? Every time a component’s state updates and components re-render, the useMemo hook prevents React from having to recalculate values.

You see, functions respond to state changes. The useMemo hook takes a function and returns the return value of that function. It caches that value to prevent spending additional effort re-rendering it, then returns it when one of the dependencies has changed.

This process is called memoization and it’s what helps to boost performance by remembering the value from a previous request so it can be used again without repeating all that math.

Use cases

The best use cases are going to be any time you’re working with heavy calculations where you want to store the value and use it on subsequent state changes. It can be a nice performance win, but using it too much can have the exact opposite effect by hogging your app’s memory.

Syntax

useMemo( () =>    { // Code goes here },   [] )

Example

When clicking the button, this mini-program indicates when a number is even or odd, then squares the value. I added lots of zeros to the loop to increase its computation power. It returns the value in spilt seconds and still works well due to the useMemo hook.

// UseMemo.js import React, { useState, useMemo } from 'react'  function Memo() {   const [memoOne, setMemoOne] = useState(0);   const incrementMemoOne = () => { setMemoOne(memoOne + 1) }   const isEven = useMemo(() => {      let i = 0 while (i < 2000000000) i++ return memoOne % 2 === 0   },   [memoOne]);      const square = useMemo(()=> {      console.log("squared the number"); for(var i=0; i < 200000000; i++);     return memoOne * memoOne;   },   [memoOne]);    return (     Memo One -      { memoOne }     { isEven ? 'Even' : 'Odd' } { square }    ); } export default Memo

Output

useMemo is a little like the useCallback hook, but the difference is that useMemo can store a memorized value from a function, where useCallback stores the memorized function itself.

useCallback

The useCallback hook is another interesting one and the last section was sort of a spoiler alert for what it does.

As we just saw, useCallback works like the useMemo hook in that they both use memoization to cache something for later use. While useMemo stores a function’s calculation as a cached value, useCallback stores and returns a function.

Use cases

Like useMemo, useCallback is a nice performance optimization in that it stores and returns a memoized callback and any of its dependencies without a re-render.

Syntax

const getMemoizedCallback = useCallback (   () => { doSomething () }, [] );

Example

 { useCallback, useState } from "react"; import CallbackChild from "./UseCallback-Child"; import "./App.css"  export default function App() {   const [toggle, setToggle] = useState(false);   const [data, setData] = useState("I am a data that would not change at every render, thanks to the useCallback");   const returnFunction = useCallback(     (name) =>      { return data + name; }, [data]   );   return (     onClick={() => {       setToggle(!toggle);     }}     >     {" "}      // Click To Toggle     { toggle && h1. Toggling me no longer affects any function }    );  }
// The Child component import React, { useEffect } from "react";  function CallbackChild(   { returnFunction } ) {   useEffect(() =>      { console.log("FUNCTION WAS CALLED"); },     [returnFunction]);   return { returnFunction(" Hook!") }; } export default CallbackChild;

Output

Final thoughts

There we go! We just looked at five super handy React hooks that I think often go overlooked. As with many roundups like this, we’re merely scratching the surface of these hooks. They each have their own nuances and considerations to take into account when you use them. But hopefully you have a nice high-level idea of what they are and when they might be a better fit than another hook you might reach for more often.

The best way to fully understand them is by practice. So I encourage you to practice using these hooks in your application for better understanding. For that, you can get way more in depth by checking out the following resources:


React Hooks: The Deep Cuts originally published on CSS-Tricks. You should get the newsletter.

CSS-Tricks

, , ,

A Deep Introduction to WordPress Block Themes

The relatively new WordPress editor, also known as the WordPress Block Editor, always under development via the Gutenberg plugin, has been with us since 2018. You can use the block editor on any WordPress theme, provided the theme loads CSS that the blocks use. But there are new themes that lean into the Block Editor much more deeply.

WordPress Block Themes allow you to build out the entire site using blocks, meaning the theme’s responsibly is mostly design guidelines, and less about controlling the pages and the content on them. This is referred to as Full-Site Editing in WordPress and the themes that are built for this are called Block Themes, because you build out everything with blocks.

Let’s dig into all this.

Illustration of a black vinyl record coming out of a record sleep sleeve from the left that contains a blue tinted image of jazz singer Joséphine Baker's profile looking right with a soft smile and parted lips. The image includes white text that says WordPress 5.9 and code is poetry.
Credit: WordPress.org

Table of Contents

Introduction

Except for those who follow its day-to-day development iterations at GitHub, most development surrounding the block editor is largely visible to users — and that’s not necessarily a bad thing. I have been personally trying to keep myself updated with the block editor through WP Tavern and Gutenberg posts, and have been using both the legacy — or “Classic” editor — as well as the block editor in my personal learning project sites.

After taking a detour to learn and experience headless WordPress sites with Gatsby and Frontity frameworks, I am now back to my native WordPress home.

Though I have been aware of the WordPress theme-experiment GitHub repository for a while — themes made completely out of blocks! — I have only started digging into block themes recently. In fact, I have been using an experimental block-based theme here in this project site.

WordPress 5.9 is now out in the wild and with it comes block-based theming for the masses. This release, dubbed Joséphine, is the formal introduction to WordPress full site editing and Block Themes.

Even though the block-based theming functionality has been available in various iterative forms in previous releases, this is a big deal for the WordPress platform and the ecosystem that relies on it. I’ve had my hands on it and thought I’d share what I’ve learned about block themes in my hands-on experience, as well as some personal thoughts on how it works.

Disclaimer: I am not a block themes expert by any stretch. I am well-versed in WordPress and a major fan of the content management system. My goal here is not to critique WordPress 5.9 or steer you in the direction of whether or not you should like it or want to use it. I’m merely coming from the perspective of an open-minded learner who is building personal sites with fairly deep understanding and familiarity with the WordPress Block Editor.

Before we dive straight into block themes, I think it’s a good idea to form a baseline understanding of just what we’re talking about when we’re tossing around terms, like blocks and site editing, as they’re so incredibly new and game-changing relative to what we’ve known and loved about WordPress for decades.

Block Editor

This is really what we mean any time we refer to the “WordPress Editor.” We call the WordPress Editor the Block Editor because it allows us to create pages and posts where each element— including text, images, videos, headers, footers, etc. — is inserted into the post using blocks that can be arranged modularly to complete page layouts. It evolved from what’s now called the “classic” editor, which was more squarely based on entering content to be published on a page or post in a predefined layout.

A full screenshot of the WordPress Block editor split into three numbers parts that are highlighted in red.
WordPress Block Editor including the block inserter (1), block editor content area (2), and the document and block settings (3)
Credit: WordPress Block Editor Handbook.

It’s sort of like content and layout coming together, where both are managed in the WordPress Editor. So, where we used to rely on the editor for content and (more or less) theme templates to define layout, both are directly editable in the WordPress Editor interface.

You can find more detail here on using the Block Editor.

Block Theme

As explained in the WordPress docs:

A block theme is a WordPress theme with templates entirely composed of blocks so that in addition to the post content of the different post types (pages, posts, …), the block editor can also be used to edit all areas of the site: headers, footers, sidebars, etc.

This WordPress documentation provides an overview of block themes in its knowledgebase, including how to create block themes and styling in this primer.

The bottom line: Block themes are different than “classic” WordPress themes. Rather than relying strictly on PHP files that conform to the WordPress Template Hierarchy, a WordPress Block Theme consists of block-based HTML templates — assembled groups of blocks that can be styled and arranged in the WordPress Site Editor (that’s coming up next) as well as using a theme.json file for global styling tokens.

Site Editor

This is the crown jewel of WordPress 5.9. While it is officially called the WordPress Site Editor, it’s largely been referred to as Full-Site Editing** (FSE) during development and is described as “the cohesive experience that allows you to directly edit and navigate between various templates, template parts, styling options, and more.” Phew, that’s a lot!

Credit: WordPress Support

The WordPress Site Editor allows us to create and editing templates that are made of blocks. So the idea is that we can assemble a group of blocks that can be applied globally to a site. Like a header component, for example. That might consist of blocks for a site logo, a primary menu, and a tagline. The site editor allows us to create a new block theme or modify an existing theme to give the site’s global appearance a completely new look without writing a line of code.

So, you know how you’ve had to build a theme in the past with a bunch of PHP templates? That’s no longer the case. Theme “development” now has a UI that’s available directly in WordPress.

More detail on using site editor is in the WordPress documentation.

The official WordPress Glossary has additional terms and definitions you may want to explore as we dig deeper into WordPress Block Themes and FSE.

Using the block editor with classic themes

The WordPress Block Editor can be used in both the classic and block themes. When the Gutenberg editor project began, the classic TinyMCE-based editor was detached from WordPress Core into the Classic Editor plugin. As long as the Classic Editor plugin is installed and active, content writing is pretty normal as it was before blocks were introduced.

Prior to the formal introduction of block editor features, we had to install the experimental Gutenberg plugin. By simply switching plugins, individual page or post contents could be created with either editor. The WordPress 5.0 release introduced the block editor alongside the default Twenty Nineteen theme, demonstrating how to add block editor features and explore its power.

In other words, the evolution toward FSE has been building for a while. And because of that, we get to enjoy a high level of backwards compatibility that allows us all to adopt block-based features when we’re good and ready.

The anatomy of block-based themes

Experimental block-based themes have been in development since early 2020. At the time I’m writing this, the GitHub theme experiment repository lists 12 block themes that explore some aspect of creating themes using blocks or block templates.

But it was probably the Twenty Twenty-One theme that was the first “default” theme to make blocks a first-class citizen, introducing block styles and block patterns, though the recently updated versions of Twenty Nineteen, and Twenty Twenty also include bundled block styling and block patterns. Currently, there are more than 130 themes from the community with bundled block editor patterns, block styles feature, including my favorite, Anders Noren’s Eksell theme.

With the ongoing development of the WordPress Block Editor’s FSE features, even more block-based themes are also being introduced.

So, what does the development of block-based themes mean for those of us who are deeply entrenched in the “classic” way of building WordPress themes? That’s what I want to look at in this section.

The file structure of block themes

In classic PHP-powered theming, markup elements are generated with PHP and JavaScript, while in block themes those templates are entirely composed of HTML blocks and structural CSS provided by the block editor. This might sound scary for lots of folks, but it’s easy to imagine just how liberating it is for others as it lowers the bar when it comes to developing a WordPress theme.

The structure of a block theme is drastically different from the classic WordPress Template Hierarchy that we all are used to. In classic PHP-based themes, page element markup has to be generated with PHP and JavaScript, whereas in block themes, the WordPress Core provides both the markup and basic styling. For example, the default Twenty Twenty-One theme contains 48 PHP files and 11 JavaScript files weighing in at 4.5 MB. Its block-based sibling, the experimental TT1 Blocks theme, contains only four PHP files, one JavaScript file, and eight HTML files at 3.5 MB.

Screenshot of a Mac window open to the default Twenty Twenty-One WordPress theme, displaying a long list of files.
Twenty Twenty-One theme folder

Screenshot of a Mac window open to the TT1 theme folder, showing that WordPress Block Themes contain fewer files.
TT1 theme folder

A block theme structure can be very simple with just a few required files : index.php, style.css and template/index.html. The following is a typical block theme file structure, pulled from the WordPress Editor Handbook:

#! basic block-theme structure theme |__ style.css |__ functions.php |__ index.php |__ theme.json |__ templates     |__ index.html     |__ single.html     |__ archive.html     |__ ... |__ parts     |__ header.html     |__ footer.html     |__ sidebar.html     |__ ...
  • styles.css: Contains theme’s style sheet
  • functions.php: Contains theme setup and may include additional files, enable an editor style sheet, and enqueue style.css, if there are any
  • index.php: An empty file to switch to default file in case the block theme is activated without the WordPress Block Editor.
  • theme.json: Optional configuration file used to enable or disable features and set default styles for both the website and blocks
  • templates: Contains page templates that are composed of blocks. These files follow the same template hierarchy as traditional themes.
    • index.html: The primary template to generate a post or page, analogous to index.php in classic themes
    • single.html: The template to generate single posts or pages
    • archive.html: The template to generate archive lists of posts
  • parts: The common collections of blocks to be used in block templates
    • header.html: The global header block
    • footer.html: The global footer block
    • sidebar.html: An optional global sidebar block

A list of theme blocks including that are specific to block themes is available in WordPress Block Editor Handbook.

Templates and template parts

Templates are basically group of assembled blocks that might include reusable block parts, like a site header or footer. Different blocks are used to compose a page template. For example, that might be a list of blog posts, a list of products, or even a widget.

Here’s an example of a block template pulled from the WordPress Block Editor Handbook.

 <!-- wp:site-title /-->  <!-- wp:image {"sizeSlug":"large"} --> <figure class="wp-block-image size-large">     <img src="https://cldup.com/0BNcqkoMdq.jpg" alt="" /> </figure> <!-- /wp:image -->  <!-- wp:group --> <div class="wp-block-group">     <!-- wp:post-title /-->     <!-- wp:post-content /--> </div> <!-- /wp:group -->  <!-- wp:group --> <div class="wp-block-group">     <!-- wp:heading -->     <h2>Footer</h2>     <!-- /wp:heading --> </div> <!-- /wp:group -->

Creating WordPress Block Themes

The WordPress Site Editor is now the primary tool for defining the look and feel of a WordPress website. You may be used to using the WordPress Customizer to do these things — and some themes have heavily tapped into that to do what the site editor is now designed to do.

So, no longer is the block editor for pages and posts; it’s the way WordPress themes are created.

I’m assuming that many of you have already used the block editor, and don’t really need a deep lesson on what it is or how to use it. That said, it’s worth poking at it a bit since it’s the impetus for everything related to WordPress theming moving forward, now that WordPress 5.9 is in the wild.

In fact, when we talk about block editing and theming, yes, we’re talking about the block editor. But really what we’re talking about is the WordPress Site Editor.

The WordPress Site Editor interface

Even as an early adopter of the Gutenberg plugin, I find the experience of the site editor intimidating and frustrating. It changes frequently and often drastically with each new release. I am hopeful, though, that WordPress 5.9 is a sort of line in the sand that helps stabilize that rocky feeling.

The site editor is accessed the same way you’re already used to accessing the WordPress Customizer. It’s located under Appearance in the dashboard menu, called Editor.

Screenshots of the WordPress admin Themes screen side-by-side, the first showing the classic WordPress menu items like Customize, Widgets, and Menus, while the second shows how a WordPress Block Themes only displays a single Editor menu item.
The site editor option is available only when a block theme is activated. If you’re using a classic theme, you’ll get the classic UI to go with it.

Let’s briefly walk-through the new Editor interface.

First, navigate to the site editor by clicking Appearance → Editor from the WordPress admin menu. That menu item may have a red “beta” label on it, like it currently does in WordPress 5.9.

That takes you to the site editor, which displays either your homepage or post archive, depending on what you have your homepage set to in Settings → Reading. From there it sort of looks like the fullscreen version of the block editor when creating or editing a page or post. But click on the WordPress logo in the top-left of the screen, and a left panel opens up revealing the WordPress Site Editor and its menu to navigate between different parts of the site. This includes Site, Templates, and Template Parts.

Screenshot of the WordPress Site Editor. There is a dark gray left panel open with an Editor heading and three links for Site, Templates, and Template Parts. The main content shows a preview of the site homepage in the WordPress Block Editor.

Let’s click into Templates. This shows us a list of the available templates provided by the theme, complete with a description of each one and where it is registered (e.g. the parent or a child theme).

Screenshot of the site editor's Templates screen which shows a two-column table listing template on the left and who a template was added by on the right.

The other way to get to this screen is from the initial page we landed on when entering the site editor. Click the name of the template in the top admin bar to reveal a button that takes you directly to the same Templates screen.

Screenshot of the Home template open in the WordPress Site Editor. The template name is at the top of the screen in a white toolbar and is expanded with a submenu that describes the template and provides a black button with white text to view all templates.

Any of templates can be edited just like any page or post in the block editor. Let’s say I don’t like to have a featured image on my index page and want to remove it. Simply delete the featured image block and save the template.

The other key part of the site editor UI is a list view that outlines the current blocks that are placed in the template. This has been a feature in WordPress since the introduction of the block editor, but what’s new this time around is that you can open and close parent blocks that contain child blocks like an accordion. Not only that, but it supports dragging and dropping blocks to change the layout directly from there.

The WordPress Site Editor with a white left panel expanded revealing an outline of the current blocks that are applied to the template.

One more thing in the site editor UI: you can clear out customizations with the click of a button. From the Templates screen, click the kebob menu next to a template and select the option to Clear customizations. This is a nice way to reset and start from scratch, should you need to.

Screenshot of the Template Parts screen in the WordPress Site Editor, showing a two-column able with a column that displays template names, and a column that identifies the location of the template part.

The WordPress Core team publishes regular updates on what’s new at Make WordPress Core. It’s worth bookmarking that to stay posted on the latest changes to the WordPress Block Editor and Site Editor.

Creating Templates and Template Parts

Templates, as you know, are core to WordPress theming. They enforce consistent and reusable layouts. That doesn’t change in WordPress 5.9. And neither does the fact that we can create template parts that are like module pieces that can be used in multiple template, say a post query that lives in an archive template and the home template.

What’s different in WordPress 5.9 is that they are created and managed with the site editor rather than PHP files that live in the theme folder.

The Block Editor Handbook lists three ways to create templates and template parts: (a) manually, by creating HTML files containing block markup, (b) using the site editor, and (c) using the template editing mode in the block editor.

Brief descriptions of creating template in the site editor and template editing mode are available in the Block Theme handbook. The WordPress 5.9 allows to create a new template using editor mode.

Screenshot of the Template Parts screen open in the WordPress Site Editor. A modal is open above the UI that contains an interface to create a template part, including the part's name and area.

The customized templates can then be exported to include in a block theme. So, yeah, we now have the ability to create a fully functioning WordPress theme without writing a line of code! The exported folder currently does not contain theme.json file, however there is a proposal over at GitHub to allow exporting both block themes and styles.

Screenshot of the WordPress Site Editor preferences panel open as a white panel to the left of the screen.

But for those who prefer working more closely with code, then manually creating WordPress templates and template parts is still a thing. You can still crack open a code editor and create HTML files containing block markup.

Global settings and styles (theme.json)

In classic themes, we write the styling rules in a style.css file. In block themes, styling is more challenging because CSS comes from different sources (e.g. core blocks, themes, and users). WordPress 5.8 introduced a concept of Global Styles — which is essentially a theme.json file — that, according to the docs, consolidate “the various APIs related to styles into a single point – a theme.json file that should be located inside the root of the theme directory.“

Screenshot of a theme dot jayson file open in the VS Code editor. The file contains objects for version and settings. The settings object contains a color object. The color object contains a palette objects which contains properties for slightly, color, name, and default.

The theme.json file is said to have been designed to offer more granular styling structure for theme authors with options to manage and customize the CSS styles coming from various origins. For example, a theme author may set certain styling features that are hidden from users, define default colors, font sizes and other features available to the user, and may set the default layout of the editor as well. Plus, theme.json allows you to customize styling on a per-block basis. It’s powerful, flexible, and super maintainable!

The block editor is expected to provide all the basic styling that theme authors are allowed to customize style, as defined by the theme.json file. However, the theme.json file could get quite long for a complex theme, and currently there is no way to partition it in a more digestible way. There is a GitHub ticket to restructure it so that different theme.json files map to theme hierarchy to /styles folder. That would be a nice enhancement for developer experience.

The default Twenty Twenty-Two theme is a good example of how WordPress full-site editing features use theme.json for global settings and styling blocks.

WordPress Block Theme approaches

Maybe you’ve always made WordPress themes from scratch. Perhaps you’ve relied on the Underscores theme as a starting point. Or maybe you have a favorite theme you extend with a child theme. The new features of the WordPress Site Editor really change the way we make themes.

Following are a few emerging strategies for block-based theme development that are deeply integrated with the WordPress Site Editor.

Universal themes

The Automattic team has built a Blockbase universal theme that’s dubbed as a new way to build themes, sort of similar to the Underscores starter theme. The Blockbase theme provides temporary “ponyfill” styles that the block editor “does not yet take into account on theme.json ‘custom’ properties” and that may eventually become obsolete once the Gutenberg plugin fully matures and is integrated into WordPress Core.

Using the universal parent theme approach, the Automattic has already released eight Blockbase child themes, and several others are in progress over at GitHub.

Twenty Twenty-Two default theme

The Twenty Twenty-Two default theme is another excellent starting point, as it’s really the first WordPress theme that ships with WordPress that is designed to work with the site editor.

In my opinion, this theme is excellent for theme developers who are already familiar with FSE features to showcase what is possible. For others users who are not developers and are not familiar with FSE features, customizations it in the block editor, then exporting it as a child theme could be painfully frustrating and overwhelming.

Hybrid themes

The concept of “Hybrid” themes in the context of FSE is discussed in this GitHub ticket. The idea is to provide paths for any user to use the site or template editor to override traditional theme templates.

Justin Tadlock in this WP Tavern post predicts four types of themes — block only, universal, hybrid, and classic — and speculates that theme authors may split between “block themes and a mashup of classic/hybrid themes.”

Proof in the pudding is provided by Frank Klein in “What I Learned Building a Hybrid Theme”:

A hybrid theme mixes the traditional theming approach with full-site editing features. A key component here is the theme.json file. It offers more control over the block editor’s settings, and simplifies styling blocks. A hybrid theme can use block templates as well, but that’s optional.

Frank is the author of the Block-Based Bosco theme and has expanded further on what a “hybrid theme” is by creating a hybrid version of the default Twenty Twenty theme. The theme is available on GitHub. Currently, there are no hybrid themes in the WordPres Theme Directory.

WordPress community themes

At the time of writing, there are 47 block-based themes with FSE features available in the theme directory. As expected, this approach is widely varied.

For example, in this post, Aino block theme author Ellen Bower discusses how they converted their classic theme into a block theme, detailing what makes a theme a “block” theme. The file structure of this approach looks different from the standard block theme structure we covered earlier.

Another popular block theme, Tove by Andars Noren, is described as a flexible base theme that follows the standard block theme file structure.

There’s also a very simple single page proof of the concept theme by Carolina Nymark that contains nothing but a single index.html called Miniblock OOAK. It’s already available in the theme directory, as is another one from Justin Tadlock that’s a work in progress (and he wrote up his process in a separate article).

Block Theme Generator app

Even though we’ve already established how friendly WordPress Block Themes are for non-developers, there are tools that help create complete block themes or merely a customized theme.json file.

David Gwyer, an Automattic engineer, has been working on a Block theme generator app which, at the time of writing, is in beta and available for testing by request.

Screenshot of the Block Theme Generator app homepage. It has a bright blue background and dark blue text that welcomes you to the site, and a screenshot of the app.

In my brief testing, the app only allowed me to generate customized theme.json file. But Gwyer told to WP Tavern that the app isn’t fully baked just yet, but features are being added often. Once complete, this could be a very helpful resource for theme authors to create customized block themes.

Block themes that are currently in use

This Twitter thread from Carolina Nymark shows some examples of block themes that are live and in production at the time of this writing. In a recent Yoast article, Carolina listed a bunch of personal and business websites that use block themes.

Personal sites

Business sites

As I mentioned earlier, I also have been using a block theme for one of my personal websites for a while. The default Twenty Twenty-Two theme also currently shows more than 60,000 active installs, which tells me there are many more examples of block-based theme implementations in the wild.

Building Block Child Themes

Child theming is still a thing in this new era of WordPress blocks, though something that’s still in early days. In other words, there is no clear approach to do make a block-based child theme, and there are no existing tools to help at the moment.

That said, a few approaches for creating WordPress child block themes are emerging.

Create Blockbase Theme plugin

The Automattic team is working on a plugin called Create Blockbase Theme. This will make it fairly trivial to create child themes based on the Blockbase universal theme we talked about earlier. Ben Dwyer has discussed how theme authors can build Blockbase child themes with simple six steps and without writing a line of code.

I tested the plugin in my own local environment, making only small changes to my Blockbase theme install, and everything appeared to work. Just note that the plugin is still experimental and under development, though you can follow the roadmap to see what’s up.

Using an alternate theme.json file

Kjell Reigstad, author of the default WordPress Twenty Twenty-Two theme, demonstrates how swapping a single theme.json file with another theme.json file that contains different style configurations can change the look and feel of a block-based theme design.

Kjell has opened a pull request that shows off several experimental child themes that are available for testing at the GitHub theme-experiment GitHub repository.

A three-by-two grid of screenshots of child themes based on the default WordPress Twenty Twenty-Two theme in alternate colors schemes.

Along these same lines, Ryan Welcher is in the process of developing a theme.json builder tool that will generate a customized theme.json file to facilitate non-coders to create similar child themes. More can be found in this WP Tavern post.

The Framboise child theme (available in theme directory) is an early example of that approach which includes only a single theme.json file.

Is there even a need for child themes?

Rich Tabor asks the question:

Indeed, a single theme.json file could serve as a child theme on its own. There is an ongoing discussion about allowing theme authors to ship multiple theme.json files with block themes that offer multiple global style variations. This way, a WordPress user could pick one of the variations to use on the site.

Some features of global style variations are already included in Gutenberg v12. 5 and expected to be available with WordPress 6.0.

Some personal thoughts

I’d be remiss to end this without weighing in on all this from a personal level. I’ll do this briefly in a few points.

Block themes are a WordPress answer to Jamstack criticisms

Jamstack enthusiasts have lobbed criticisms at the WordPress platform, most notably that WordPress themes are bloated with PHP files. Well, that’s no longer the case with WordPress Block Themes.

We saw earlier how an entire theme can be a single index.html file and a theme.json file. No bloat there. And nothing but markup.

I miss the WordPress Customizer

Especially the ability to inject custom code. From here on out, it’s going to require a deep level of familiarity with the WordPress Site Editor UI to accomplish the same thing.

Customizations a site is easy-peasy.

Customizing a classic theme — even something as minimal as changing fonts — can be difficult if you don’t know what you’re doing. That’s changed now with the site editor and the introduction of the theme.json file, where a theme can be customized (and even exported!) without writing a single line of code.

I still hold my opinion, though that the site editor interface is confusing. I think a pleasant user experience is a far ways off but looking forward to the next WordPress 6.0 release for better user experience.

Barriers to designing themes is getting lower.

It’s less about PHP and template files, and more about developing patterns and creating content. That sounds exactly what a content management system should be designed to do! I am already excited with new features being considered for the WordPress 6.0 release.

Resources

There is already a ton of other articles that cover WordPress Block Themes, full-site editing, and the block editor. And many of those came before WordPress 5.9 was released!

So, in addition to this article, here’s a collection of others for you to consider as you begin or continue down your journey of WordPress blocks and site editing.

WordPress 5.9

Site editor and block themes

Selected blog posts


As expected in beta testing, the site editor is still intimating and confusing, nevertheless, I am finding it a fun to work with block themes. Indeed, I have been already modifying Twenty Twenty-Two as a child theme and plan to create style alternatives using single theme.json file.

Have you been using block themes in your project, if so, share your experience and thoughts; I love reading any comments and feedback!


A Deep Introduction to WordPress Block Themes originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , , ,
[Top]

Mars Theme: A Deep Look at Frontity’s Headless WordPress Theme

This post was in progress before Automattic acquired Frontity and its entire team. According to Frontity’s founders, the framework will be transitioned into a community-led project and leave the project in “a stable, bug-free position” with documentation and features. Like other open-source community projects, Frontity will remain free as it has been, with opportunities to contribute to the project and make it an even better framework for decoupled WordPress. More detail is found in this FAQ page.

In my previous article, we created a headless WordPress site with Frontity and briefly looked at its file structure. In this companion article, we will go into a deep dive of the @frontity/mars-theme package, or Mars Theme, with a step-by-step walkthrough on how to customize it to make our own. Not only is the Mars Theme a great starter, it’s Frontity’s default theme — sort of like WordPress Twenty Twenty-One or the like. That makes it a perfect starting point for us to get hands-on experience with Frontity and its features.

Specifically, we will look at the fundamental parts of Frontity’s Mars Theme, including what they call “building blocks” as well as the different components that come with the package. We’ll cover what those components do, how they work, and finally, how styling works with examples.

Ready? Let’s go!


Frontity’s building blocks

Let’s revisit the file structure of the Frontity project we made in the last article as that shows us exactly where to find Frontity’s building blocks, the frontity.settings.js, and package.json and packages/mars-theme folder. We covered these is great detail before but, in particular, the package.json file gives us a lot of information about the project, like the name, description, author, dependencies, etc. Here’s what that file includes:

  • frontity: this is the main package that includes all the methods used in Frontity app development. It’s also where the CLI lives.
  • @frontity/core: This is the most important package because it takes care of all the bundling, rendering, merging, transpiling, serving, etc. We don’t need to access to it in order to develop a Frontity app. The full list is captured in the Frontity docs.
  • @frontity/wp-source: This package connects to the WordPress REST API of our site and fetches all the data needed in the Mars Theme.
  • @frontity/tiny-router: This package handles window.history and helps us with routing.
  • @frontity/htmal2react: This package converts HTML to React, working with processors that match HTML portions while replacing them with React components.

Frontity core, or @frontity/package (also referred as Frontity’s building block), is composed of useful React component libraries in its @frontity/components package, which exports helpful things like Link, Auto Prefetch, Image, Props, Iframe, Switch, and other functions, objects, etc., that can be directly imported into Frontity project components. A more detailed description of these components—including syntax info use cases—is in this package reference API.

The Frontity docs provide a little more information on what happens when a Frontity project is started:

When starting frontity, all the packages defined in frontity.settings.js are imported by @frontity/file-settings and the settings and exports from each package are merged by @frontity/core into a single store where you can access the state and actions of the different packages during development using @frontity/connect, the frontity state manager.

Next up, we’re familiarizing ourselves with how these building blocks, utilities and exports are used in the Mars Theme package to create a functioning Frontity project with a headless WordPress endpoint.

Section 1: Digging into the Mars Theme

Before discussing styling and customizing let’s briefly familiarize ourselves with the Mars Theme (@frontity/mars-theme) file structure and how it is put together.

#! frontity/mars-theme file structure packages/mars-theme/ |__ src/   |__ index.js   |__ components/      |__ list/        |__ index.js        |__ list-item.js        |__ list.js        |__ pagination.js      |__ featured-media.js      |__ header.js      |__ index.js      |__ link.js      |__ loading.js      |__ menu-icon.js      |__ menu-model.js      |__ menu.js      |__ nav.js      |__ page-error.js      |__ post.js      |__ title.js

The Mars Theme has three important component files: /src/index.js file, src/list/index.js and src/components/index.js. Frontity’s documentation is a great resource for understanding the Mars Theme, with especially great detail on how different Mars Theme components are defined and connected together in a Frontity site. Let’s start familiarizing ourselves with the theme’s three most important components: Root, Theme and List.

Theme Root component (/src/index.js)

The src/index.js file, also known as the theme’s Root, is one of the most important Mars Theme components. The Root serves as an entry point that targets <div id="root"> in the site markup to inject the roots of all the installed packages required to run a Frontity project. A Frontity theme exports a root and other required packages in the DOM as shown in the following use case example from the Frontity documentation:

<!-- /index.HTML (rendered by Frontity) --> <html>   <head>...</head>   <body>     <div id="root">       <MyAwesomeTheme />       <ShareModal />       <YetAnotherPackage />     </div>   </body> </html>

This Frontity doc explains how Frontity extends its theme using extensibility patterns called Slot and Fill. An example of the Root component (/src/index.js) is taken from its Mars Theme package (@frontity/mars-theme).

This is everything the package pulls in when initializing the Root component:

// mars-theme/src/components/index.js import Theme from "./components"; // import processor libraries import image from "@frontity/html2react/processors/image"; import iframe from "@frontity/html2react/processors/iframe"; import link from "@frontity/html2react/processors/link";  const marsTheme = {   // The name of the extension   name: "@frontity/mars-theme",   // The React components that will be rendered   roots: {     /** In Frontity, any package can add React components to the site.       * We use roots for that, scoped to the `theme` namespace. */     theme: Theme,   },   state: {     /** State is where the packages store their default settings and other       * relevant state. It is scoped to the `theme` namespace. */     theme: {       autoPrefetch: "in-view",       menu: [],       isMobileMenuOpen: false,       featured: {         showOnList: false,         showOnPost: false,       },     },   },    /** Actions are functions that modify the state or deal with other parts of     * Frontity-like libraries. */   actions: {     theme: {       toggleMobileMenu: ({ state }) => {         state.theme.isMobileMenuOpen = !state.theme.isMobileMenuOpen;       },       closeMobileMenu: ({ state }) => {         state.theme.isMobileMenuOpen = false;       },     },   },   /** The libraries that the extension needs to create in order to work */   libraries: {     html2react: {       /** Add a processor to `html2react` so it processes the `<img>` tags         * and internal link inside the content HTML.         * You can add your own processors too. */       processors: [image, iframe, link],     },   }, };  export default marsTheme;

The Mars Theme root component exports packages that includes any of the roots, fills, state, actions and libraries elements. More detailed information on Root can be found in this Frontity doc.

Theme component (/src/components/index.js)

The Frontity Theme component is its main root level component that is exported by the Theme namespace (lines 12-16, highlighted in the previous example. The Theme component is wrapped with the @frontity/connect function (line 51, highlighted below) which provides access to its state, actions and libraries props from the Root component instance and allows Theme component to read the state, manipulate through actions, or use code from other features packages in the libraries.

// mars-theme/src/components/index.js import React from "react" // Modules from @emotion/core, @emotion/styled, css, @frontity/connect, react-helmet import { Global, css, connect, styled, Head } from "frontity"; import Switch from "@frontity/components/switch"; import Header from "./header"; import List from "./list"; import Post from "./post"; import Loading from "./loading"; import Title from "./title"; import PageError from "./page-error";  /** Theme is the root React component of our theme. The one we will export  * in roots. */ const Theme = ({ state }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);    return (     <>       {/* Add some metatags to the <head> of the HTML with react-helmet */}       <Title />       <Head>         <meta name="description" content={state.frontity.description} />         <html lang="en" />       </Head>        {/* Add some global styles for the whole site, like body or a's.        Not classes here because we use CSS-in-JS. Only global HTML tags. */}       <Global styles={globalStyles} />        {/* Render Header component. Add the header of the site. */}       <HeadContainer>         <Header />       </HeadContainer>        {/* Add the main section. It renders a different component depending       on the type of URL we are in. */}       <Main>         <Switch>           <Loading when={data.isFetching} />           <List when={data.isArchive} />           <Post when={data.isPostType} />           <PageError when={data.isError} />         </Switch>       </Main>     </>   ); };  export default connect(Theme);  {/* define Global styles and styled components used Theme component here */} const globalStyles = css`   body {     margin: 0;     font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,       "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;   }   a,   a:visited {     color: inherit;     text-decoration: none;   } `; const HeadContainer = styled.div`   // ... `;  const Main = styled.div`   // ... `;

This example is pulled directly from the Mars Theme’s /src/components/index.js component, which we imported with connect from frontity (line 4, above). We are using state.source.get() to retrieve data to be rendered from the current path (lines 39-46, highlighted above); for example, List, Post and other components.

Section 2: Working with the List component

What we just looked at are the theme-level components in Frontity’s Mars Theme. You may have noticed that those components import additional components. Let’s look at a specific one of those, the List component.

The List component is exported by src/components/list/index.js which uses @loadable/components to split the List component code in such a way that the component only loads when a user clicks a List view; otherwise it won’t render at all, like when a Post view is clicked instead.

// src/components/list/index.js import { loadable } from "frontity";  // Codesplit the list component so it's not included if the users // load a post directly. export default loadable(() => import("./list"));

In this example, Frontity utilizes loadble functions (integrated from Loadable components) for code splitting which loads a component asynchronously and separates code into different bundles that are dynamically loaded at run time. Frontity’s core package API reference goes into much more detail.

Displaying lists of posts

To display a list of posts in an archive page, we first have to look Frontity src/components/list/list.js component. As the name suggests, the List component renders lists of posts using state.source.get(link) and its items field (lines 22-25, highlighted below).

// src/components/list/list.js import { connect, styled, decode } from "frontity"; import Item from "./list-item"; import Pagination from "./pagination";  const List = ({ state }) => {   // Get the data of the current list.   const data = state.source.get(state.router.link);   return (     <Container>       {/* If the list is a taxonomy, we render a title. */}       {data.isTaxonomy && (         <Header>           {data.taxonomy}: {state.source[data.taxonomy][data.id].name}         </Header>       )}       {/* If the list is an author, we render a title. */}       {data.isAuthor && (         <Header>Author: {state.source.author[data.id].name}</Header>       )}       {/* Iterate over the items of the list. */}       {data.items.map(({ type, id }) => {         const item = state.source[type][id];         // Render one Item component for each one.         return <Item key={item.id} item={item} />;       })}       <Pagination />     </Container>   ); }; export default connect(List);

In the code example above, the connect function is imported by frontity in line 2 and is wrapped around the exported connect(List) component in line 31 (the last line). Two other components, list-item.js and pagination.js are also imported. Let’s look at those next!

Here’s what we have for list-item.js:

// src/components/list/list-item.js import { connect, styled } from "frontity"; import Link from "../link"; import FeaturedMedia from "../featured-media";  const Item = ({ state, item }) => {   const author = state.source.author[item.author];   const date = new Date(item.date);   return (     <article>      {/* Rendering clickable post Title */}       <Link link={item.link}>         <Title dangerouslySetInnerHTML={{ __html: item.title.rendered }} />       </Link>       <div>         {/* If the post has an author, we render a clickable author text. */}         {author && (           <StyledLink link={author.link}>             <AuthorName>               By <b>{author.name}</b>             </AuthorName>           </StyledLink>         )}         {/* Rendering post date */}         <PublishDate>           {" "}           on <b>{date.toDateString()}</b>         </PublishDate>       </div>       {/* If the want to show featured media in the        * list of featured posts, we render the media. */}       {state.theme.featured.showOnList && (         <FeaturedMedia id={item.featured_media} />       )}       {/* If the post has an excerpt (short summary text), we render it */}       {item.excerpt && (         <Excerpt dangerouslySetInnerHTML={{ __html: item.excerpt.rendered }} />       )}     </article>   ); }; // Connect the Item to gain access to `state` as a prop export default connect(Item);

The Item component renders the preview of a blog post with clickable post title (lines, 12-14, highlighted above), author name (lines 19-21, highlighted above) and published date (lines: 25-28, highlighted above) along with <FeaturedMedia /> which serves as a post’s optional featured image.

Paginating a list of posts

Let’s look at the Pagination component that was rendered earlier in the List component by the src/components/list/pagination/js that follows:

// src/components/list/pagination.js import { useEffect } from "react"; import { connect, styled } from "frontity"; import Link from "../link";  const Pagination = ({ state, actions }) => {   // Get the total posts to be displayed based for the current link   const { next, previous } = state.source.get(state.router.link);   // Pre-fetch the the next page if it hasn't been fetched yet.   useEffect(() => {     if (next) actions.source.fetch(next);   }, []);   return (     <div>       {/* If there's a next page, render this link */}       {next && (         <Link link={next}>           <Text>← Older posts</Text>         </Link>       )}       {previous && next && " - "}       {/* If there's a previous page, render this link */}       {previous && (         <Link link={previous}>           <Text>Newer posts →</Text>         </Link>       )}     </div>   ); }; /**  * Connect Pagination to global context to give it access to  * `state`, `actions`, `libraries` via props  */ export default connect(Pagination);

The Pagination component is used so that users can paginate between lists of posts — you know, like navigating forward from Page 1 to Page 2, or backward from Page 2 to Page 1. The state, actions, libraries props are provided by the global context that wraps and exports them with connect(Pagination).

Displaying single posts

The Post component displays both single posts and pages. Indeed, structurally both are the same except, in posts, we usually display meta data (author, date, categories etc). Meta data isn’t usually used in pages.

In this Post component, conditional statements are rendered only if the post object contains data (i.e. data.isPost) and a featured image is selected in sate.theme.featured in the theme’s root component:

// src/components/post.js import { useEffect } from "react"; import { connect, styled } from "frontity"; import Link from "./link"; import List from "./list"; import FeaturedMedia from "./featured-media";  const Post = ({ state, actions, libraries }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);   // Get the data of the post.   const post = state.source[data.type][data.id];   // Get the data of the author.   const author = state.source.author[post.author];   // Get a human readable date.   const date = new Date(post.date);   // Get the html2react component.   const Html2React = libraries.html2react.Component;    useEffect(() => {     actions.source.fetch("/");     {/* Preloading the list component which runs only on mount */}     List.preload();   }, []);    // Load the post, but only if the data is ready.   return data.isReady ? (     <Container>       <div>         <Title dangerouslySetInnerHTML={{ __html: post.title.rendered }} />         {/* Only display author and date on posts */}         {data.isPost && (           <div>             {author && (               <StyledLink link={author.link}>                 <Author>                   By <b>{author.name}</b>                 </Author>               </StyledLink>             )}             <DateWrapper>               {" "}               on <b>{date.toDateString()}</b>             </DateWrapper>           </div>         )}       </div>       {/* Look at the settings to see if we should include the featured image */}       {state.theme.featured.showOnPost && (         <FeaturedMedia id={post.featured_media} />       )}       {/* Render the content using the Html2React component so the HTML is processed        by the processors we included in the libraries.html2react.processors array. */}       <Content>         <Html2React html={post.content.rendered} />       </Content>     </Container>   ) : null; }; {/* Connect Post to global context to gain access to `state` as a prop. */}  export default connect(Post);

We just saw how important the List component is when it comes to displaying a group of posts. It’s what we might correlate to the markup we generally use when working with the WordPress loop for archive pages, latest posts feeds, and other post lists.

There are a few more components worth looking at before we get into Mars Theme styling.

The following MarsLink component comes from src/components/link.js, which is a wrapper on top of the {@link Link} component. It accepts the same props as the {@link Link} component.

// src/components/link.js import { connect, useConnect } from "frontity"; import Link from "@frontity/components/link";  const MarsLink = ({ children, ...props }) => {   const { state, actions } = useConnect();    /** A handler that closes the mobile menu when a link is clicked. */   const onClick = () => {     if (state.theme.isMobileMenuOpen) {       actions.theme.closeMobileMenu();     }   };    return (     <Link {...props} onClick={onClick} className={className}>       {children}     </Link>   ); }; // Connect the Item to gain access to `state` as a prop export default connect(MarsLink, { injectProps: false });

As explained in this tutorial, the Link component provides a link attribute that takes a target URL as its value. Quoting from the doc: it outputs an <a> element into the resulting HTML, but without forcing a page reload which is what would occur if you simply added an <a> element instead of using the Link component.

Frontity menu (src/components/nav.js)

Earlier, we defined values for menu items in the frontity.settings.js file. In the Nav component (located in src/components/nav/js) those menu item values are iterated over, match their page url, and display the component inside the Header component.

// src/components/nav.js import { connect, styled } from "frontity"; import Link from "./link";  const Nav = ({ state }) => (   <NavContainer>     // Iterate over the menu exported from state.theme and menu items value set in frontity.setting.js     {state.theme.menu.map(([name, link]) => {       // Check if the link matched the current page url       const isCurrentPage = state.router.link === link;       return (         <NavItem key={name}>           {/* If link URL is the current page, add `aria-current` for a11y */}           <Link link={link} aria-current={isCurrentPage ? "page" : undefined}>             {name}           </Link>         </NavItem>       );     })}   </NavContainer> ); // Connect the Item to gain access to `state` as a prop export default connect(Nav);

The Mars Theme provides two additional menu components — menu.js and menu-modal.js — for mobile device views which, like nav.js, are available from the Mars Theme GitHub repository.

In Frontity, featured media items values are defined in the Root component ‘s theme.state.featured line that we discussed earlier. Its full code is available in the /src/components/featured-media.js component file.

Now that we’re more familiar with the Mars Theme, as well as its building blocks, components, and functions, we can move into the different approaches that are available for styling the Mars Theme front-end.

As we move along, you may find this Frontity doc a good reference for the various styling approaches we cover.

Section 4: How to style a Frontity project

For those of us coming from WordPress, styling in Frontity looks and feels different than the various approaches for overriding styles in a typical WordPress theme.

First off, Frontity provides us with reusable components made with with styled-components, and Emotion, a CSS library for styling components in JavaScript, right out of the box. Emotion is popular with React and JavaScript developers, but not so much in the WordPress community based on what I’ve seen. CSS-Tricks has covered CSS-in-JS in great detail including how it compares with other styling, and this video provides background information about the library. So, knowing that both styled-components and Emotion are available and ready to use is nice context as we get started.

Frontity’s documentation has great learning resources for styling frontity components as well as set-by-step guidance for customizing Frontity theme styles.

I am new to the CSS-in-JS world, except for some general reading on it here and there. I was exposed to CSS-in-JS styling in a Gatsby project, but Gatsby provides a bunch of other styling options that aren’t readily available in Frontity or the Mars Theme. That said, I feel I was able to get around that lack of experience, and what I learned from my discovery work is how I’m going to frame things.

So, with that, we are going to visit a few styling examples, referencing Frontity’s styling documentation as we go in order to familiarize ourselves with even more information.

Using styled-components

As the name suggests, we need a component in order to style it. So, first, let’s create a styled-component using Emotion’s styled function.

Let’s say we want to style a reusable <Button /> component that’s used throughout our Frontity project. First, we should create a <Button /> component (where its div tag is appended with a dot) and then call the component with a template literal for string styles.

// Creating Button styled component import { styled } from "frontity"  const Button = styled.div`   background: lightblue;   width: 100%;   text-align: center;   color: white; `

Now this <Button /> component is available to import in other components. Let’s look specifically at the Mars Theme <Header /> component to see how the styled-component is used in practice.

// mars-theme/src/components/header.js import { connect, styled } from "frontity"; import Link from "./link"; import MobileMenu from "./menu";  const Header = ({ state }) => {   return (     <>       <Container> // This component is defined later         <StyledLink link="/"> // This component is defined later           <Title>{state.frontity.title}</Title> // This component is defined later         </StyledLink>         // ...       </Container>     </>   ); };  // Connect the Header component to get access to the `state` in its `props` export default connect(Header);  // Defining the Container component that is a div with these styles const Container = styled.div`    width: 848px;   max-width: 100%;   box-sizing: border-box;   padding: 24px;   color: #fff;   display: flex;   flex-direction: column;   justify-content: space-around; `; // Defining Title component that is h2 with these styles  const Title = styled.h2`   margin: 0;   margin-bottom: 16px; `; // Defining StyledLink component that is a third-party Link component const StyledLink = styled(Link)`   text-decoration: none; `;

In the above code example, the <StyledLink /> component (lines 39-41, highlighted above) is used to style another component, <Link />. Similarly. the <Container /> and <Title /> styled-components are used to style the site title and the site’s main container width.

The Emotion docs describe how a styled component can be used as long as it accepts className props. This is a useful styling tool that can be extended using a variable as shown in the following example below from Frontity’s documentation:

// mars-theme/src/components/header.js  // ... // We create a variable to use later as an example Const LinkColor = "green";  // ...   // Defining StyledLink component that is a third-party Link component const StyledLink = styled(Link)`   text-decoration: none;   Background-color: $ {linkColor}; `;

The styled component above is used extensively in the Mars Theme. But before we go further, let’s look at using a CSS prop to style components.

Using a CSS prop

The css prop is available as a template literal for inline styling from the Frontity core package. It is similar to styled-components, except css does not return a React component but rather a special object that can be passed to a component through the css prop.

/* Using as CSS prop */ import { css } from "frontity";  const PinkButton = () => (   <div css={css`background: pink`}>     My Pink Button   </div> );

See that? We can style a component inline using the css prop on a component. Additional use case examples are available in the Emotion docs.

Using the <Global /> component

<Global /> is a React component that allows to us create site-wide general styles, though Frontity does not optimize it for performance. Global styles should be added to the <Theme /> root component.

// packages/mars-theme/src/components/index.js // ...  import { Global, css, styled } from "frontity"; import Title from "./title"; import Header from "./header"; // ...  // Theme root const Theme = ({ state }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);    return (    <>      {/* Add some metatags to the <head> of the HTML. */}       <Title />         // ...       {/* Add global styles */}       <Global styles={globalStyles} />       {/* Add the header of the site. */}       <HeadContainer>         <Header />       </HeadContainer>         // ...    </>   );  };  export default connect(Theme);  const globalStyles = css`   body {     margin: 0;     font-family: -apple-system, "Helvetica Neue", Helvetica, sans-serif;   }   a,   a:visited {     color: inherit;     text-decoration: none;   } `;  const HeadContainer = styled.div`   // ... `;

The <Global /> component has a style attribute that takes a css function as its value and consists of standard CSS inside back ticks (lines 35-45, highlighted above) as template literals. Frontity recommends using global styles for globally-used HTML tags, like <html>, <body>, <a>, and <img>.

Additional CSS styling options — including a dynamic CSS prop and React style props — are described in this Frontity guide to styling.

Resources for customizing a Frontity theme

I did a lot of research heading into my Mars Theme project and thought I’d share some of the more useful resources I found for styling Frontity themes:

  • Official Frontity themes. In addition to the default Mars Theme, Frontity has a ready-to-use package that ports the default WordPress Twenty Twenty theme in its entirety to a Frontity project. You will notice in the next section that my style customizations were inspired by this great learning resource.
  • Community themes. At this time of this writing, there are a grand total of nine Frontity community members who contributed fully functional theme packages. Those themes can be cloned into your own project and customized according to your needs. Likewise, many of the sites included in the Frontity showcase have GitHub repository links, and just as we can copy or pick up design tips from WordPress themes, we can use these resources to customize our own Frontity theme by referencing these packages.
  • Creating your own theme from scratch. The Frontity tutorial site has an excellent step-by-step guide to create your own fully working and functional theme package from scratch. Although it’s a little time consuming to go through it all, it is the best approach to fully understand a Frontity site project.

Now that we have covered the more commonly used Frontity styling techniques, let’s apply what we’ve learned to start customizing our Mars Theme project.

Section 5: Customizing the Frontity Mars Theme

I’m going to share one of my working Frontity projects, where I took the Mars Theme as a base and modified it with the resources we’ve covered so far. Because this is my learning playground, I took time to learn from Frontity default themes, community themes and Frontity showcase sites.

So here are examples of how I customized Frontity’s Mars Theme for my headless WordPress site project.

Changing the theme package name

First, I wanted to change the @frontity/mars-theme package name to something different. It’s a good idea to change the package name and make sure all of the dependencies in the package file are up to date. Luis Herrera outlines the required steps for renaming the Mars Theme package in this frontity community forum, which I used as a reference to go from @fontity/mars-theme package to @frontity/labre-theme.

So, open up the package.json file and change the name property on line 2. This is the name of the package that gets used throughout the project.

Screenshot of the package.json file open in VS Code. The left panel shows the files and the right panel displays the code.
I renamed my project from mars-theme to labre-theme in my package.json file,.

We should also update the name of the project folder while we’re at it. We can do that on line 25. I changed mine from ./package/mars-theme to ./package/labre-theme. Now, the theme package is properly listed as a dependency and will be imported to the project.

Our frontity-settings.js file needs to reflect the name change. So, let’s open that up and:

  • rename the package name on line 13 (I changed mine from @frontity/mars-theme to @frontity/labre-theme), and
  • rename the name on line 3 (I changed mine from mars-demo to labre-demo).
// @frontity-settings.js const settings = {   "name": "labre-demo",   "state": {     "frontity": {       "url": "http://frontitytest.local",       "title": "Frontity Demo Blog",       "description": "Exploring Frontity as Headless WordPress"     }   },   "packages": [     {       "name": "@frontity/labre-theme",       "state": {         "theme": {           "menu": [             ["Home", "/"],             ["Block", "/category/block/"],             ["Classic", "/category/classic/"],             ["Alignments", "/tag/alignment-2/"],             ["About", "/about/"]           ],  // ...

Next up, we want to re-initialize the project with these changes. We should delete the node_modules folder with rm -rf node_modules in a terminal and reinstall the npm package with yarn install. Once the npm package is reinstalled, everything gets properly linked internally and our Frontity project runs just fine without any errors.

Refactoring navigation with dynamic menu fetching

As we discussed earlier, Frontity menu items are either hard-coded in the frontity.setting.js file or in index.js component that’s stored in the Frontity state. However, WordPress can dynamically fetch the Frontity menu. In fact, Frontity just so happens to have a YouTube video on the subject. Let me break down the key steps here.

The first step is to install the WP-REST-API V2 Menus plugin in WordPress. The plugin is freely available in the WordPress Plugin Directory, which means you can find it and activate it directly from the WordPress admin.

Why do we need this plugin? It extends the new routes to all the registered WordPress menus to the REST API (e.g. /menus/v1/menus/<slug>).

If we check our project site at /wp-json/menu/v1/menus, it should display our selected menu items in the JSON. We can get the menu items with the menu item’s slug property.

Next, let’s use the menuHandler function from the tutorial. Create a new menu-handler.js file at src/components/handler/menu-handler.js and paste in the following code:

// src/components/handler/menu-handler.js const menuHandler = {   name: "menus",   priority: 10,   pattern: "/menu/:slug",   func: async ({ link, params, state, libraries }) => {     console.log("PARAMS:", params);     const { slug } = params;      // Fetch the menu data from the endpoint     const response = await libraries.source.api.get({       endpoint: `/menus/v1/menus/$ {slug}`,     });      // Parse the JSON to get the object     const menuData = await response.json();      // Add the menu items to source.data     const menu = state.source.data[link];     console.log(link);     Object.assign(menu, {       items: menuData.items,       isMenu: true,     });   }, };  export default menuHandler;

This menuHandler function is only executed if the pattern value (i.e. /menu/:slug) matches. Now let’s update our /src/index.js root component so it imports the handler:

// src/index.js import Theme from "./components"; import image from "@frontity/html2react/processors/image"; import iframe from "@frontity/html2react/processors/iframe"; import link from "@frontity/html2react/processors/link"; import menuHandler from "./components/handlers/menu-handler";  const labreTheme = {   // ...   state: {     theme: {       autoPrefetch: "in-view",       menu: [],       {/* Add menuURL property with menu slug as its value */}       menuUrl: "primary-menu",       isMobileMenuOpen: false,       // ...     },   },    /** Actions are functions that modify the state or deal with other parts of     * Frontity-like libraries */   actions: {     theme: {       toggleMobileMenu: ({ state }) => {         state.theme.isMobileMenuOpen = !state.theme.isMobileMenuOpen;       },       closeMobileMenu: ({ state }) => {         state.theme.isMobileMenuOpen = false;       },       {/* Added before SSR action */}       beforeSSR: async ({ state, actions }) => {         await actions.source.fetch(`/menu/$ {state.theme.menuUrl}/`);       },     },   },   libraries: {     // ...     {/* Added menuHandler source */}     source: {       handlers: [menuHandler],     },   }, };  export default labreTheme;

Add an array of handlers under the source property and fetch data before the beforeSSR function. It does not fetch but does match the menu-handler slug, which means menuHandler() is executed. That puts the menu items into state and they become available to manipulate.

Please note that we have added a new menuUrl property here (line 15 above) which can be used as a variable at our endpoint in handlers, as well as the nav.js component. Then, changing the value of menuUrl in the index.js root component, we could display another menu.

Let’s get this data into our theme through state and map with menu-items to display on the site.

// src/components/nav.js import { connect, styled } from "frontity"; import Link from "./link";  /** Navigation Component. It renders the navigation links */ const Nav = ({ state }) => {   {/* Define menu-items constants here */}   const items = state.source.get(`/menu/$ {state.theme.menuUrl}/`).items;    return (   <NavContainer>     {items.map((item) => {        return (         <NavItem key={item.ID}>            <Link link={item.url}>{item.title}</Link>          </NavItem>       );     })}   </NavContainer>   ); };  export default connect(Nav);  const NavContainer = styled.nav`   list-style: none;   // ...

If we change our menu slug here and in index.js, then we get a different menu. To view dynamic menu items in mobile view, we should similarly update menu-modal.js components as well.

Additionally, the tutorial describes how to fetch nested menus as well, which you can learn from the tutorial video, starting at about 18:09.

Modifying the file structure

I decided to restructure my Labre (formerly known as Mars) theme folder. Here’s how it looks after the changes:

#! modified Frontity labre-theme structure packages/labre-theme/ |__ src/   |__ index.js   |__ components/      |__image/      |__assets/      |__ list/      |__ footer/        |__footer.js        |__ widget.js      |__ header/        |__ header.js        |__ menu-icon.js        |__ menu-model.js        |__ nav.js      |__ pages/        |__ index.js        |__ page.js      |__ posts/        |__ index.js        |__ post.js      |__ styles/      // ...

As you can see, I added separate folders for pages, styles, headers, posts, and images. Please take a note that we have to update file paths in index.js and other related components anytime we change the way files and folders are organized. Otherwise, they’ll be pointing to nothing!

You may have noticed that the original Mars Theme folder structure includes neither a footer component, nor a separate page component. Let’s make those components to demonstrate how our new folder structure works.

We can start with the page component. The Mars Theme generates both pages and posts with the posts.js component by default — that’s because pages and posts are essentially the same except that posts have meta data (e.g. authors, date, etc.) and they can get away with it. But we can separate them for our own needs by copying the code in posts.js and pasting it into a new pages.js file in our /pages folder.

// src/components/pages/page.js import React, { useEffect } from "react"; import { connect, styled } from "frontity"; import List from "../list";  const Page = ({ state, actions, libraries }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);   // Get the data of the post.   const page = state.source[data.type][data.id];   //  ...   // Load the page, but only if the data is ready.   return data.isReady ? (     <Container>       <div className="post-title">         <Title dangerouslySetInnerHTML={{ __html: page.title.rendered }} />       </div>        {/* Render the content using the Html2React component so the HTML is processed by the processors we included in the libraries.html2react.processors array. */}       <Content>         <Html2React html={page.content.rendered} />       </Content>     </Container>   ) : null; }; // Connect the Page component to get access to the `state` in its `props` export default connect(Page);  // Copy styled components from post.js except, DateWrapper const Container = styled.div`     width: 90vw;     width: clamp(16rem, 93vw, 58rem);     margin: 0;     padding: 24px; ` // ..

All we did here was remove the meta data from post.js (lines 31-34 and 55-76) and the corresponding styled components. Just as we did with the Mars Theme /list folder, we should export the loadable function in both the pages and posts folders to code split the <List /> component. This way, the <List /> component isn’t displayed if a user is on a single post.

// src/components/pages/index.js import { loadable } from "frontity";  /** Codesplit the list component so it's not included *   if the users load a post directly. */ export default loadable(() => import("./page"));

Next, we should update path url of /src/components/index.js component as shown below:

// src/components/index.js import { Global, css, connect, styled, Head } from "frontity"; import Switch from "@frontity/components/switch"; import Header from "./header/header"; import List from "./list"; import Page from "./pages/page"; import Post from "./posts/post"; import Loading from "./loading"; import Title from "./title"; import PageError from "./page-error";  /** Theme is the root React component of our theme. The one we will export  * in roots. */ const Theme = ({ state }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);    return (     <>       // ...        {/* Add some global styles for the whole site */}        <Global styles={globalStyles} />       {/* Add the header of the site. */}       <HeadContainer>         <Header />       </HeadContainer>       {/* Add the main section */}       <Main>         <Switch>           <Loading when={data.isFetching} />           <List when={data.isArchive} />           <Page when={data.isPage} /> {/* Added Page component */}           <Post when={data.isPostType} />           <PageError when={data.isError} />         </Switch>       </Main>     </>   ); };  export default connect(Theme);  // styled components

Now we’re importing the <Page / component and have added our <Main /> styled component.

Let’s move on to our custom footer component. You probably know what to do by now: create a new footer.js component file and drop it into the /src/components/footer/ folder. We can add some widgets to our footer that display the sitemap and some sort of “Powered by” blurb:

// src/components/footer/footer.js import React from "react"; import { connect, styled } from "frontity"; import Widget from "./widget"  const Footer = () => {   return (   <>     <Widget />     <footer>       <SiteInfo>         Frontity LABRE Theme 2021 | {" "} Proudly Powered by {"  "}         <FooterLinks href="https://wordpress.org/" target="_blank" rel="noopener">WordPress</FooterLinks>         {"  "} and         <FooterLinks href="https://frontity.org/" target="_blank" rel="noopener"> Frontity</FooterLinks>       </SiteInfo>     </footer>     </>   ); };  export default connect(Footer); // ...

This is a super simple example. Please note that I have imported a <Widget /> component (line 4, highlighted above) and called the component (line 9, highlighted above). We don’t actually have a <Widget /> component yet, so let’s make that while we’re at it. That can be a widget.js file in the same directory as the footer, /src/components/footer/.

Screen shot of VS code editor open to a widget.js file that shows the syntax highlighted markup for a component.
This widget.js component was inspired by Aamodt Group‘s footer component, available in a GitHub repository.
Four columns of links, each with a heading. The text is dark against a light gray background.
The widget is hard-coded but works.

Customizing the theme header

The default header.js component in Mars Theme is very basic with a site title and site description and navigation items underneath. I wanted to refactor the header component with a site logo and title on the left and the nav.js component (top navigation) on the right.

// src/components/header.js import { connect, styled } from "frontity"; import Link from "./link"; import Nav from "./nav"; import MobileMenu from "./menu"; import logo from "./images/frontity.png"  const Header = ({ state }) => {   return (     <>       <Container>         <StyledLink link="/">          {/* Add header logo*/}           <Logo src={logo} />           <Title>{state.frontity.title}</Title>         </StyledLink>           {/*<Description>{state.frontity.description}</Description> */}           <Nav />       </Container>         <MobileMenu />     </>   ); }; // Connect the Header component to get access to the `state` in its `props` export default connect(Header);  const Container = styled.div`   width: 1000px;   // ...   `} {/* Logo styled component */} const Logo = styled.img`   max-width: 30px;   display: inline-block;   border-radius: 15px;   margin-right: 15px; `;  // ...

My refactored header.js component imports a logo image (line 6, highlighted above) and uses in line 14. The nav.js component shown below is basically the same, only with some minor styling modifications.

Screenshot showing refactored site header with site logo and site title (left) and top navigation (right)

Adding the <Global> style component

We have already covered the <Global> component and how it’s used for site-wide CSS. There are only a few global styles in the default Mars Theme root component, and I wanted to add more.

I did that with a separate globalStyles file at /src/components/styles/globalStyles.js — similar to Frontity’s Twenty Twenty theme — and added root variables, a CSS reset, and common site-wide element styles, found in the GitHub repo.

Implementing fluid typography

Even though it’s not really in scope, I really wanted to use fluid typography in my custom theme as part of my overall learning journey. So, I added it to the global styles.

CSS-Tricks has extensively covered fluid typography and how the clamp() function is used to set target font sizes. Following those CSS-Tricks posts and this Picalilli one as my guide, I defined two custom properties with clamped font size ranges on the :root element in the globalStyles.js component.

// src/components/styles/globalStyles.js :root {   --wide-container: clamp(16rem, 90vw, 70rem);   --normal-container: clamp(16rem, 90vw, 58rem); }

The wide-container wrapper is used for header and footer components whereas the normal-container will be used for displaying posts and pages.

I also clamped the headings under elementBase in the globalStyles.js component as shown in this GitHub repo.

It was a fun working with the clamp() function because it meant I could set a range of sizes without any media queries at all!

Adding webfonts to the theme

I also wanted to use a different webfont in my theme. Importing webfonts in CSS using @font-face is covered here on CSS-Tricks. Frontity’s Twenty Twenty Theme uses it, so that’s a good place to reference as well.

I wanted three Google fonts:

We can use the fonts with either with a <link>in the HTML head or with @import in CSS. But Chris covered how to use @font-face with Google Fonts, which allows us to optimize the number of HTTP requests we make since we can download the fonts to our own server.

I use the Google webfonts helper to host the downloaded font files. Here’s what I got:

/* source: google webfonts helper */ /* source-sans-pro-regular - latin */ @font-face {   font-family: 'Source Sans Pro';   font-style: normal;   font-weight: 400;   src: url('../fonts/source-sans-pro-v14-latin-regular.eot'); /* IE9 Compat Modes */   src: local(''),     url('../fonts/source-sans-pro-v14-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */     url('../fonts/source-sans-pro-v14-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */     url('../fonts/source-sans-pro-v14-latin-regular.woff') format('woff'), /* Modern Browsers */     url('../fonts/source-sans-pro-v14-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */     url('../fonts/source-sans-pro-v14-latin-regular.svg#SourceSansPro') format('svg'); /* Legacy iOS */ }

Looking at the Twenty Twenty Theme as a reference for how it’s done there, I created a font-face.js file and dropped it into the /src/components/styles folder as shown in this GitHub repository.

Those fonts point to a /fonts folder that doesn’t exist. So, let’s make one there and make sure all of the correct font files are in it so the fonts load properly.

Importing globalStyles and @face-font components to the root <Theme /> component

Let’s open our theme root component, /src/components.index.js, and add our globalStyles.js and font-face.js components in there. As shown below, we should import both components into index.js and call the components later.

// src/components/index.js  // ... import FontFace from "./styles/font-face"; import globalStyles from "./styles/globalStyles";  /** Theme is the root React component of our theme. The one we will export  * in roots. */ const Theme = ({ state }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);    return (     <>     // ...      {/* Add some global styles for the whole site, like body or a's.      *  Not classes here because we use CSS-in-JS. Only global HTML tags. */}       <Global styles={globalStyles} />       <FontFace />       {/* Add the header of the site. */}       // ...  export default connect(Theme);   {/* delete original globalStyles css component */}   // ...

Finally, we should remove mars-theme globalStyles component from index.js. Now our new fonts are applied throughout our project.

Styling pages and posts

Our posts and pages are pretty much styled already, except for some Gutenberg block contents, like buttons, quotes, etc.

To style our post entry meta data, let’s add icons for the author, date, categories, and tags. Frontity’s port of the WordPress Twenty Nineteen theme uses SVG icons and components for author.js, categories.js, posted-on.js and tags.js components, which we can totally copy and use in our own project. I literally copied the top-level entry-meta folder and everything in it from the frontity-twentynineteen theme and added it all to the /components/posts/ project folder.

Next we should update our src/components/list/list-item.js component so we can use the new assets:

// src/components/list/list-item.js  import { connect, styled } from "frontity"; import Link from "../link"; import FeaturedMedia from "../featured-media";  // import entry-meta import Author from "../entry-meta/author"; import PostedOn from "../entry-meta/posted-on";  const Item = ({ state, item }) => {    return (     <article>       <div>         {/* If the post has an author, we render a clickable author text. */}         <EntryMeta>           <Author authorId={item.author} /> {"|  "}           <PostedOn post={item} />         </EntryMeta>       </div>        <Link link={item.link}>         <Title dangerouslySetInnerHTML={{ __html: item.title.rendered }} />       </Link>       // ...     </article>   ); };  // Connect the Item to gain access to `state` as a prop export default connect(Item);

The styled component for the <EntryMeta /> component can be something like as shown in the GitHub repository.

With these styles in place, our archive page entry meta looks good with icons displayed before entry-meta taxonomy (authors, posted-on).

Here we will modify archives taxonomy page styling with more descriptive header. Let’s update list.js component of our /src/components/list/list.js as shown below.

// src/components/list/list.js  import React from "react"; import { connect, styled, decode } from "frontity"; import Item from "./list-item"; import Pagination from "./pagination";  const List = ({ state }) => {   // Get the data of the current list.   const data = state.source.get(state.router.link);    return (     <Container className="entry-content">       {/* If the list is a taxonomy, we render a title. */}       {data.isAuthor ? (         <Header>           Author Archives:{" "}           <PageDescription>           {decode(state.source.author[data.id].name)}           </PageDescription>         </Header>         ) : null}          {/* If the list is a taxonomy or category, we render a title. */}         {data.isTaxonomy || data.isCategory ? (           <Header>             {data.taxonomy.charAt(0).toUpperCase() + data.taxonomy.slice(1)}{" "}             Archives:{" "}             <PageDescription>             {decode(state.source[data.taxonomy][data.id].name)}             </PageDescription>           </Header>         ) : null}       // ...        <Pagination />     </Container>   ); }; export default connect(List);  const PageDescription = styled.span`   font-weight: bold;   font-family: var(--body-family);     color: var(--color-text); `; // ...

In the example above, we wrapped taxonomy.id data with PageDesctiption styled component applied some styling rules.

The post pagination in the default Mars Theme is very basic with almost no styling. Let’s borrow from the Frontity Twenty Nineteen theme again and add the pagination component and styling from the theme by copying the pagination.js component file in its entirety, and paste it to /src/components/list/pagination.js in our theme.

Showing two example posts in a post list, one with comments enabled, and the other with comments disabled. The post content is black against a light gray background.
I added some minor CSS and it works perfectly in our project.

To customize the actual individual posts and pages, let’s make bold header title that’s centered and displays the entry meta:

// src/components/posts/post.js  // ... // Import entry-meta import Author from "../entry-meta/author"; import PostedOn from "../entry-meta/posted-on"; import Categories from "../entry-meta/categories"; import Tags from "../entry-meta/tags";  const Post = ({ state, actions, libraries }) => {   // ...   // Load the post, but only if the data is ready.   return data.isReady ? (     <Container className="main">       <div>         <Title dangerouslySetInnerHTML={{ __html: post.title.rendered }} />          {/* Hide author and date on pages */}         {data.isPost && (           <EntryMeta>           <Author authorId={post.author} />           <PostedOn post={post} />         </EntryMeta>         )}       </div>        {/* Look at the settings to see if we should include the featured image */}       {state.theme.featured.showOnPost && (         <FeaturedMedia id={post.featured_media} />       )}        {data.isAttachment ? (         <div dangerouslySetInnerHTML={{ __html: post.description.rendered }} />       ) : (         <Content>           <Html2React html={post.content.rendered} />           {/* Add footer meta-entry */}           <EntryFooter>             <Categories cats={post.categories} />             <Tags tags={post.tags} />           </EntryFooter>         </Content>       )}     </Container>   ) : null; };  export default connect(Post); // ...
Screenshot showing header meta-entry (top) and footer meta-entry (bottom). Header has a large bold title above meta containing the author name and post date, all centered. There is a thick line between the header and content. The content is a simple paragraph containing lorem ipsum text. Dark content against a light gray background.

Adding Gutenberg block styles

WordPress uses a separate stylesheet for blocks in the Block Editor. Right now, that stylesheet isn’t being used but it would be great if we could get some base styles in there that we use for the various block content we add to pages and posts.

A post with DevTools open and highlighting the markup for the button component.
That .wp-block-buttons class is declared in the WordPress blocks stylesheet that we aren’t using… yet.

The WordPress Block Editor uses two styling files: style.css and theme.css. Let’s copy these directly from Frontity’s port of the Twenty Twenty theme because that’s how they implemented the WordPress styles. We can place those inside a /styles/gutenberg/ folder.

“Gutenberg” is the codename that was given to the WordPress Block Editor when it was in development. It’s sometimes still referred to that way.

Let’s add the above two style files to our theme root component, /src/components/index.js, just like we did earlier for globalStyles:

//  src/components/index.js import gutenbergStyle from "./styles/gutenberg/style.css"; import gutenbergTheme from "./styles/gutenberg/theme.css"

Here’s our updated <Theme /> root component:

// src/components/index.js  // ... import FontFace from "./styles/font-face"; import globalStyles from "./styles/globalStyles"; // Add Gutenberg styles import gutenbergStyle from "./styles/gutenberg/style.css"; import gutenbergTheme from "./styles/gutenberg/theme.css"  /** Theme is the root React component of our theme. The one we will export   * in roots. */ const Theme = ({ state }) => {   // Get information about the current URL.   const data = state.source.get(state.router.link);    return (     <>     // ...     {/* Add some global styles for the whole site, like body or a's.       * Not classes here because we use CSS-in-JS. Only global HTML tags. */}       <Global styles={globalStyles} />       <Global styles={css(gutenbergStyle)} />       <Global styles={css(gutenbergTheme)} />       <FontFace />       {/* Add the header of the site. */}       // ... export default connect(Theme);   {/* Delete original globalStyles css component */}  // ...

We could go about overriding styles many different ways. I went with a simple route. For example, to overriding button styles — .wp-block-buttons — in the styled-component for pages and posts.

Screenshot showing button style customization (left panel) and styled button in blue (right)

We can write override any other block styles the same way. In Frontity’s Twenty Nineteen theme, the entire stylesheet from the WordPress version of the theme is added to the Frontity version to replicate the exact same appearance. Frontity’s Twenty Twenty port uses only a select few of the styles in the WordPress Twenty Twenty themes, but as inline styles.

Additional styling resources

All the resources we covered in this section on styling are available in the GitHub repository. If you wish to expand my @frontity/labre-theme project further, here are the resources that I gathered.

Section 6: Resources and credit

There are ample resources to learn and customize your Frontity project. While preparing this post, I have referred to the following resources extensively. Please refer to original posts for more detailed information.

Frontity documentation and articles

  • Step-by-step tutorial (Frontity): This is the perfect place to start if you’re new to Frontity, or even if you’ve previously used Frontity and want to level up.
  • Conceptial guides (Frontity): These guides helps solve some of the common challenges that come up when working with dynamic server-side rendering in React apps connected to WordPress.
  • Frontity API reference (Frontity). This contains detailed information about Frontity CLI, packages, plugins and themes. Once you’ve mastered the basics of working with Frontity, this is where you’re likely to spend most of your time when working on projects.”
  • Frontity example repo (Frontity): This is a collection of Frontity projects that demonstrate how Frontity is used in the wild.

Frontity case studies

Frontity talks and videos

Frontity community

Frontity has a vibrant and engaging community forum for asking questions or getting help regarding your Frontity project.

Wrapping up and personal thoughts

If you can’t already tell from this post or the others I’ve written, I have a huge passion for headless WordPress sites. As I wrote in a previous article, I came across Frontity through when Chris posted this article. I have been experimenting with it for over six months, choosing to take a deep drive into Frontity and the building blocks used in its default Mars Theme. I must admit that it’s a fascinating software framework and I’ve had an enjoyable learning experience. I may even use this sort of setup for my own personal site!

Here are a few key takeaways from my experience working with Frontity so far:

  • It’s beginner-friendly and low maintenance: One of the things that impressed me most with Frontity is how relatively easy it is to jump into, even as a beginner. It installs with a couple of commands and takes care of all the setup and configuration for connecting to WordPress via the REST API—something I would have struggled with if left to my own devices.
  • It works with experimental block themes. In my very limited testing, Frontity’s framework works as expected with experimental block themes, just as it does with classic WordPress themes, like Twenty Twenty. I tested with the Quadrat theme that supports the experimental stuff the Gutenberg team is working on.
  • Hosting is good, but maybe too expensive: As Chris wrote, Frontity is “a perfect match for Vercel.” However, the current Jamstack pricing model that includes Vercel is unattractive for many ordinary WordPress users.
  • Frontity’s documentation is good, but could be better: The Frontity team recently reorganized Frontity documentation into tutorials, guides and an API reference. However, in my opinion it’s still confusing for those just getting into the framework.

Because I enjoyed this project so much, I am currently doing a theme project from scratch. Even in WordPress, I learned best by getting my hands dirty building WordPress themes from scratch.

While I am still doing my Gatsby and Frontity side projects, I have not lost my sight from the ongoing WordPress block editor and block-based theme development. At the time of writing, there are already sixteen block-based themes in the WordPress theme directory. I have just started exploring and understanding experimental block themes, which might be another interesting learning project.

After this project, my thoughts about Gatsby, Frontity and the concept of headless sites are still evolving. That’s only because it’s tough to make a fair comparison of when a lot of the tooling is actively in development and changing all the time. There are even experimental themes, that are much lighter and different structural markups than the current PHP-based classic themes, which might be a subject for yet another time.


Please share your experience and thoughts if you have been using Frontity in your projects. As always, I enjoy reading any comments and feedback!


The post Mars Theme: A Deep Look at Frontity’s Headless WordPress Theme appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

A Deep Dive on Skipping to Content

While most people browsing the web on a computer use a mouse, many rely on their keyboard instead. Theoretically, using a web page with the keyboard should not be a problem — press the TAB key to move the keyboard focus from one focusable element to the next then press ENTER to activate, easy! However, many (if not most) websites tend to have a menu of links at the top of the page, and it can sometimes take a lot of presses to get to the content that you want. Take the homepage of 20min for example, one of the biggest news sites here in Switzerland. You want to read the top story? That’ll be the best part of 40 key presses — not the best best use of anyone’s time.

So, if you want keyboard users to actually use your website rather than getting bored and going somewhere else, you need to do a bit of work behind the scenes to make skipping straight to your main content quicker and easier. You can find all sorts of techniques for this scattered across the web (including here at CSS-Tricks) but most are missing a trick or two and many recommend using outdated or deprecated code. So, in this article, I’m going to take a deep dive into skipping to content and cover everything in a 2021-friendly fashion.

Two types of keyboard users

Although there are numerous differences in the exact type of keyboard or equivalent switch device that people use to navigate, from a coding point of view, we only need to consider two groups:

  • People who use the keyboard in conjunction with a screen reader — like NVDA or JAWS on a PC, or VoiceOver on a Mac — that reads the content of the screen out loud. These devices are often used by people with more severe visual impairments.
  • All other keyboard users.

Our skip-to-content techniques need to cater to both these groups while not getting in the way of all the mouse users. We will use two complementary techniques to get the best result: landmarks and skip links.

Look at a basic example

I created an example website I’m calling Style Magic to illustrate the techniques we’re covering. We’ll start off with it in a state that works fine for a mouse user but is a bit of a pain for those using a keyboard. You can find the base site and the versions for each of the techniques in this collection over at CodePen and, because testing keyboard navigation is a little tricky on CodePen, you can also find standalone versions here.

Try using the TAB key to navigate this example. (It’s easier on the standalone page; TAB to move from one link to the next, and SHIFT+TAB to go backwards.) You will find that it’s not too bad, but only because there aren’t many menu items.

If you have the time and are on Windows then as I’d also encourage you to download a free copy of the NVDA screen reader and try all the examples with that too, referring to WebAIM’s overview for usage. Most of you on a Mac already have the VoiceOver screen reader available and WebAIM has a great intro to using it as well.

Adding landmarks

One of the things that screen reading software can do is display a list of landmarks that they find on a web page. Landmarks represent significant areas of a page, and the user can pull up that list and then jump straight to one of those landmarks.

If you are using NVDA with a full keyboard, you hit INS+F7 to bring up the “Elements List” then ALT+d to show the landmarks. (You can find a similar list on VoiceOver by using the Web Item Rotor.) If you do that on example site, though, you will only be presented with an unhelpful empty list.

An open dialog with a UI for viewing different types of content on the page in a screen reader, including links, headings, form fields, buttons, and landmarks. Landmarks is selected and there are no results in the window.
A disappointingly empty list of landmarks in NVDA

Let’s fix that first.

Adding landmarks is incredibly easy and, if you are using HTML5, you might already have them on your website without realizing it, as they are directly linked to the HTML5 semantic elements (<header>, <main>, <footer>, and so on).

Here’s a before and after of the HTML used to generate the header section of the site:

<div class="bg-dark">   <div class="content-width flex-container">     <div class="branding"><a href="#">Style Magic</a></div>     <div class="menu-right with-branding">       <a href="#">Home</a>       <!-- etc. -->       </div>   </div> </div>

Becomes

<div class="bg-dark">  <header class="content-width flex-container">         <section class="branding"><a href="#">Style Magic</a></section>     <nav aria-label="Main menu" class="menu-right with-branding">       <a href="#">Home</a>       <!-- etc. -->     </nav>   </header> </div>

The classes used remain the same, so we don’t need to make any changes at all to the CSS.

Here’s a full list of the changes we need to make in our example site:

  • The <div> denoting the header at the top of the page is now a <header> element.
  • The <div> containing the branding is now a <section> element.
  • The two <div>s containing menus have been replaced with <nav> elements.
  • The two new <nav> elements have been given an aria-label attribute which describes them: “Main menu” for the menu at the top of the page, and “Utility menu” for the menu at the bottom of the page.
  • The <div> containing the main content of the page is now a <main> element.
  • The <div> denoting the footer at the bottom of the page is now a <footer> element.

You can see the full updated HTML on CodePen.

Let’s try that landmark list trick in NVDA again (INS+F7 then ALT+d — here’s the link to the standalone page so you can test yourself):

Open screen reader dialog window showing the landmarks on the current page, including "banner," "main," and "content info."
Hurrah for landmarks!

Great! We now have the banner landmark (mapped to the <header> element), Main menu; navigation (mapped to the top <nav> element, and displaying our aria-label), main (mapped to <main>) and content info (mapped to footer). From this dialog I can use TAB and the cursor keys to select the main landmark and skip straight to the content of the page, or even better, I can just press the D key when browsing the page to jump from one landmark role directly to the next. Users of the JAWS screen reader have it even easier — they can simply press Q when browsing to jump straight to the main landmark.

As an added bonus, using semantic elements also help search engines understand and index your content better. That’s a nice little side benefit of making a site much more accessible.

I expect you’re sitting back thinking “job done” at this point. Well, I’m afraid there’s always a “but” to consider. Google did some research way back in 2011 on the use of CTRL+f to search within a web page and found that a startling 90% of people either didn’t know it existed, or have never used it. Users with a screen reader behave in much the same way when it comes to landmarks — a large portion of them simply do not use this feature even though it’s very useful. So, we’re going to add a skip link to our site to help out both groups as well as all those keyboard users who don’t use a screen reader.

The basic requirements for what makes a good skip link are:

  • It should be perceivable to all keyboard users (including screen reader users) when it is needed.
  • It should provide enough information to the keyboard user to explain what it does.
  • It should work on as wide a range of current browsers as possible.
  • It should not interfere with the browsing of a mouse user.

Step 1: Improving the keyboard focus appearance

First up, we’re going to improve the visibility of the keyboard focus across the site. You can think of the keyboard focus as the equivalent to the position of the cursor when you are editing text in a word processor. When you use the TAB key to navigate the keyboard focus moves from link to link (or form control).

The latest web browsers do a reasonable job of showing the position of the keyboard focus but can still benefit from a helping hand. There are lots of creative ways to style the focus ring, though our goal is making it stand out more than anything.

We can use the :focus pseudo-class for our styling (and it’s a good idea to apply the same styles to :hover as well, which we’ve already done on the example site — CodePen, live site). That’s the very least we can do, though it’s common to go further and invert the link colors on :focus throughout the page.

Here’s some CSS for our :focus state (a copy of what we already have for :hover):

a:focus { /* generic rule for entire page */   border-bottom-color: #1295e6; } .menu-right a:focus, .branding a:focus {   /* inverted colors for links in the header and footer */   background-color: white;   color: #1295e6; }

Step 2: Adding the HTML and CSS

The last change is to add the skip link itself to the HTML and CSS. It consists of two parts, the trigger (the link) and the target (the landmark). Here’s the HTML that I recommend for the trigger, placed right at the start of the page just inside the <header> element:

<header class="content-width flex-container">   <a href="#skip-link-target" class="text-assistive display-at-top-on-focus">Skip to main content.</a>   <!-- etc. --> </header>

And here’s the HTML for the target, placed directly before the start of the <main> content:

<a href="#skip-link-target" class="text-assistive display-at-top-on-focus" id="skip-link-target">Start of main content.</a>  <main class="content-width">   <!-- etc. --> </main>

Here’s how the HTML works:

  • The skip link trigger links to the skip link target using a standard page fragment (href="#skip-link-target") which references the id attribute of the target (id="skip-link-target"). Following the link moves the keyboard focus from the trigger to the target.
  • We link to an anchor (<a>) element rather than adding the id attribute directly to the <main> element for two reasons. First, it avoids any issues with the keyboard focus not moving correctly (which can be a problem in some browsers); secondly, it means we can provide clear feedback to the user to show that the skip link worked.
  • The text of the two links is descriptive so as to clearly explain to the user what is happening.

We now have a functioning skip link, but there’s one problem: it’s visible to everyone. We’ll use CSS to hide it from view by default, which keeps it out of the way of mouse users, then have it appear only when it receives the keyboard focus. There are lots of ways to do this and most of them are okay, but there’s a couple of wrong ways that you should avoid:

  • Do: use clip-path to make the link invisible, or use transform: translate or position: absolute to position it off screen instead.
  • Don’t: use display: none, visibility: hidden, the hidden attribute, or set the width or height of the skip link to zero. All of these will make your skip link unusable for one or both classes of keyboard users.
  • Don’t: use clip as it is deprecated.

Here’s the code that I recommend to hide both links. Using clip-path along with its prefixed -webkit- version hits the spot for 96.84% of users at time of writing, which (in my opinion) is fine for most websites and use cases. Should your use case require it, there are a number of other techniques available that are detailed on WebAIM.

.text-assistive {   -webkit-clip-path: polygon(0 0, 0 0, 0 0, 0 0);   clip-path: polygon(0 0, 0 0, 0 0, 0 0);   box-sizing: border-box;   position: absolute;   margin: 0;   padding: 0; }

And to show the links when they have focus, I recommend using a version of this CSS, with colors and sizing to match your branding:

.text-assistive.display-at-top-on-focus {   top: 0;   left: 0;   width: 100%;   } .text-assistive.display-at-top-on-focus:focus {   -webkit-clip-path: none;   clip-path: none;   z-index: 999;   height: 80px;   line-height: 80px;   background: white;   font-size: 1.2rem;   text-decoration: none;   color: #1295e6;   text-align: center; } #skip-link-target:focus {   background: #084367;   color: white; }

This provides for a very visible display of the trigger and the target right at the top of the page where a user would expect to see the keyboard focus directly after loading the page. There is also a color change when you follow the link to provide clear feedback that something has happened. You can fiddle with the code yourself on CodePen (shown below) and test it with NVDA on the standalone page.

Taking this further

Skip links aren’t just for Christmas your main menu, they are useful whenever your web page has a long list of links. In fact, this CodePen demonstrates a good approach to skip links within the content of your pages (standalone page here) using transform: translateY() in CSS to hide and show the triggers and targets. And if you are in the “lucky” position of needing to support older browsers, then you here’s a technique for that on this CodePen (standalone page here).

Let’s check it out!

To finish off, here are a couple of short videos demonstrating how the skip links work for our two classes of keyboard user.

Here’s how the finished skip link works when using the NVDA screen reader:

Here it is again when browsing using the keyboard without a screen reader:


We just looked at what I consider to be a modern approach for accessible skip links in 2021. We took some of the ideas from past examples while updating them to account for better CSS practices, an improved UI for keyboard users, and an improved experience for those using a screen reader, thanks to an updated approach to the HTML.


The post A Deep Dive on Skipping to Content appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Getting Deep into Shadows

Let’s talk shadows in web design. Shadows add texture, perspective, and emphasize the dimensions of objects. In web design, using light and shadow can add physical realism and can be used to make rich, tactile interfaces.

Take the landing page below. It is for cycling tours in Iceland. Notice the embellished drop shadow of the cyclist and how it creates the perception that they are flying above not only the content on the page, but the page itself, as though they are “popping” over the screen. It feels dynamic and immediate, which is perfect for the theme of adventure.

A view of a BMX biker from above leaping over a vast area of light brown land with the words free ride along the bottom edge in large, bold, and white block lettering.
Credit: Kate Hahu

Compare that with this next example. It’s a “flat” design, sans shadows. In this case, the bike itself is the focal point. The absence of depth and realism allows the bike to stand out on its own.

Screenshot of a webpage with a light pink background with a white box that contains the site content with a headline that reads "Ride as Much or as Little" in red, an email subscription form, and a large image of a red and white bicycle to the right.
Credit: saravana

You can appreciate the differences between these approaches. Using shadows and depth is a design choice; they should support the theme and the message you want the content to convey.

Light and shadows

As we just saw, depth can enhance content. And what exactly makes a shadow? Light!

It’s impossible to talk about shadow without getting into light. It controls the direction of a shadow as well as how deep or shallow the shadow appears. You can’t have one without the other.

Google’s Material Design design system is a good example of employing light and shadows effectively. You’ve certainly encountered Material Design’s aesthetics because Google employs it on nearly all of its products.

A masonry grid of photos on a mobile screen. The purple header that contains the page title and hamburger menu has a shadow along its bottom edge that separates it from the white background of the photos.

White modal with a list of checkboxes. The modal has an underlying black shadow against the background.

A two-by-three grid of cards on a mobile screen. Each card has a light shallow shadow against a white background.

The design system takes cues from the physical world and expresses interfaces in three-dimensional space using light, surfaces, and cast shadows. Their guidelines on using light and shadows covers this in great detail.

In the Material Design environment, virtual lights illuminate the UI. Key lights create sharper, directional shadows, called key shadows. Ambient light appears from all angles to create diffused, soft shadows, called ambient shadows.

Shadows are a core component of Material Design. Compare that with Apple’s Human Interface Guidelines for macOS, where translucency and blurring is more of a driving factor for evoking depth.

Screenshot of Apple's Reminders app on a desktop. The left column that contains search and navigation is opaque and blends lightly into the desktop background while the solid white right column contains a checkbox list of reminders.

Screenshot of Apple's Maps app. The left column contains the map addresses and different route options with an opaque background that lightly blends in with the desktop background. The right column contains the map and does not blend in with the background.

In this case, light is still an influential factor, as it allows elements to either blend into the desktop, or even into other panels in the UI. Again, it’s is a design choice to employ this in your interface. Either way, you can see how light influences the visual perception of depth.

Light sources and color

Now that we understand the relationship between light and shadows, we ought to dig in a little deeper to see how light affects shadows. We’ve already seen how the strength of light produces shadows at different depths. But there’s a lot to say about the way light affects the direction and color of shadows.

There are two kinds of shadows that occur when a light shines on an object, a drop shadow and a form shadow.

Photo of an orange with light shining on it from the top right. That area is brighter than the left side which is covered in shadow. The ground contains a light reflection of the orange.

Drop shadows

A drop shadow is cast when an object blocks a light source. A drop shadow can vary in tone and value. Color terminology can be dense and confusing, so let’s talk about tone and value for a moment.

Tone is a hue blended with grey. Value describes the overall lightness or darkness of a color. Value is a big deal in painting as it is how the artist translates light and object relationships to color.

Illustration showing the effects of Hue, Tint, Tone, and Shade on red rectangles. Each rectangle is a slightly different shade of red where tint adds white, tone adds gray and shade adds black.

In the web design world, these facets of color are intrinsic to the color picker UI.

Form shadows

A form shadow, on the other hand, is the side of an object facing away from the light source. A form shadow has softer, less defined edges than a drop shadow. Form shadows illustrate the volume and depth of an object.

The appearance of a shadow depends on the direction of light, the intensity of light, and the distance between the object and the surface where the shadow is cast. The stronger the light, the darker and sharper the shadow is. The softer the light, the fainter and softer the shadow is. In some cases, we get two distinct shadows for directional light. The umbra is where light is obstructed and penumbra is where light is cast off.

Two vertically stacked illustrations.The top is a green circle with a yellow light source coming at it from the left and both umbra and penumbra shadows are cast to the right. The bottom illustration is the same green circle and light source, but with a solid black shadow cast to the right.

If a surface is close to an object, the shadow will be sharper. If a surface is further away, the shadow will be fainter. This is not some abstract scientific stuff. This is stuff we encounter every day, whether you realize it or not.

This stuff comes up in just about everything we do, even when writing with a pencil.

Light may also be reflected from sides of an object or another surface. Bright surfaces reflect light, dark surfaces absorb light.

These are the most valuable facets of light to understand for web design. The physics behind light is a complex topic, I have just lightly touched on some of it here. If you’d like to see explicit examples of what shadows are cast based on different light sources, this guide to drawing shadows for comics is instructive.

Positioning light sources

Remember, shadows go hand-in-hand with light, so defining a light source — even though there technically isn’t one — is the way to create impressive shadow effects. The trick is to consistently add shadows relative to the light source. A light source positioned above an element will cast a shadow below the element. Placing a light source to the left of an element will cast a shadow to the right. Placing multiple light sources to the top, bottom, left and right of an element actually casts no shadow at all!

Showing two browser mockups side by side. The left has light shining on it from all four directions showing uniform light and no shadows. The right has a single light source from the top casting a shadow along the bottom edge.

A light source can be projected in any direction you choose. Just make sure it’s used consistently in your design, so the shadow on one element matches other shadows on the page.

Elevation

Shadows can also convey elevation. Once again, Material Design is a good example because it demonstrates how shadows are used to create perceived separation between elements.

Showing a mobile screen flat on a light blue background with header, box, and navigational elements elevated over the screen showing depth.
Credit: Nate Wilson

Inner shadows

Speaking of elevation, the box-shadow property is the only property that can create inner shadows for a sunken effect. So, instead of elevating up, the element appears to be pressed in. That’s thanks to the inset keyword.

That good for something like an effect where clicking a button appears to physically press it.

It’s also possible to “fake” an inner text shadow with a little trickery that’s mostly supported across browsers:

Layering shadows

We’re not limited to a single shadow per element! For example, we can provide a comma-separated list of shadows on the box-shadow property. Why would we want to do that? Smoother shadows, for one.

Interesting effects is another.

Layering shadows can even enhance typography using the text-shadow property.

Just know that layering shadows is a little different for filter: drop-shadow() It’s syntax also takes a list, but it’s space-separated instead of comma-separated.

.box {   box-shadow:      0 2px 2px #555, /* commas */     0 6px 5px #777,     0 12px 10px #999   ; }  .box {   filter:     drop-shadow(0 2px 2px #555) /* spaces */     drop-shadow(0 6px 5px #777)     drop-shadow(0 12px 10px #999); }

Another thing? Shadows stack on top of one another, in the order they are declared where the top shadow is the first one in the list.

Two vertically stacked examples showing a white circle with a yellow and a grey circle behind it and the CSS code snippets that create the effect. On the top, the gray shadow is above the yellow shadow. On the bottom, the yellow shadow is above the gray shadow.

You may have guessed that drop-shadow() works a little differently here. Shadows are added exponentially, i.e. 2^number of shadows - 1.

Here’s how that works:

  • 1 shadow = (2^1 – 1). One shadow is rendered.
  • 2 shadows = (2^2 – 1). Three shadows are rendered.
  • 3 shadows = (2^3 – 1). Seven shadows are rendered.

Or, in code:

.one-shadow {   filter: drop-shadow(20px 20px 0 grey); }  .three-shadows {   filter:      drop-shadow(20px 20px 0 grey)     drop-shadow(40px 0 0 yellow); }  .seven-shadows {   filter:      drop-shadow(20px 20px 0 grey)     drop-shadow(40px 0 0 yellow);     drop-shadow(80px 0 0 red); }

The <feDropShadow> element works the exact same way for SVGs.

Shadows and accessibility

Here’s something for you to chew on: shadows can help improve accessibility.

Google conducted a study with low-vision participants to better understand how shadows and outlines impact an individual’s ability to identify and interact with a component. They found that using shadows and outlines:

  • increases the ease and speed of finding a component when scanning pages, and
  • improves one’s ability to determine whether or not a component is interactive.

That wasn’t a wide-ranging scientific study or anything, so let’s turn around and see what the W3C says in it’s guidelines for WCAG 2.0 standards:

[…] the designer might darken the background behind the letter, or add a thin black outline (at least one pixel wide) around the letter in order to keep the contrast ratio between the letter and the background above 4.5:1.

That’s talking about light text on a light background. WCAG recommends a contrast ratio that’s at least 4.5:1 between text and images. You can use text shadows to add a stronger contrast between them.

Photo credit: Woody Van der Straeten

Shadows and performance

Before diving into shadows and adding them on all the things, it’s worth calling out that they do affect performance.

For example, filter: drop-shadow is hardware-accelerated by some browsers. A new compositor layer may be created for that element, and offloaded to the GPU. You don’t want to have too many layers, as it takes up limited GPU memory, and will eventually degrade performance. You can assess this in your browser’s DevTools.

Blurring is an expensive operation, so use it sparingly. When you blur something, it mixes the colors from pixels all around the output pixel to generate a blurred result. For example, if your <blur-radius> parameter is 2px, then the filter needs to look at two pixels in every direction around each output pixel to generate the mixed color. This happens for each output pixel, so that means a lot of calculations that grow exponentially. So, shadows with a large blur radius are generally slower to render than other shadows.

Did you know?

Did you know that shadows don’t influence the document layout?

A shadow is the same size as the element it targets. You can modify the size of a box-shadow (through the spread radius parameter), but other properties cannot modify the shadow size.

And did you know that a shadow implicitly has a lower z-index than elements? That’s why shadows sit below other elements.

And what about clipping and masking? If an element with a box-shadow is clipped (with clip-path) or uses a mask (with mask), the shadow isn’t shown. Conversely, if an element with text-shadow or filter: drop-shadow() is clipped, a shadow is shown, as long as it is within the clip region.

Here’s another: We can’t create oblique shadows (with diagonal lines) with shadow properties. That requires creating a shadow element and use a transform:skew() on it.

Oh, and one more: box-shadow follows border-radius. If an element has rounded corners, the shadow is rounded as well. In other words, the shadow mirrors the shape of the box. On the other hand, filter: drop-shadow() can create an irregular shape because it respects transparency and follows the shape of the content.

Showing two of the same card component side-by-side. They are brightly colored with a background gradient that goes from red to gold. The Nike logo is at the top, a title is below it, then a paragraph of white text beneath that. A red show with an exaggerated shadow is on both cards. The cards illustrated the difference between box shadow, which follows the boundaries of the card's edges, and drop shadow, which includes the shape of the shoe outside the card boundary.

Best use cases for different types of shadows

Practically anything on the web can have a shadow and there are multiple CSS properties and functions that create shadows. But choosing the right type of shadow is what makes a shadow effective.

Let’s evaluate the options:

  • box-shadow: This CSS property creates shadows that conform to the elements bounding box. It’s versatile and can be used on anything from cards to buttons to just about anything where the shadow simply needs to follow the element’s box.
  • text-shadow: This is a CSS property that creates shadows specifically for text elements.
  • filter: drop-shadow(): The CSS property here is filter, but what create the shadow is the drop-shadow function it accepts. What makes this type of shadow different from, say box-shadow, is that it follows the rendered shape of any element (including pseudos).
  • <feDropShadow>: This is actually an SVG element, whereas the rest are CSS properties. So, you would use this to create drop shadows directly in SVG markup.

Once you get the hang of the different types of shadows and each one’s unique shadow-creating powers, the possibilities for shadow effects feels endless. From simple drop shadows to floating elements, and even inner shadows, we can create interesting visuals that add extra meaning or value to UI.

The same goes for text shadows.

Shadows in the wild

Shadows are ubiquitous. We’re seeing them used in new and interesting ways all the time.

Have you heard the buzzword “neumorphism” floating around lately? That’s all about shadows. Here’s an implementation by Maria Muñoz:

Yuan Chuan, who makes amazing generative art, calls shadows a secret weapon in UI design:

CSS relies on existing DOM structure in the browser. It’s not possible to generate new elements other than ::before and ::after. Sometimes I really wish CSS had the ability to do so straightforwardly.

Yet, we can partially make up for this by creating various shadows and gradients entirely in CSS.

That’s why having drop-shadow is so exciting. Together with text-shadow and box-shadow we can do a lot more.

Just check out how he uses drop shadows to create intricate patterns.

Yes, that’s pretty crazy. And speaking of crazy, it’s worth mentioning that going too crazy can result in poor performance, so tread carefully.

What about pseudo-elements?

Oh yes, shadow properties are supported by the ::before and ::after pseudo-elements.

Other pseudos that respect shadows? The ::first-letter pseudo-element accepts box-shadow and text-shadow. The ::first-line pseudo-element accepts text-shadow.

Look at how Jhey Tompkins got all creative using box-shadow on pseudo elements to create animated loaders.

Animating shadows

Yes, we can make them move! The properties and function we’ve covered here are totally compatible with CSS animations and transitions. That means we can move shadows, blur shadows, expand/shrink shadows (with box-shadow), and alter the color.

Animating a shadow can provide a user with a cue that an element is interactive, or that an action has taken place. We saw earlier with our button example that an inset shadow showed that the button had been pressed. Another common animation pattern is elevating a card on hover.

If you want to optimize the animation performance, avoid animating box-shadow! It is more performant to animate drop-shadow(). But if you want the smoothest animation, a hack is the best option! Add an ::after pseudo-element with a bigger box-shadow, and animate its opacity instead.

Of course, there is a lot more you can animate. I will leave that exploration up to you!

Wrapping up

Phew, who knew there was so much to something as seemingly “simple” as CSS shadows! There’s the light source and how shadows are cast. The different types of shadows and their color. There’s using shadows for evoking depth, elevating elements and insetting them. There’s the fact that we can layer shadows on top of other shadows. And that we have a selection of CSS properties that we can use for different use cases. Then, there are the accessibility and performance implications that come with them. And, hey, animation is thing! That’s a heckuva lot!

Anyway, hopefully this broad overview gave you something new to chew on, or at the very least, helped you brush up on some concepts.


The post Getting Deep into Shadows appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Smarter Ways to Generate a Deep Nested HTML Structure

Let’s say we want to have the following HTML structure:

<div class='boo'>   <div class='boo'>     <div class='boo'>       <div class='boo'>         <div class='boo'></div>       </div>     </div>   </div> </div>

That’s real a pain to write manually. And the reason why this post was born was being horrified on seeing it generated with Haml like this:

.boo   .boo     .boo       .boo         .boo

There were actually about twenty levels of nesting in the code I saw, but maybe some people are reading thing on a mobile phone, so let’s not fill the entire viewport with boos, even if Halloween is near.

As you can probably tell, manually writing out every level is far from ideal, especially when the HTML is generated by a preprocessor (or from JavaScript, or even a back-end language like PHP). I’m personally not a fan of deep nesting and I don’t use it much myself, but if you’re going for it anyway, then I think it’s worth doing in a manner that scales well and is easily maintainable.

So let’s first take a look at some better solutions for this base case and variations on it and then see some fun stuff done with this kind of deep nesting!

The base solution

What we need here is a recursive approach. For example, with Haml, the following bit of code does the trick:

- def nest(cls, n); -  return '' unless n > 0; -  "<div class='#{cls}'>#{nest(cls, n - 1)}</div>"; end  = nest('👻', 5)

There’s an emoji class in there because we can and because this is just a fun little example. I definitely wouldn’t use emoji classes on an actual website, but in other situations, I like to have a bit of fun with the code I write.

We can also generate the HTML with Pug:

mixin nest(cls, n)   div(class=cls)     if --n       +nest(cls, n)  +nest('👻', 5)

Then there’s also the JavaScript option:

function nest(_parent, cls, n) {   let _el = document.createElement('div'); 	   if(--n) nest(_el, cls, n);    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

With PHP, we can use something like this:

<?php function nest($  cls, $  n) {   echo "<div class='$  cls'>";   if(--$  n > 0) nest($  cls, $  n);   echo "</div>"; }  nest('👻', 5); ?>

Note that the main difference between what each of these produce is related to formatting and white space. This means that targeting the innermost “boo” with .👻:empty is going to work for the Haml, JavaScript and PHP-generated HTML, but will fail for the Pug-generated one.

Adding level indicators

Let’s say we want each of our boos to have a level indicator as a custom property --i, which could then be used to give each of them a different background, for example.

You may be thinking that, if all we want is to change the hue, then we can do that with filter: hue-rotate() and do without level indicators. However, hue-rotate() doesn’t only affect the hue, but also the saturation and lightness. It also doesn’t provide the same level of control as using our own custom functions that depend on a level indicator, --i.

For example, this is something I used in a recent project in order to make background components smoothly change from level to level (the $ c values are polynomial coefficients):

--sq: calc(var(--i)*var(--i)); /* square */ --cb: calc(var(--sq)*var(--i)); /* cube */ --hue: calc(#{$  ch0} + #{$  ch1}*var(--i) + #{$  ch2}*var(--sq) + #{$  ch3}*var(--cb)); --sat: calc((#{$  cs0} + #{$  cs1}*var(--i) + #{$  cs2}*var(--sq) + #{$  cs3}*var(--cb))*1%); --lum: calc((#{$  cl0} + #{$  cl1}*var(--i) + #{$  cl2}*var(--sq) + #{$  cl3}*var(--cb))*1%);  background: hsl(var(--hue), var(--sat), var(--lum));

Tweaking the Pug to add level indicators looks as follows:

mixin nest(cls, n, i = 0)   div(class=cls style=`--i: $  {i}`)     if ++i < n       +nest(cls, n, i)  +nest('👻', 5)

The Haml version is not too different either:

- def nest(cls, n, i = 0); -   return '' unless i < n; -   "<div class='#{cls}' style='--i: #{i}'>#{nest(cls, n, i + 1)}</div>"; end  = nest('👻', 5)

With JavaScript, we have:

function nest(_parent, cls, n, i = 0) {   let _el = document.createElement('div');    _el.style.setProperty('--i', i); 	   if(++i < n) nest(_el, cls, n, i);    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

And with PHP, the code looks like this:

<?php function nest($  cls, $  n, $  i = 0) {   echo "<div class='$  cls' style='--i: $  i'>";   if(++$  i < $  n) nest($  cls, $  n, $  i);   echo "</div>"; }  nest('👻', 5); ?>

A more tree-like structure

Let’s say we want each of our boos to have two boo children, for a structure that looks like this:

.boo   .boo     .boo       .boo       .boo     .boo       .boo       .boo   .boo     .boo       .boo       .boo     .boo       .boo       .boo

Fortunately, we don’t have to change our base Pug mixin much to get this (demo):

mixin nest(cls, n)   div(class=cls)     if --n       +nest(cls, n)       +nest(cls, n)  +nest('👻', 5)

The same goes for the Haml version:

- def nest(cls, n); -   return '' unless n > 0; -   "<div class='#{cls}'>#{nest(cls, n - 1)}#{nest(cls, n - 1)}</div>"; end  = nest('👻', 5)

The JavaScript version requires a bit more effort, but not too much:

function nest(_parent, cls, n) {   let _el = document.createElement('div');      if(n > 1) {     nest(_el, cls, n);     nest(_el, cls, n)   }    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

With PHP, we only need to call the nest() function once more in the if block:

<?php function nest($  cls, $  n) {   echo "<div class='$  cls'>";   if(--$  n > 0) {     nest($  cls, $  n);     nest($  cls, $  n);   }   echo "</div>"; }  nest('👻', 5); ?>

Styling the top level element differently

We could of course add a special .top (or .root or anything similar) class only for the top level, but I prefer leaving this to the CSS:

:not(.👻) > .👻 {   /* Top-level styles*/ }

Watch out!

Some properties, such as transform, filter, clip-path, mask or opacity don’t only affect an element, but also also all of its descendants. Sometimes this is the desired effect and precisely the reason why nesting these elements is preferred to them being siblings.

However, other times it may not be what we want, and while it is possible to reverse the effects of transform and sometimes even filter, there’s nothing we can do about the others. We cannot, for example, set opacity: 1.25 on an element to compensate for its parent having opacity: .8.

Examples!

First off, we have this pure CSS dot loader I recently made for a CodePen challenge:

Here, the effects of the scaling transforms and of the animated rotations add up on the inner elements, as do the opacities.

Next up is this yin and yang dance, which uses the tree-like structure:

For every item, except the outermost one (:not(.☯️) > .☯️), the diameter is equal to half of that of its parent. For the innermost items (.☯️:empty, which I guess we can call the tree leaves), the background has two extra radial-gradient() layers. And just like the first demo, the effects of the animated rotations add up on the inner elements.

Another example would be these spinning candy tentacles:

Each of the concentric rings represents a level of nesting and combines the effects of the animated rotations from all of its ancestors with its own.

Finally, we have this triangular openings demo (note that it’s using individual transform properties like rotate and scale so the Experimental Web Platform features flag needs to be enabled in chrome://flags in order to see it working in Chromium browsers):

Triangular openings (live demo).

This uses a slightly modified version of the basic nesting mixin in order to also set a color on each level:

- let c = ['#b05574', '#f87e7b', '#fab87f', '#dcd1b4', '#5e9fa3']; - let n = c.length;  mixin nest(cls, n)   div(class=cls style=`color: $  {c[--n]}`)     if n       +nest(cls, n)  body(style=`background: $  {c[0]}`)   +nest('🔺', n)

What gets animated here are the individual transform properties scale and rotate. This is done so that we can set different timing functions for them.


The post Smarter Ways to Generate a Deep Nested HTML Structure appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , ,
[Top]

State of Jamstack 2020: Data Deep Dive

(This is a sponsored post.)

The Jamstack, a modern approach to building websites and apps, delivers better performance, higher security, lower cost of scaling, and a better developer experience. But how popular is it among developers worldwide, and what do they love and hate about it?

We at Kentico Kontent decided to take a closer look at the current state of Jamstackʼs adoption and use. Surveying more than 500 developers in four countries, we wanted to find out how long they’ve been working with the Jamstack, what they’re using this architecture for, where they typically deploy and host their projects, and more.

Based on the findings, we created The State of Jamstack 2020 Report that provides an overview of the results and comments on the most interesting facts. Now we’ve released something just as exciting:

Our free interactive data visualization page lets you dive into the data from our survey and discover how the web developers’ answers varied according to age, gender, primary programming language, and other factors.

Do you know where the majority of US developers working for large companies store their content? Or what’s their favorite static site generator? The answers may surprise you—click below to find out:

Direct Link to ArticlePermalink


The post State of Jamstack 2020: Data Deep Dive appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

Let’s Take a Deep Dive Into the CSS Contain Property

Compared to the past, modern browsers have become really efficient at rendering the tangled web of HTML, CSS, and JavaScript code a typical webpage provides. It takes a mere milliseconds to render the code we give it into something people can use.

What could we, as front-end developers, do to actually help the browser be even faster at rendering? There are the usual best practices that are so easy to forget with our modern tooling — especially in cases where we may not have as much control over generated code. We could keep our CSS under control, for instance, with fewer and simpler selectors. We could keep our HTML under control; keep the tree flatter with fewer nodes, and especially fewer children. We could keep our JavaScript under control; while being careful with our HTML and CSS manipulations.

Actually, modern frameworks such as Vue and React do help out a good bit with that last part.

I would like to explore a CSS property that we could use to help the browser figure out what calculations it can reduce in priority or maybe even skip altogether.

This property is called contain. Here is how MDN defines this property:

The contain CSS property allows an author to indicate that an element and its contents are, as much as possible, independent of the rest of the document tree. This allows the browser to recalculate layout, style, paint, size, or any combination of them for a limited area of the DOM and not the entire page, leading to obvious performance benefits.

A simple way to look at what this property provides is that we can give hints to the browser about the relationships of the various elements on the page. Not necessarily smaller elements, such as paragraphs or links, but larger groups such as sections or articles. Essentially, we’re talking about container elements that hold content — even content that can be dynamic in nature. Think of a typical SPA where dynamic content is being inserted and removed throughout the page, often independent of other content on the page.

A browser cannot predict the future of layout changes to the webpage that can happen from JavaScript inserting and removing content on the page. Even simple things as inserting a class name to an element, animating a DOM element, or just getting the dimensions of an element can cause a reflow and repaint of the page. Such things can be expensive and should be avoided, or at least be reduced as much as possible.

Developers can sort of predict the future because they’ll know about possible future changes based on the UX of the page design, such as when the user clicks on a button it will call for data to be inserted in a div located somewhere in the current view. We know that’s a possibility, but the browser does not. We also know that there’s a distinct possibility that inserting data in that div will not change anything visually, or otherwise, for other elements on the page.

Browser developers have spent a good amount of time optimizing the browser to handle such situations. There are various ways of helping the browser be more efficient in such situations, but more direct hints would be helpful. The contain property gives us a way to provide these hints.

The various ways to contain

The contain property has three values that can be used individually or in combination with one another: size, layout, and paint. It also has two shorthand values for common combinations: strict and content. Let’s cover the basics of each.

Please keep in mind that there are a number of rules and edge cases for each of these that are covered in the spec. I would imagine these will not be of much concern in most situations. Yet, if you get an undesired result, then a quick look at the spec might be handy.

There is also a style containment type in the spec that this article will not cover. The reason being that the style containment type is considered of little value at this time and is currently at-risk of being removed from the spec.

Size containment

size containment is actually a simple one to explain. When a container with this containment is involved in the layout calculations, the browser can skip quite a bit because it ignores the children of that container. It is expected the container will have a set height and width; otherwise, it collapses, and that is the only thing considered in layout of the page. It is treated as if it has no content whatsoever.

Consider that descendants can affect their container in terms of size, depending on the styles of the container. This has to be considered when calculating layout; with size containment, it most likely will not be considered. Once the container’s size has been resolved in relation to the page, then the layout of its descendants will be calculated.

size containment doesn’t really provide much in the way of optimizations. It is usually combined with one of the other values.

Although, one benefit it could provide is helping with JavaScript that alters the descendants of the container based on the size of the container, such as a container query type situation. In some circumstances, altering descendants based on the container’s size can cause the container to change size after the change was done to the descendants. Since a change in the container’s size can trigger another change in the descendants you could end up with a loop of changes. size containment can help prevent that loop.

Here’s a totally contrived example of this resizing loop concept:

In this example, clicking the start button will cause the red box to start growing, based on the size of the purple parent box, plus five pixels. As the purple box adjusts in size, a resize observer tells the red square to again resize based on the size of the parent. This causes the parent to resize again and so on. The code stops this process once the parent gets above 300 pixels to prevent the infinite loop.

The reset button, of course, puts everything back into place.

Clicking the checkbox “set size containment” sets different dimensions and the size containment on the purple box. Now when you click on the start button, the red box will resize itself based on the width of the purple box. True, it overflows the parent, but the point is that it only resizes the one time and stops; there’s no longer a loop.

If you click on the resize container button, the purple box will grow wider. After the delay, the red box will resize itself accordingly. Clicking the button again returns the purple box back to its original size and then the red box will resize again.

While it is possible to accomplish this behavior without use of the containment, you will miss out on the benefits. If this is a situation that can happen often in the page the containment helps out with page layout calculations. When the descendants change internal to the containment, the rest of the page behaves as if the changes never happened.

Layout containment

layout containment tells the browser that external elements neither affect the internal layout of the container element, nor does the internal layout of the container element affect external elements. So when the browser does layout calculations, it can assume that the various elements that have the layout containment won’t affect other elements. This can lower the amount of calculations that need to be done.

Another benefit is that related calculations could be delayed or lowered in priority if the container is off-screen or obscured. An example the spec provides is:

[…] for example, if the containing box is near the end of a block container, and you’re viewing the beginning of the block container

The container with layout containment becomes a containing box for absolute or fixed position descendants. This would be the same as applying a relative position to the container. So, keep that in mind how the container’s descendants may be affected when applying this type of containment.

On a similar note, the container gets a new stacking context, so z-index can be used the same as if a relative, absolute, or fixed position was applied. Although, setting the top, right, bottom, or left properties has no effect on the container.

Here’s a simple example of this:

Click the box and layout containment is toggled. When layout containment is applied, the two purple lines, which are absolute positioned, will shift to inside the purple box. This is because the purple box becomes a containing block with the containment. Another thing to note is that the container is now stacked on top of the green lines. This is because the container now has a new stacking context and follows those rules accordingly.

Paint containment

paint containment tells the browser none of the children of the container will ever be painted outside the boundaries of the container’s box dimensions. This is similar to placing overflow: hidden; on the container, but with a few differences.

For one, the container gets the same treatment as it does under layout containment: it becomes a containing block with its own stacking context. So, having positioned children inside paint containment will respect the container in terms of placement. If we were to duplicate the layout containment demo above but use paint containment instead, the outcome would be much the same. The difference being that the purple lines would not overflow the container when containment is applied, but would be clipped at the container’s border-box.

Another interesting benefit of paint containment is that the browser can skip that element’s descendants in paint calculations if it can detect that the container itself is not visible within the viewport. If the container is not in the viewport or obscured in some way, then it’s a guarantee that its descendants are not visible as well. As an example, think of a nav menu that normally sits off-screen to the left of the page and it slides in when a button is clicked. When that menu is in its normal state off-screen, the browser just skips trying to paint its contents.

Containments working together

These three containments provide different ways of influencing parts of rendering calculations performed by the browser. size containment tells the browser that this container should not cause positional shifting on the page when its contents change. layout containment tells the browser that this container’s descendants should not cause layout changes in elements outside of its container and vice-versa. paint containment tells the browser that this container’s content will never be painted outside of the container’s dimensions and, if the container is obscured, then don’t bother painting the contents at all.

Since each of these provide different optimizations, it would make sense to combine some of them. The spec actually allows for that. For example, we could use layout and paint together as values of the contain property like this:

.el {   contain: layout paint; }

Since this is such an obvious thing to do, the spec actually provides two shorthand values:

Shorthand Longhand
content layout paint
strict layout paint size

The content value will be the most common to use in a web project with a number of dynamic elements, such as large multiple containers that have content changing over time or from user activity.

The strict value would be useful for containers that have a defined size that will never change, even if the content changes. Once in place, it’ll stay the intended size. A simple example of that is a div that contains third-party external advertising content, at industry-defined dimensions, that has no relation to anything else on the page DOM-wise.

Performance benefits

This part of the article is tough to explain. The problem is that there isn’t much in the way of visuals about the performance benefits. Most of the benefits are behind-the-scenes optimizations that help the browser decide what to do on a layout or paint change.

As an attempt to show the contain property’s performance benefits, I made a simple example that changes the font-size on an element with several children. This sort of change would normally trigger a re-layout, which would also lead to a repaint of the page. The example covers the contain values of none, content, and strict.

The radio buttons change the value of the contain property being applied to the purple box in the center. The “change font-size” button will toggle the font-size of the contents of the purple box by switching classes. Unfortunately, this class change is also a potential trigger for re-layout. If you’re curious, here is a list of situations in JavaScript and then a similar list for CSS that trigger such layout and paint calculations. I bet there’s more than you think.

My totally unscientific process was to select the contain type, start a performance recording in Chome’s developer tools, click the button, wait for the font-size change, then stop the recording after another second or so. I did this three times for each containment type to be able to compare multiple recordings. The numbers for this type of comparison are in the low milliseconds each, but there’s enough of a difference to get a feel for the benefits. The numbers could potentially be quite different in a more real-world situation.

But there are a few things to note other than just the raw numbers.

When looking through the recording, I would find the relevant area in the timeline and focus there to select the task that covers the change. Then I would look at the event log of the task to see the details. The logged events were: recalculate style, layout, update layer tree, paint, and composite layers. Adding the times of all those gives us the total time of the task.

DevTools showing set time at 27.9 milliseconds which is the same as the total time to recalculate styles.
The event log with no containment.

One thing to note for the two containment types is that the paint event was logged twice. I’ll get back to that in a moment.

DevTools showing set time at 13.8 milliseconds which is the same as the total time to recalculate styles.

Completing the task at hand

Here are the total times for the three containment types, three runs each:

Containment Run 1 Run 2 Run 3 Average
none 24 ms 33.8 ms 23.3 ms 27.03 ms
content 13.2 ms 9 ms 9.2 ms 10.47 ms
strict 5.6 ms 18.9 ms 8.5 ms 11 ms

The majority of the time was spent in layout. There were spikes here and there throughout the numbers, but remember that these are unscientific anecdotal results. In fact, the second run of strict containment had a much higher result than the other two runs; I just kept it in because such things do happen in the real world. Perhaps the music I was listening to at the time changed songs during that run, who knows. But you can see that the other two runs were much quicker.

So, by these numbers you can start to see that the contain property helps the browser render more efficiently. Now imagine my one small change being multiplied over the many changes made to the DOM and styling of a typical dynamic web page.

Where things get more interesting is in the details of the paint event.

Layout once, paint twice

Stick with me here. I promise it will make sense.

I’m going to use the demo above as the basis for the following descriptions. If you wish to follow along then go to the full version of the demo and open the DevTools. Note that you have to open up the details of the “frame” and not the “main” timeline once you run the performance tool to see what I’m about to describe.

Showing frame details open and main details closed in DevTools.
Showing frame details open and main details closed in DevTools

I’m actually taking screenshots from the “fullpage” version since DevTools works better with that version. That said, the regular “full” version should give roughly the same idea.

The paint event only fired once in the event log for the task that had no containment at all. Typically, the event didn’t take too long, ranging from 0.2 ms to 3.6 ms. The deeper details is where it gets interesting. In those details, it notes that the area of paint was the entire page. In the event log, if you hover on the paint event, DevTools will even highlight the area of the page that was painted. The dimensions in this case will be whatever the size of your browser viewport happens to be. It will also note the layer root of the paint.

Showing DevTools paint calculation of 0.7 milliseconds.
Paint event details

Note that the page area to the left in the image is highlighted, even outside of the purple box. Over to the right, are the dimensions of the paint to the screen. That’s roughly the size of the viewport in this instance. For a future comparison, note the #document as the layer root.

Keep in mind that browsers have the concept of layers for certain elements to help with painting. Layers are usually for elements that may overlap each other due to a new stacking context. An example of this is the way applying position: relative; and z-index: 1; to an element will cause the browser to create that element as a new layer. The contain property has the same effect.

There is a section in DevTools called “rendering” and it provides various tools to see how the browser renders the page. When selecting the checkbox named “Layer borders” we can see different things based on the containment. When the containment is none then you should see no layers beyond the typical static web page layers. Select content or strict and you can see the purple box get converted to its own layer and the rest of the layers for the page shift accordingly.

Layers with no containment
Layers with containment

It may be hard to notice in the image, but after selecting content containment the purple box becomes its own layer and the page has a shift in layers behind the box. Also notice that in the top image the layer line goes across on top of the box, while in the second image the layer line is below the box.

I mentioned before that both content and strict causes the paint to fire twice. This is because two painting processes are done for two different reasons. In my demo the first event is for the purple box and the second is for the contents of the purple box.

Typically the first event will paint the purple box and report the dimensions of that box as part of the event. The box is now its own layer and enjoys the benefits that applies.

The second event is for the contents of the box since they are scrolling elements. As the spec explains; since the stacking context is guaranteed, scrolling elements can be painted into a single GPU layer. The dimensions reported in the second event is taller, the height of the scrolling elements. Possibly even narrower to make room for the scrollbar.

First paint event with content containment
Second paint event with content containment

Note the difference in dimensions on the right of both of those images. Also, the layer root for both of those events is main.change instead of the #document seen above. The purple box is a main element, so only that element was painted as opposed as to whole document. You can see the box being highlighted as opposed to the whole page.

The benefits of this is that normally when scrolling elements come into view, they have to be painted. Scrolling elements in containment have already been painted and don’t require it again when coming into view. So we get some scrolling optimizations as well.

Again, this can be seen in the demo.

Back to that Rendering tab. This time, check “Scrolling performance issue” instead. When the containment is set to none, Chrome covers the purple box with an overlay that’s labeled “repaints on scroll.”

DevTools showing “repaints on scroll” with no containment

If you wish to see this happen live, check the “Paint flashing” option.

Please note: if flashing colors on the screen may present an issue for you in some way, please consider not checking the “Paint flashing” option. In the example I just described, not much changes on the page, but if one were to leave that checked and visited other sites, then reactions may be different.

With paint flashing enabled, you should see a paint indicator covering all the text within the purple box whenever you scroll inside it. Now change the containment to content or strict and then scroll again. After the first initial paint flash it should never reappear, but the scrollbar does show indications of painting while scrolling.

Paint flashing enabled and scrolling with no containment
Paint flashing and scrolling with content containment

Also notice that the “repaints on scroll” overlay is gone on both forms of containment. In this case, containment has given us not only some performance boost in painting but in scrolling as well.

An interesting accidental discovery

As I was experimenting with the demo above and finding out how the paint and scrolling performance aspects worked, I came across an interesting issue. In one test, I had a simple box in the center of page, but with minimal styling. It was essentially an element that scrolls with lots of text content. I was applying content containment to the container element, but I wasn’t seeing the scrolling performance benefits described above.

The container was flagged with the “repaints on scroll” overlay and the paint flashing was the same as no containment applied, even though I knew for a fact that content containment was being applied to the container. So I started comparing my simple test against the more styled version I discussed above.

I eventually saw that if the background-color of the container is transparent, then the containment scroll performance benefits do not happen.

I ran a similar performance test where I would change the font-size of the contents to trigger the re-layout and repaint. Both tests had roughly the same results, with only difference that the first test had a transparent background-color and the second test had a proper background-color. By the numbers, it looks like the behind-the-scenes calculations are still more performant; only the paint events are different. It appears the element doesn’t become its own layer in the paint calculations with a transparent background-color.

The first test run only had one paint event in the event log. The second test run had the two paint events as I would expect. Without that background color, it seems the browser decides to skip the layer aspect of the containment. I even found that faking transparency by using the same color as the color behind the element works as well. My guess is if the container’s background is transparent then it must rely on whatever is underneath, making it impossible to separate the container to its own paint layer.

I made another version of the test demo that changes the background-color of the container element from transparent to the same color used for the background color of the body. Here are two screenshots showing the differences when using the various options in the Rendering panel in DevTools.

Rendering panel with transparent background-color

You can see the checkboxes that have been selected and the result to the container. Even with a content containment applied, the box has “repaints on scroll” as well as the green overlay showing painting while scrolling.

Rendering panel with background-color applied

In the second image, you can see that the same checkboxes are selected and a different result to the container. The “repaints on scroll” overlay is gone and the green overlay for painting is also gone. You can see the paint overlay on the scrollbar to show it was active.

Conclusion: make sure to apply some form of background color to your container when applying containment to get all the benefits.

Here’s what I used for the test:

This is the bottom of the page

This article has covered the basics of the CSS contain property with its values, benefits, and potential performance gains. There are some excellent benefits to applying this property to certain elements in HTML; which elements need this applied is up to you. At least, that’s what I gather since I’m unaware of any specific guidance. The general idea is apply it to elements that are containers of other elements, especially those with some form of dynamic aspect to them.

Some possible scenarios: grid areas of a CSS grid, elements containing third-party content, and containers that have dynamic content based on user interaction. There shouldn’t be any harm in using the property in these cases, assuming you aren’t trying to contain an element that does, in fact, rely in some way on another element outside that containment.

Browser support is very strong. Safari is the only holdout at this point. You can still use the property regardless because the browser simply skips over that code without error if it doesn’t understand the property or its value.

So, feel free to start containing your stuff!

The post Let’s Take a Deep Dive Into the CSS Contain Property appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Don’t comma-separate :focus-within if you need deep browser support

I really like :focus-within. It’s a super useful selector that allows you to essentially select a parent element when any of its children are in focus.

Say you wanted to reveal some extra stuff when a <div> is hovered…

div:hover {   .extra-stuff {      /* reveal it */   } }

That’s not particularly keyboard-friendly. But if something in .extra-stuff is tab-able anyway (meaning it can be focused), that means you could write it like this to make it a bit more accessible:

div:hover, div:focus-within {   .extra-stuff {      /* reveal it */   } }

That’s nice, but it causes a tricky problem.

Browsers ignore entire selectors if it doesn’t understand any part of them. So, if you’re dealing with a browser that doesn’t support :focus-within then it would ignore the CSS example above, meaning you’ve also lost the :hover state.

Instead:

div:hover {   .extra-stuff {      /* reveal it */   } } div:focus-within {   .extra-stuff {      /* reveal it */   } }

That is safer. But it’s repetitive. If you have a preprocessor like Sass…

@mixin reveal {   .extra-stuff {      transform: translateY(0);   } } div:hover {   @include reveal; } div:focus-within {   @include reveal; }

See the Pen
Mixing for :focus-within without comma-separating
by Chris Coyier (@chriscoyier)
on CodePen.

I’d say it’s a pretty good use-case for having native CSS mixins.

The post Don’t comma-separate :focus-within if you need deep browser support appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Color Inputs: A Deep Dive into Cross-Browser Differences

In this article, we’ll be taking a look at the structure inside <input type='color'> elements, browser inconsistencies, why they look a certain way in a certain browser, and how to dig into it. Having a good understanding of this input allows us to evaluate whether a certain cross-browser look can be achieved and how to do so with a minimum amount of effort and code.

Here’s exactly what we’re talking about:

But before we dive into this, we need to get into…

Accessibility issues!

We’ve got a huge problem here: for those who completely rely on a keyboard, this input doesn’t work as it should in Safari and in Firefox on Windows, but it does work in Firefox on Mac and Linux (which I only tested on Fedora, so feel free to yell at me in the comments if it doesn’t work for you using another distribution).

In Firefox on Windows, we can Tab to the input to focus it, press Enter to bring up a dialog… which we then cannot navigate with the keyboard!

I’ve tried tabbing, arrow keys, and every other key available on the keyboard… nothing! I could at least close the dialog with good old Alt + F4. Later, in the bug ticket I found for this on Bugzilla, I also discovered a workaround: Alt + Tab to another window, then Alt + Tab back and the picker dialog can be navigated with the keyboard.

Things are even worse in Safari. The input isn’t even focusable (bug ticket) if VoiceOver isn’t on. And even when using VoiceOver, tabbing through the dialog the inputs opens is impossible.

If you’d like to use <input type='color'> on an actual website, please let browsers know this is something that needs to be solved!

How to look inside

In Chrome, we need to bring up DevTools, go to Settings and, in the Preferences section under Elements, check the Show user agent shadow DOM option.

How to view the structure inside an input in Chrome.

Then, when we return to inspect our element, we can see inside its shadow DOM.

In Firefox, we need to go to about:config and ensure the devtools.inspector.showAllAnonymousContent flag is set to true.

How to view the structure inside an input in Firefox.

Then, we close the DevTools and, when we inspect our input again, we can see inside our input.

Sadly, we don’t seem to have an option for this in pre-Chromium Edge.

The structure inside

The structure revealed in DevTools differs from browser to browser, just like it does for range inputs.

In Chrome, at the top of the shadow DOM, we have a <div> wrapper that we can access using ::-webkit-color-swatch-wrapper.

Inside it, we have another <div> we can access with ::-webkit-color-swatch.

Screenshot of Chrome DevTools showing the shadow DOM of the <input type='color'>. Right at the top, we have a div which is the swatch wrapper and can be accessed using ::-webkit-color-swatch-wrapper. Inside it, there’s another div which is the swatch and can be accessed using ::-webkit-color-swatch. This div has the background-color set to the value of the parent color input.”/><figcaption>Inner structure in Chrome.</figcaption></figure>
<p>In Firefox, we only see one <code><div></code>, but it’s not labeled in any way, so how do we access it?</p>
<p>On a hunch, given this <code><div></code> has the <code>background-color</code> set to the input’s <code>value</code> attribute, just like the <code>::-webkit-color-swatch</code> component, I tried <code>::-moz-color-swatch</code>. And it turns out it works!</p>
<figure><img src=this issue from 2016. Pre-Chromium Edge apparently doesn’t allow us to style whatever is inside this input. Well, that’s a bummer.

How to look at the browser styles

In all browsers, we have the option of not applying any styles of our own and then looking at the computed styles.

In Chrome and Firefox, we can also see the user agent stylesheet rule sets that are affecting the currently selected element (though we need to explicitly enable this in Firefox, as seen in the previous section).

Screenshot collage of Chrome DevTools and Firefox DevTools showing where to look for user agent styles: Elements > Styles in Chrome and Inspector > Styles in Firefox.”/><figcaption>Checking browser styles in Chrome and Firefox.</figcaption></figure>
<p>This is oftentimes more helpful than the computed styles, but there are exceptions and we should still always check the computed values as well.</p>
<p>In Firefox, we can also see the CSS file for the <code>form</code> elements at <code>view-source:resource://gre-resources/forms.css</code>.</p>
<figure><img src=
Checking browser styles in Firefox.

The input element itself

We’ll now be taking a look at the default values of a few properties in various browsers in order to get a clear picture of what we’d really need to set explicitly in order to get a custom cross-browser result.

The first property I always think about checking when it comes to <input> elements is box-sizing. The initial value of this property is border-box in Firefox, but content-box in Chrome and Edge.

Comparative screenshots of DevTools in the three browsers showing the computed values of box-sizing for the actual input.
The box-sizing values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

We can see that Firefox is setting it to border-box on <input type='color'>, but it looks like Chrome isn’t setting it at all, so it’s left with the initial value of content-box (and I suspect the same is true for Edge).

In any event, what it all means is that, if we are to have a border or a padding on this element, we also need to explicitly set box-sizing so that we get a consistent result across all these browsers.

The font property value is different for every browser, but since we don’t have text inside this input, all we really care about is the font-size, which is consistent across all browsers I’ve checked: 13.33(33)px. This is a value that really looks like it came from dividing 40px by 3, at least in Chrome.

Comparative screenshots of DevTools in the three browsers showing the font values for the actual input.
The font values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

This is a situation where the computed styles are more useful for Firefox, because if we look at the browser styles, we don’t get much in terms of useful information:

Screenshot of what we get if we look at the browser styles where the font was set for Firefox. The value for the font is -moz-field, which is an alias for the look of a native text field. Expanding this to check the longhands shows us empty instead of actual values.
Sometimes the browser styles are pretty much useless (Firefox screenshot).

The margin is also consistent across all these browsers, computing to 0.

Comparative screenshots of DevTools in the three browsers showing the margin values for the actual input.
The margin values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

The border is different for every single browser. In both Chrome and Edge, we have a solid 1px one, but the border-color is different (rgb(169, 169, 169) for Chrome and rgb(112, 112, 112) for Edge). In Firefox, the border is an outset 2px one, with a border-color of… ThreeDLightShadow?!

Comparative screenshots of DevTools in the three browsers showing the border values for the actual input.
The border values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

What’s the deal with ThreeDLightShadow? If it doesn’t sound familiar, don’t worry! It’s a (now deprecated) CSS2 system value, which Firefox on Windows shows me to be rgb(227, 227, 227) in the Computed styles tab.

Screenshot of Computed panel search in Firefox on Windows, showing that the ThreeDLightShadow keyword computes to rgb(227, 227, 227).
Computed border-color for <input type='color'> in Firefox on Windows.

Note that in Firefox (at least on Windows), the operating system zoom level (SettingsSystemDisplayScale and LayoutChange the size of text, apps and other items) is going to influence the computed value of the border-width, even though this doesn’t seem to happen for any other property I’ve checked and it seems to be partially related to the border-style.

Screenshot showing the Windows display settings window with the zoom level options dropdown opened.
Zoom level options on Windows.

The strangest thing is the computed border-width values for various zoom levels don’t seem to make any sense. If we keep the initial border-style: outset, we have:

  • 1.6px for 125%
  • 2px for 150%
  • 1.7px for 175%
  • 1.5px for 200%
  • 1.8px for 225%
  • 1.6px for 250%
  • 1.66667px for 300%

If we set border-style: solid, we have a computed border-width of 2px, exactly as it was set, for zoom values that are multiples of 50% and the exact same computed values as for border-style: outset for all the other zoom levels.

The padding is the same for Chrome and Edge (1px 2px), while Firefox is the odd one out again.

Comparative screenshots of DevTools in the three browsers showing the padding values for the actual input.
The padding values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

It may look like the Firefox padding is 1px. That’s what it is set to and there’s no indication of anything overriding it — if a property is overridden, then it’s shown as grey and with a strike-through.

Screenshot of Firefox DevTools highlighting how the border set on input[type='color'] overrides the one set on input and the look (grey + strike-through) of overridden properties.
Spotting overrides in Firefox.

But the computed value is actually 0 8px! Moreover, this is a value that doesn’t depend on the operating system zoom level. So, what the hairy heck is going on?!

Screenshot of Firefox DevTools showing how the computed padding value on <input type='color'> isn’t the one that was set on input, even if no override seems to be happening.”/><figcaption>Computed value for <code>padding</code> in Firefox doesn’t match the value that was set on input.</figcaption></figure>
<p>Now, if you’ve actually tried inspecting a color input, took a close look at the styles set on it, and your brain works differently than mine (meaning you do read what’s in front of you and don’t just scan for the one thing that interests you, completely ignoring everything else…) then you’ve probably noticed there is something overriding the <code>1px</code> padding (and <a href=should be marked as such) — the flow-relative padding!

Screenshot of Firefox DevTools showing the flow-relative padding overriding the old padding due to higher specificity of selector (input[type='color'] vs. input).
Flow-relative padding overrides in Firefox.

Dang, who knew those properties with lots of letters were actually relevant? Thanks to Zoltan for noticing and letting me know. Otherwise, it probably would have taken me two more days to figure this one out.

This raises the question of whether the same kind of override couldn’t happen in other browsers and/or for other properties.

Edge doesn’t support CSS logical properties, so the answer is a “no” in that corner.

In Chrome, none of the logical properties for margin, border or padding are set explicitly for <input type='color'>, so we have no override.

Concerning other properties in Firefox, we could have found ourselves in the same situation for margin or for border, but with these two, it just so happens the flow-relative properties haven’t been explicitly set for our input, so again, there’s no override.

Even so, it’s definitely something to watch out for in the future!

Moving on to dimensions, our input’s width is 44px in Chrome and Edge and 64px in Firefox.

Comparative screenshots of DevTools in the three browsers showing the width values for the actual input.
The width values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

Its height is 23px in all three browsers.

Comparative screenshots of DevTools in the three browsers showing the height values for the actual input.
The height values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

Note that, since Chrome and Edge have a box-sizing of content-box, their width and height values do not include the padding or border. However, since Firefox has box-sizing set to border-box, its dimensions include the padding and border.

Comparative screenshots of DevTools in the three browsers showing the layout boxes.
The layout boxes for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

This means the content-box is 44pxx23px in Chrome and Edge and 44xpxx19px in Firefox, the padding-box is 48pxx25 in Chrome and Edge and 60pxx19px in Firefox and the border-box is 50pxx27px in Chrome and Edge and 64pxx23 in Firefox.

We can clearly see how the dimensions were set in Chrome and I’d assume they were set in the same direct way in Edge as well, even if Edge doesn’t allow us to trace this stuff. Firefox doesn’t show these dimensions as having been explicitly set and doesn’t even allow us to trace where they came from in the Computed tab (as it does for other properties like border, for example). But if we look at all the styles that have been set on input[type='color'], we discover the dimensions have been set as flow-relative ones (inline-size and block-size).

Screenshot of the Firefox user agent styles showing flow relative dimensions being set on input[type='color'].
How <input type='color'> dimensions have been set in Firefox.

The final property we check for the normal state of the actual input is background. Here, Edge is the only browser to have a background-image (set to a top to bottom gradient), while Chrome and Firefox both have a background-color set to ButtonFace (another deprecated CSS2 system value). The strange thing is this should be rgb(240, 240, 240) (according to this resource), but its computed value in Chrome is rgb(221, 221, 221).

Comparative screenshots of DevTools in the three browsers showing the background values for the actual input.
The background values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

What’s even stranger is that, if we actually look at our input in Chrome, it sure does look like it has a gradient background! If we screenshot it and then use a picker, we get that it has a top to bottom gradient from #f8f8f8 to #ddd.

Screenshot of the input in Chrome. A very light, almost white, grey to another light, but still darker grey gradient from top to bottom can be seen as the background, not the solid background-color indicated by DevTools.
What the actual input looks like in Chrome. It appears to have a gradient, in spite of the info we get from DevTools telling us it doesn’t.

Also, note that changing just the background-color (or another property not related to dimensions like border-radius) in Edge also changes the background-image, background-origin, border-color or border-style.

Animated gif. Shows the background-image, background-origin, border-color, border-style before and after changing the seemingly unrelated background-color - their values don't get preserved following this change.
Edge: side-effects of changing background-color.

Other states

We can take a look at the styles applied for a bunch of other states of an element by clicking the :hov button in the Styles panel for Chrome and Firefox and the a: button in the same Styles panel for Edge. This reveals a section where we can check the desired state(s).

Screenshot collage highlighting the buttons that bring up the states panel in Chrome, Firefox and Edge.
Taking a look at other states in Chrome, Firefox, Edge (from top to bottom).

Note that, in Firefox, checking a class only visually applies the user styles on the selected element, not the browser styles. So, if we check :hover for example, we won’t see the :hover styles applied on our element. We can however see the user agent styles matching the selected state for our selected element shown in DevTools.

Also, we cannot test for all states like this and let’s start with such a state.

:disabled

In order to see how styles change in this state, we need to manually add the disabled attribute to our <input type='color'> element.

Hmm… not much changes in any browser!

In Chrome, we see the background-color is slightly different (rgb(235, 235, 228) in the :disabled state versus rgb(221, 221, 221) in the normal state).

Chrome DevTools screenshot showing the background being set to rgb(235, 235, 228) for a :disabled input.
Chrome :disabled styling.

But the difference is only clear looking at the info in DevTools. Visually, I can tell tell there’s a slight difference between an input that’s :disabled and one that’s not if they’re side-by-side, but if I didn’t know beforehand, I couldn’t tell which is which just by looking at them, and if I just saw one, I couldn’t tell whether it’s enabled or not without clicking it.

Disabled and enabled input side by side in Chrome. There is a slight difference in background-color, but it's pretty much impossible to tell which is which just by looking at them.
Disabled (left) versus enabled (right) <input type='color'> in Chrome.

In Firefox, we have the exact same values set for the :disabled state as for the normal state (well, except for the cursor, which realistically, isn’t going to produce different results save for exceptional cases anyway). What gives, Firefox?!

Comparison of styles set in Firefox for <input type='color'> in its normal state and its :disabled state. The padding and border set in the :disabled case are exactly the same as those set in the normal case.”/><figcaption>Firefox <code>:disabled</code> (top) versus normal (bottom) styling.</figcaption></figure>
<p>In Edge, both the <code>border-color</code> and the <code>background</code> gradient are different.</p>
<figure><img src=
Edge :disabled styling (by checking computed styles).

We have the following styles for the normal state:

border-color: rgb(112, 112, 112); background-image: linear-gradient(rgb(236, 236, 236), rgb(213, 213, 213));

And for the :disabled state:

border-color: rgb(186, 186, 186); background-image: linear-gradient(rgb(237, 237, 237), rgb(229, 229, 229));

Clearly different if we look at the code and visually better than Chrome, though it still may not be quite enough:

Disabled and enabled input side by side in Edge. There is a slight difference in background-image and a bigger difference in border-color, but it still may be difficult to tell whether an input is enabled or not at first sight without having a reference to compare.
Disabled (left) versus enabled (right) <input type='color'> in Edge.
:focus

This is one state we can test by toggling the DevTools pseudo-classes. Well, in theory. In practice, it doesn’t really help us in all browsers.

Starting with Chrome, we can see that we have an outline in this state and the outline-color computes to rgb(77, 144, 254), which is some kind of blue.

Chrome DevTools screenshot showing an outline for an input having <code>:focus</code>.”/><figcaption>Chrome <code>:focus</code> styling.</figcaption></figure>
<p>Pretty straightforward and easy to spot.</p>
<p>Moving on to Firefox, things start to get hairy! Unlike Chrome, toggling the <code>:focus</code> pseudo-class from DevTools does nothing on the input element, though by focusing it (by tab click), the <code>border</code> becomes blue and we get a <code>dotted</code> rectangle within — but there’s no indication in DevTools regarding what is happening.</p>
<figure><img src=
What happens in Firefox when tabbing to our input to :focus it.

If we check Firefox’s forms.css, it provides an explanation for the dotted rectangle. This is the dotted border of a pseudo-element, ::-moz-focus-inner (a pseudo-element which, for some reason, isn’t shown in DevTools inside our input as ::-moz-color-swatch is). This border is initially transparent and then becomes visible when the input is focused — the pseudo-class used here (:-moz-focusring) is pretty much an old Firefox version of the new standard (:focus-visible), which is currently only supported by Chrome behind the Experimental Web Platform features flag.

Firefox DevTools screenshot where the inner dotted rectangle on :focus comes from: it is set as a transparent border on the ::-moz-focus-inner pseudo-element and it becomes visible when the input should have a noticeable :focus indicator.
Firefox: where the inner dotted rectangle on :focus comes from.

What about the blue border? Well, it appears this one isn’t set by a stylesheet, but at an OS level instead. The good news is we can override all these styles should we choose to do so.

In Edge, we’re faced with a similar situation. Nothing happens when toggling the :focus pseudo-class from DevTools, but if we actually tab to our input to focus it, we can see an inner dotted rectangle.

Animated gif. Shows how, on :focus, our input gets an inner dotted rectangle.
What happens in Edge when tabbing to our input to :focus it.

Even though I have no way of knowing for sure, I suspect that, just like in Firefox, this inner rectangle is due to a pseudo-element that becomes visible on :focus.

:hover

In Chrome, toggling this pseudo-class doesn’t reveal any :hover-specific styles in DevTools. Furthermore, actually hovering the input doesn’t appear to change anything visually. So it looks like Chrome really doesn’t have any :hover-specific styles?

In Firefox, toggling the :hover pseudo-class from DevTools reveals a new rule in the styles panel:

Screenshot of Firefox DevTools showing the rule set that shows up for the :hover state.
Firefox :hover styling as seen in DevTools.

When actually hovering the input, we see the background turns light blue and the border blue, so the first thought would be that light blue is the -moz-buttonhoverface value and that the blue border is again set at an OS level, just like in the :focus case.

Animated gif. Shows that, on actually hovering our <input type='color'>, it gets a light blue background and a blue border.”/><figcaption>What actually happens in Firefox on <code>:hover</code>.</figcaption></figure>
<p>However, if we look at the computed styles, we see the same <code>background</code> we have in the normal state, so that blue <code>background</code> is probably really set at an OS level as well, in spite of having that rule in the <code>forms.css</code> stylesheet.</p>
<figure><img src=
Firefox: computed background-color of an <input type='color'> on :hover.

In Edge, toggling the :hover pseudo-class from DevTools gives our input a light blue (rgb(166, 244, 255)) background and a blue (rgb(38, 160, 218)) border, whose exact values we can find in the Computed tab:

Screenshot of Edge DevTools showing the computed value for background-color and border-color in the :hover state.
Edge: computed background-color and border-color of an <input type='color'> on :hover.
:active

Checking the :active state in the Chrome DevTools does nothing visually and shows no specific rules in the Styles panel. However, if we actually click our input, we see that the background gradient that doesn’t even show up in DevTools in the normal state gets reversed.

Screenshot of the input in :active state in Chrome. A very light, almost white, grey to another light, but still darker grey gradient from bottom to top can be seen as the background, not the solid background-color indicated by DevTools.
What the actual input looks like in Chrome in the :active state. It appears to have a gradient (reversed from the normal state), in spite of the info we get from DevTools telling us it doesn’t.

In Firefox DevTools, toggling the :active state on does nothing, but if we also toggle the :hover state on, then we get a rule set that changes the inline padding (the block padding is set to the same value of 0 it has in all other states), the border-style and sets the background-color back to our old friend ButtonFace.

Screenshot of Firefox DevTools showing the rule set that shows up for the :active state.
Firefox :active styling as seen in DevTools.

In practice, however, the only thing that matches the info we get from DevTools is the inline shift given by the change in logical padding. The background becomes a lighter blue than the :hover state and the border is blue. Both of these changes are probably happening at an OS level as well.

Animated gif. Shows that, on actually clicking our <input type='color'>, it gets a light blue background and a blue border in addition to sliding 1 pixel in the inline direction as a result of changing the inline padding.”/><figcaption>What actually happens in Firefox in an <code>:active</code> state.</figcaption></figure>
<p>In Edge, activating the <code>:active</code> class from DevTools gives us the exact same styles we have for the <code>:hover</code> state. However, if we have both the <code>:hover</code> and the <code>:active</code> states on, things change a bit. We still have a light blue <code>background</code> and a blue <code>border</code>, but both are darker now (<code>rgb(52, 180, 227)</code> for the <code>background-color</code> and <code>rgb(0, 137, 180)</code> for the <code>border-color</code>):</p>
<figure><img src=
The computed background-color and border-color of an <input type='color'> on :active viewed in Edge.

This is the takeaway: if we want a consistent cross-browser results for <input type='color'>, we should define our own clearly distinguishable styles for all these states ourselves because, fortunately, almost all the browser defaults — except for the inner rectangle we get in Edge on :focus — can be overridden.

The swatch wrapper

This is a component we only see in Chrome, so if we want a cross-browser result, we should probably ensure it doesn’t affect the swatch inside — this means ensuring it has no margin, border, padding or background and that its dimensions equal those of the actual input’s content-box.

In order to know whether we need to mess with these properties (and maybe others as a result) or not, let’s see what the browser defaults are for them.

Fortunately, we have no margin or border, so we don’t need to worry about these.

Chrome DevTools screenshot showing the margin and border values for the swatch wrapper.
The margin and border values for the swatch wrapper in Chrome.

We do however have a non-zero padding (of 4px 2px), so this is something we’ll need to zero out if we want to achieve a consistent cross-browser result.

Chrome DevTools screenshot showing the padding values for the swatch wrapper.
The padding values for the swatch wrapper in Chrome.

The dimensions are both conveniently set to 100%, which means we won’t need to mess with them.

Chrome DevTools screenshot showing the size values for the swatch wrapper.
The size values for the swatch wrapper in Chrome.

Something we need to note here is that we have box-sizing set to border-box, so the padding gets subtracted from the dimensions set on this wrapper.

Chrome DevTools screenshot showing the <code>box-sizing</code> value for the swatch wrapper.”/><figcaption>The <code>box-sizing</code> value for the swatch wrapper in Chrome.</figcaption></figure>
<p>This means that while the <code>padding-box</code>, <code>border-box</code> and <code>margin-box</code> of our wrapper (all equal because we have no <code>margin</code> or <code>border</code>) are identical to the <code>content-box</code> of the actual <code><input type='color'></code> (which is <code>44px</code>x<code>23px</code> in Chrome), getting the wrapper’s <code>content-box</code> involves subtracting the <code>padding</code> from these dimensions. It results that this box is <code>40px</code>x<code>15px</code>.</p>
<figure><img src=
The box model for the swatch wrapper in Chrome.

The background is set to transparent, so that’s another property we don’t need to worry about resetting.

Chrome DevTools screenshot showing the background values for the swatch wrapper.
The background values for the swatch wrapper in Chrome.

There’s one more property set on this element that caught my attention: display. It has a value of flex, which means its children are flex items.

Chrome DevTools screenshot showing the display value for the swatch wrapper.
The display value for the swatch wrapper in Chrome.

The swatch

This is a component we can style in Chrome and Firefox. Sadly, Edge doesn’t expose it to allow us to style it, so we cannot change properties we might want to, such as border, border-radius or box-shadow.

The box-sizing property is one we need to set explicitly if we plan on giving the swatch a border or a padding because its value is content-box in Chrome, but border-box in Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of box-sizing for the swatch component.
The box-sizing values for the swatch viewed in Chrome (top) and Firefox (bottom).

Fortunately, the font-size is inherited from the input itself so it’s the same.

Comparative screenshots of DevTools in the two browsers showing the computed values of font-size for the swatch component.
The font-size values for the swatch viewed in Chrome (top) and Firefox (bottom).

The margin computes to 0 in both Chrome and Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of margin for the swatch component.
The margin values for the swatch viewed in Chrome (top) and Firefox (bottom).

This is because most margins haven’t been set, so they end up being 0 which is the default for <div> elements. However, Firefox is setting the inline margins to auto and we’ll be getting to why that computes to 0 in just a little moment.

Screenshot of Firefox DevTools.
The inline margin for the swatch being set to auto in Firefox.

The border is solid 1px in both browsers. The only thing that differs is the border-color, which is rgb(119, 119, 119) in Chrome and grey (or rgb(128, 128, 128), so slightly lighter) in Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of border for the swatch component.
The border values for the swatch viewed in Chrome (top) and Firefox (bottom).

Note that the computed border-width in Firefox (at least on Windows) depends on the OS zoom level, just as it is in the case of the actual input.

The padding is luckily 0 in both Chrome and Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of padding for the swatch component.
The padding values for the swatch viewed in Chrome (top) and Firefox (bottom).

The dimensions end up being exactly what we’d expect to find, assuming the swatch covers its parent’s entire content-box.

Comparative screenshots of DevTools in the two browsers showing the box model for the swatch component.
The box model for the swatch viewed in Chrome (top) and Firefox (bottom).

In Chrome, the swatch parent is the <div> wrapper we saw earlier, whose content-box is 4pxx15px. This is equal to the margin-box and the border-box of the swatch (which coincide as we have no margin). Since the padding is 0, the content-box and the padding-box for the swatch are identical and, subtracting the 1px border, we get dimensions that are 38pxx13px.

In Firefox, the swatch parent is the actual input, whose content-box is 44pxx19px one. This is equal to the margin-box and the border-box of the swatch (which coincide as we have no margin). Since the padding is 0, the content-box and the padding-box for the swatch are identical and, subtracting the 1px border, we get that their dimensions are 42pxx17px.

In Firefox, we see that the swatch is made to cover its parent’s content-box by having both its dimensions set to 100%.

Comparative screenshots of DevTools in the two browsers showing the size values for the swatch component.
The size values for the swatch viewed in Chrome (top) and Firefox (bottom).

This is the reason why the auto value for the inline margin computes to 0.

But what about Chrome? We cannot see any actual dimensions being set. Well, this result is due to the flex layout and the fact that the swatch is a flex item that’s made to stretch such that it covers its parent’s content-box.

Chrome DevTools screenshot showing the flex value for the swatch wrapper.
The flex value for the swatch wrapper in Chrome.

Final thoughts

Phew, we covered a lot of ground here! While it may seem exhaustive to dig this deep into one specific element, this is the sort of exercise that illustrates how difficult cross-browser support can be. We have our own styles, user agent styles and operating system styles to traverse and some of those are always going to be what they are. But, as we discussed at the very top, this winds up being an accessibility issue at the end of the day, and something to really consider when it comes to implementing a practical, functional application of a color input.

Remember, a lot of this is ripe territory to reach out to browser vendors and let them know how they can update their implementations based on your reported use cases. Here are the three tickets I mentioned earlier where you can either chime in or reference to create a new ticket:

The post Color Inputs: A Deep Dive into Cross-Browser Differences appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]