Tag: Look

First Look At The CSS object-view-box Property

Ahmad Shadeed — doing what he always does so well — provides an early look at the object-view-box property, something he describes as a native way to crop an image in the browser with CSS.

The use case? Well, Ahmad wastes no time showing how to use the property to accomplish what used to require either (1) a wrapping element with hidden overflow around an image that’s sized and positioned inside that element or (2) the background-image route.

But with object-view-box we can essentially draw the image boundaries as we can with an SVG’s viewbox. So, take a plain ol’ <img> and call on object-view-box to trim the edges using an inset function. I’ll simply drop Ahmad’s pen in here:

Only supported in Chrome Canary for now, I’m afraid. But it’s (currently) planned to release in Chrome 104. Elsewhere:

To Shared LinkPermalink on CSS-Tricks

First Look At The CSS object-view-box Property originally published on CSS-Tricks. You should get the newsletter.


, , ,

A Look at the Cloudinary WordPress Plugin

(This is a sponsored post.)

Cloudinary (the media hosting and optimization service) has a brand new version (v3) of its WordPress plugin that has really nailed it. First, a high-level look at the biggest things this plugin does:

  • It takes over your media handling. Images and video are served by Cloudinary instead of your own server, which is good for a whole host of reasons. But don’t worry, your assets are also on your own server, so there is no lock-in.
  • It serves your images and video as performantly as possible. Everything is optimized, served in the best format, and use techniques like responsive images and lazy loading, all while providing a good loading experience. All together, those things are massive for performance.
  • It provides and image gallery block with lots of functionality.

Setting it up is as easy as copy and pasting from your Cloudinary account.

So, yes, you need a Cloudinary account. You can check out the programmable media plans here. There is a free tier that will likely work for many sites and paid plans that will cover sites that have heavy media needs, of which you’ll likely find the pricing amicable. Once you have your account, you pop the connection string (from your dashboard) into this quick onboarding wizard and you’re basically done. The default settings are good.

You could do literally nothing else and the plugin will work its magic, but it’s fun to look through all the settings.

Here are the general settings:

Those two (default) settings are important. Auto sync is nice in that all your images (even your entire existing media library) is synced up to Cloudinary and stays in sync. This is necessary to host your images (and do fancy optional stuff like “transforms”) but you could otherwise think of it as a backup. When you use “Cloudinary and WordPress” as the Storage setting, it means that media will be uploaded to your own server and Cloudinary. That’s what I would highly recommend, but if you’re in a situation where, say, you have very limited or no storage on your WordPress host, you could have the images go straight to Cloudinary (only).

In the Image settings, you can see two of Cloudinary’s most powerful weapons: f_auto and q_auto, standing for “auto image formatting” and “auto quality compression.” Those are defaults I’d highly recommend leaving alone. It means that any browser on any device gets the best possible format of the image, and Cloudinary adjusts the quality as appropriate for that image. Cloudinary has very good tech for doing this, so let it do it.

The “doing images right” checklist is a thing.

Remember, we blogged it just recently. Host them on a CDN. Optimze them. Serve them in the best possible format for the requesting browser. Use responsive images. Lazy load them. None of those things are trivial, and that’s just a partial list. The good news is: this plugin does all that stuff for you, does it well, and does it without you having to think too much about it.

Showing the source HTML code for an image block. It's a lot of code, starting with the image tag and all of the srcset attributes to make the image responsive.

I like seeing the output. This is where the rubber meets the road. From this I can see that responsive images are implemented correctly and lots of different sizes are available. I can see the the image sources are pointing at the Cloudinary CDN. I can see lazy loading is implemented and working. I can see the width and height attributes are there as they should be to ensure space is reserved for the images during loading. This is everything.

It goes the extra mile by hosting the images the used by the theme as well.

Heck, it replaces CSS background-images in your theme’s stylesheet with Cloudinary-hosted versions. That’s… amazing. There must be some real clever WordPress filter stuff going on.

I like seeing this in there:

Screenshot of a Cloudinary screen in the WordPress admin. It provides settings for the Gallery Block, including colors, main viewer options, carousel, and a toggle for advanced settings. A preview of the block is to the right of the settings.

Why? It shows that this plugin is part of modern WordPress. Block editor WordPress. The block itself is simple, but useful. It shows images in a variety of useful layouts with a “lightbox”-like effect (wow, it’s been a long time since I’ve typed the word lightbox). Hey, sometimes you just need a dang image gallery and you might as well use one that is well done.

Who am I to say?

Just a lowly blogger, I suppose. But I can tell you I’ve been watching this evolve for quite a while. A ways back, I had implemented a hand-rolled Cloudinary integration here on CSS-Tricks because I wanted all this stuff. I ultimately had to give up on it as it was more technical debt than I could maintain.

The previous versions of the WordPress plugin were better, but it’s not until now, v3, where this integration is truly nailed.

Shortly after that time I tore down my custom integration, I blogged “Workflow Considerations for Using an Image Management Service” and outlined what I thought the (rather high) bar would be for integrating a third-party image host. It was a lot to ask, and I wasn’t really sure if anyone would find the incentive and motivation to do it all. Well, Cloudinary has done it here. This is as perfect a media management plugin as I could imagine.

The post A Look at the Cloudinary WordPress Plugin appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , ,

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.


, , , , , ,

A Look at Building with Astro

Astro is a brand new framework for building websites. To me, the big thing is that it allows you to build a site like you’re using a JavaScript framework (and you are), but the output is a zero-JavaScript static site. You can opt-in to client-side JavaScript as needed, and there are clever options for doing so. Notably, the learning curve is somewhat flattened by the fact that it supports componentry you may already know: React/Preact (JSX), Svelte, Vue, or web components.

Table stakes

Starting a new project is as easy as it should be:

npm init astro npm install npm start

There is a helpful little process and output:

As expected (like you would get with Next or Nuxt or any other site builder kind of project) you get a dev server at a local port you can pop right up:

From here, I consider the table stakes to be CSS injection / Hot Module Reloading. No worries there:

A static site generator with honest-to-god real actual components

This is such a wonderful thing to me. I really like the idea of static site generators—I think they make a lot of sense in a lot of situations. Sending HTML over-the-wire is just a good move for resiliency, CDN-efficiency, SEO, accessibility, you name it. But in the past a lot of the options were either:

  • A JavaScript powered static site generator, that does generate a “static” site, but also ships a JavaScript bundle (e.g. Next or Gatsby)
  • A static site generator that is more focused on HTML and has its own templating/formats that aren’t JavaScript components (e.g. Eleventy or Jekyll)

I know there are exceptions, but this covers the vast majority of the site generator market.

But I want both!

  • I want to craft sites from JavaScript-components, because the syntax and tooling around them is just better than any other component system we have right now.
  • I want static output that is actually zero-JavaScript (unless I manually opt-in to things).

That’s what happens with Astro.

Those components?

  • They can be .jsx files
  • They can be .svelte files
  • They can be .vue files
  • These are “renderers” and you can BYO.

Astro also has it’s own format (.astro) and it’s also very compelling because:

  • It’s obviously a first-class citizen of how Astro works
  • It’s comfortably JSX-like…
  • …except better because it does stuff like makes the <head> work automatically
  • Styled scoping works out of the box, through a normal <style> tag
  • “Fenced” JavaScript runs during build. Let’s look at that next.

Astro files

I mentioned some of the cool parts about the .astro syntax right above. At a higher level, I just like how they look. So little boilerplate! Just gets right to it.

--- import SomeComponent from "../components/SomeComponent";  // This runs in Node, so you look at your command line to see it. console.log("Hi.");  // Example: <SomeComponent greeting="(Optional) Hello" name="Required Name" /> const { greeting = 'Hello', name } = Astro.props; const items = ["Dog", "Cat", "Platipus"]; --- <!-- JSX-like, but also more pleasantly HTML like, like this comment  --> <div class="module">   <h1>{greeting}, {name}!</h1>   <ul>     {items.map((item) => (       <li>{item}</li>     ))} </ul> </div>  <SomeComponent regular="props" />  <style>    /* Scoped! */   .module {     padding: 1rem;   } </style>

The “fences” (---) at the top is where the initial JavaScriptin’ goes. That’s where I yank in the props for this component if it needs any (they can be typed if you like that), do imports/exports, and set up data for the template below.

What feels a little funky, but is in-line with the Astro vibe, is that this is essentially Node JavaScript. It runs in the build process. So that console.log() statement I don’t see in my browser console, I see it in my command line.

pages-style routing

It’s tempting to say Next.js popularized this, but really the concept is as old as file systems. Think of how a classic Apache server works. If you have a file system like:

index.html /about/   index.html

In a browser, you can visit http://website.com/about and that will render that index.html page under the /about folder. That’s what the routing is like here. By virtue of me having:

/pages/   index.astro   about.astro

I’ll have a homepage as well as an /about/ page on my site. That’s just a refreshingly nice way to deal with routing—as opposed to needing to build your own routing with component-ry all to itself.

If you want to do that thing where all the content of your site lives in Markdown files right in the repo, that’s a first-class citizen.

I think this is super common for stuff like blogs and documentation, especially as those are already popular targets for static site generators. And in these early days, I think we’re going to see a lot of Astro sites along those lines while people wait to see if it’s ready for bigger undertakings.

One way to use Markdown is to make Pages in Markdown straight away. The Markdown will also have “fences” (Frontmatter) where you chuck what layout you want to use (best to use an .astro file) and pass in data if you need to. Then the entire content of the Markdown file will flow into the <slot />. Pretty darn slick:

Another incredibly satisfying way to use Markdown in Astro is using the built-in <Markdown /> component. Import it and use it:

--- import { Markdown } from 'astro/components'; ---  <main>   <Markdown>     # Hello world!          - Do thing     - Another thing in my *cool list*   </Markdown>    <div>Outside Markdown</div> </main>

You can also go snag some Markdown from elsewhere in your project and barf that into a component. That leads into fetching data, so let’s look at that next.

I suppose it’s kind of weird how Astro supports all these different frameworks out of the box.

I’ve overheard some pushback that Astro is inefficient at the npm install level since you have to bring down a bunch of stuff you likely won’t need or use. I’ve overheard some pushback on the idea that mixing-matching JavaScript frameworks is a terrible idea.

I agree it’s weird-feeling, but I’m not particularly worried about non-user-facing things. When things are happening only during the build process and all the user ever gets is HTML, use whatever feels good! If you ultimately do load the components-based frameworks to do on-page interactive things, surely it makes sense to limit it to one. And since you’re getting so much at build time, maybe it makes sense to use something designed for super light on-rendered-page interactivity.

Fetching data rules

We were just talking about Markdown so let’s close the loop there. You can “fetch” data internally in Astro by using fetchContent. Look how straightforward it is:

I fetch it the raw Markdown, then I could use the HTML it returns if I want, or slap it into a <Markdown /> component if that makes sense for whatever reason:

--- import { Markdown } from 'astro/components'; const localData = Astro.fetchContent('../content/data.md'); ---  <div class="module">   <Markdown content={localData[0].astro.source} /> </div>

But I don’t have to fetch internal data only. I’m a fan of Eleventy. During an Eleventy build, you can certainly go fetch data from an outside source, but I’d argue it’s a little finnicky. You fetch the data with code in a separate JavaScript file, pulling in your own network library, then processing and returning the data to use elsewhere in a template. Like this. In Astro, that fetching can happen right alongside the component where you need it.

Check out this real-world-ish example where I yank in data from right here from CSS-Tricks and display it as cards.

--- import Card from '../components/Card.astro'; import Header from '../components/Header';  const remoteData = await fetch('https://css-tricks.com/wp-json/wp/v2/posts?per_page=12&_embed').then(response => response.json()); ---  <!doctype html> <html lang="en">  <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1">     <title>CSS-Trickzz</title>     <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⭐️</text></svg>">     <link rel="stylesheet" href="/style/global.css">      <style lang="scss">       .grid {         margin: 4rem;         display: grid;         grid-template-columns: repeat(2, 1fr);         @media (max-width: 650px) {           grid-template-columns: repeat(1, 1fr);           margin: 2rem;         }         gap: 3rem;       }     </style> </head>  <body>   <main>     <Header />      <div class="grid">       {remoteData.map((post) => {         return(           <Card              title={post.title.rendered}             link={post.link}             excerpt={post.excerpt.rendered}             featured_img={post.featured_media_src_url}           />         )       })}     </div>    </main> </body>  </html>

Check it out, I can build a page from CSS-Tricks data just that easily:

What’s fascinating about that is that the data happens:

  1. in Node, not client-side, and
  2. during the build process.

So, in order to keep a website like this updated, I’d have to run the build/deploy process regularly.


Let’s say you want to use Sass to style your site. With many site generators, they punt on this, as a philosophy. Like saying “nah, we don’t want to be opinionated here, you style however you want to”. And I get that, it might be a strength as sometimes frameworks that are too opinionated lose people. But to me, it often feels unfortunate as now I’m on my own to wire up some style-processing build processes (e.g. Gulp) that I really just don’t want to deal with.

With Astro, the philosophy seems to be to support a wide swath of popular styling techniques out of the box right away.

  • Just import "./style.css"; vanilla stylesheets
  • Use a <style> block anywhere in .astro files and the CSS will be scoped to that component…
  • … which is like CSS modules, but that’s only needed if you go for a .jsx file, and if you do, it’s supported.
  • The styling capabilities of .svelte and .vue files work as expected.
  • Sass is built in, just put <style lang="scss"> on the styling blocks wherever.

The styling doc has more detail.

The fancy opt-in JavaScript tricks

Allow me to blockquote this from the README:

  • <MyComponent /> will render an HTML-only version of MyComponent (default)
  • <MyComponent:load /> will render MyComponent on page load
  • <MyComponent:idle /> will use requestIdleCallback() to render MyComponent as soon as main thread is free
  • <MyComponent:visible /> will use an IntersectionObserver to render MyComponent when the element enters the viewport

That’s some fancy dancing. HTML by default, and you opt-in to running your components client-side (JavaScript) only when you specifically want to, and even then, under efficient conditions.

I put a little Vue-based counter (from their examples) onto my demo site and used the :visible modifier to see it work. Check it out:

The Vue stuff only loads when it needs to. Like the blog post says:

Of course, sometimes client-side JavaScript is inevitable. Image carousels, shopping carts, and auto-complete search bars are just a few examples of things that require some JavaScript to run in the browser. This is where Astro really shines: When a component needs some JavaScript, Astro only loads that one component (and any dependencies). The rest of your site continues to exist as static, lightweight HTML.

The Discord is poppin’

I should point out that Astro is super duper new. As I write, they don’t even have real documentation up. It might feel a bit early to be using a framework with docs that only exist as a README. They are working on it though! I’ve seen previews of it because I happen to be in the Discord.

I think they are very smart to have a public Discord as it means there is a hot-n-fast feedback loop for them to improve the framework. I’ve found that being in it is super useful.

I believe they are hoping that Astro grows up into much more than a framework, but a complete platform, where Astro is just the open-source core. You can hear Fred talk with Jason about that on a Learn with Jason episode.

The post A Look at Building with Astro appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, ,

Getting the WordPress Block Editor to Look Like the Front End Design

I’m a WordPress user and, if you’re anything like me, you always have two tabs open when you edit a post: one with the new fancy pants block editor, aka Gutenberg, and another with a preview of the post so you know it won’t look wonky on the front end.

It’s no surprise that a WordPress theme’s styles only affect the front end of your website. The back end posy editor generally looks nothing like the front end result. We’re used to it. But what if I said it’s totally possible for the WordPress editor nearly mirror the front end appearance?

All it takes is a custom stylesheet.

Mind. Blown. Right? Well, maybe it’s not that mind blowing, but it may save you some time if nothing else. 🙂

WordPress gives us a hint of what’s possible here. Fire up the default Twenty Twenty theme that’s packaged with WordPress, fire up the editor, and it sports some light styling.

This whole thing consists of two pretty basic changes:

  1. A few lines of PHP in your theme’s functions.php file that tell the editor you wish to load a custom stylesheet for editor styles
  2. Said custom stylesheet

Right then, enough pre-waffle! Let’s get on with making the WordPress editor look like the front end, shall we?

Step 1: Crack open the functions.php file

OK I was lying, just a little more waffling. If you’re using a WordPress theme that you don’t develop yourself, it’s probably best that you setup a child theme before making any changes to your main theme. </pre-waffle>

Fire up your favorite text editor and open up the theme’s functions.php file that’s usually located in the root of the theme folder. Let’s drop in the following lines at the end of the file:

// Gutenberg custom stylesheet add_theme_support('editor-styles'); add_editor_style( 'style-editor.css' ); // make sure path reflects where the file is located

What this little snippet of code does is tell WordPress to add support for a custom stylesheet to be used with Gutenberg, then points to where that stylesheet (that we’re calling editor-style.css) is located. WordPress has solid documentation for the add_theme_support function if you want to dig into it a little more.

Step 2: CSS tricks (see what I did there?!)

Now we’re getting right into our wheelhouse: writing CSS!

We’ve added editor-styles support to our theme, so the next thing to do is to add the CSS goodness to the stylesheet we defined in functions.php so our styles correctly load up in Gutenberg.

There are thousands of WordPress themes out there, so I couldn’t possibly write a stylesheet that makes the editor exactly like each one. Instead, I will show you an example based off of the theme I use on my website. This should give you an idea of how to build the stylesheet for your site. I’ll also include a template at the end, which should get you started.

OK let’s create a new file called style-editor.css and place it in the root directory of the theme (or again, the child theme if you’re customizing a third-party theme).

Writing CSS for the block editor isn’t quite as simple as using standard CSS elements. For example, if we were to use the following in our editor stylesheet it wouldn’t apply the text size to <h2> elements in the post.

h2 {   font-size: 1.75em; }

Instead of elements, our stylesheet needs to target Block Editor blocks. This way, we know the formatting should be as accurate as possible. That means <h2> elements needs to be scoped to the .rich-text.block-editor-rich-text__editable class to style things up.

Showing the block editor with a light yellow background, a heading that reads Holly Dolly, and a heading 2 with DevTools open to the left in dark mode and a block-editor-rich-text-__editor class highlighted in red.
It just takes a little peek at DevTools to find a class we can latch onto.
h2.rich-text.block-editor-rich-text__editable {   font-size: 1.75em; }

I just so happened to make a baseline CSS file that styles common block editor elements following this pattern. Feel free to snag it over at GitHub and swap out the styles so they complement your theme.

I could go on building the stylesheet here, but I think the template gives you an idea of what you need to populate within your own stylesheet. A good starting point is to go through the stylesheet for your front-end and copy the elements from there, but you will likely need to change some of the element classes so that they apply to the Block Editor window.

If in doubt, play around with elements in your browser’s DevTools to work out what classes apply to which elements. The template linked above should capture most of the elements though.

The results

First of all, let’s take a look at what the WordPress editor looks like without a custom stylesheet:

Showing the WordPress block editor without any custom styling, which is a plain white screen with black text including a heading one paragraph, a blockquote and a black rounded button.
The block editor sports a clean, stark UI in its default appearance. It’s pulling in Noto Serif from Google Fonts but everything else is pretty bare bones.

Let’s compare that to the front end of my test site:

Showing a webpage with the same heading, paragraph, blockquote and button, but with styles that include a red-to-orange gradient that goes left-to-right as a background behind the white heading, a typewriter-like font, the same gradient to style the blockquote borders and text, and to style the button.

Things are pretty different, right? Here we still have a simple design, but I’m using gradients all over, to the max! There’s also a custom font, button styling, and a blockquote. Even the containers aren’t exactly square edges.

Love it or hate it, I think you will agree this is a big departure from the default Gutenberg editor UI. See why I have to have a separate tab open to preview my posts?

Now let’s load up our custom styles and check things out:

The same look as the custom styles on the front end but now displayed in the WordPress block editor.

Well would you look at that! The editor UI now looks pretty much exactly the same as the front end of my website. The content width, fonts, colors and various elements are all the same as the front end. I even have the fancy background against the post title!

Ipso facto — no more previews in another tab. Cool, huh?

Making the WordPress editor look like your front end is a nice convenience. When I’m editing a post, flipping between tabs to see what the posts looks like on the front end ruins my mojo, so I prefer not to do it.

These couple of quick steps should be able to do the same for you, too!

The post Getting the WordPress Block Editor to Look Like the Front End Design appeared first on CSS-Tricks.

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


, , , , , , ,

A Look at What’s New in Chrome DevTools in 2020

I’m excited to share some of the newer features in Chrome DevTools with you. There’s a brief introduction below, and then we’ll cover many of the new DevTools features. We’ll also look at what’s happening in some other browsers. I keep up with this stuff, as I create Dev Tips, the largest collection of DevTools tips you’ll find online! 

It’s a good idea to find out what’s changed in DevTools because it’s constantly evolving and new features are specifically designed to help and improve our development and debugging experience.

Let’s jump into the latest and greatest. While the public stable version of Chrome does have most of these features, I’m using Chrome Canary as I like to stay on the bleeding edge.


Lighthouse is an open source tool for auditing web pages, typically around performance, SEO, accessibility and such. For a while now, Lighthouse has been bundled as part of DevTools meaning you can find it in a panel named… Lighthouse!

Screenshot of DevTools open on a CSS-Tricks page. The Lighthouse panel is open showing a best practices score of 100 out of 100.
Well done, Mr. Coyier. 🏆

I really like Lighthouse because it’s one of easiest parts of DevTools to use. Click “Generate report” and you immediately get human-readable notes for your webpage, such as:

Document uses legible font sizes 100% legible text


Avoid an excessive DOM size (1,189 elements)

Almost every single audit links to developer documentation that explains how the audit may fail, and what you can do to improve it.

The best way to get started with Lighthouse is to run audits on your own websites:

  1. Open up DevTools and navigate to the Lighthouse panel when you are on one of your sites
  2. Select the items you want to audit (Best practices is a good starting point)
  3. Click Generate report
  4. Click on any passed/failed audits to investigate the findings

Even though Lighthouse has been part of DevTools for a while now (since 2017!), it still deserves a significant mention because of the user-facing features it continues to ship, such as:

  • An audit that checks that anchor elements resolve to their URLs (Fun fact: I worked on this!)
  • An audit that checks whether the Largest Contentful Paint metic is fast enough
  • An audit to warn you of unused JavaScript

A better “Inspect Element”

This is a subtle and, in some ways, very small feature, but it can have profound effects on how we treat web accessibility.

Here’s how it works. When you use Inspect Element — what is arguably the most common use of DevTools — you now get a tooltip with additional information on accessibility.

Screenshot showing DevTools open on a CSS-Tricks page. An element is highlighted on the page and a tooltip with a white background is above it providing information on the element's color, font, contrast, name, role, and whether it is keyboard-focusable.
Accessibility is baked right in!

The reason I say this can have a profound impact is because DevTools has had accessibility features for quite some time now, but how many of us actually use them? Including this information on a commonly used feature like Inspect Element will gives it a lot more visibility and makes it a lot more accessible.

The tooltip includes:

  • the contrast ratio of the text (how well, or how poorly, does the foreground text contrast with the background color)
  • the text representation
  • the ARIA role
  • whether or not the inspected element is keyboard-focusable

To try this out, right-click (or Cmd + Shift + C) on an element and select Inspect to view it in DevTools.

I made a 14-minute video on Accessibility debugging with Chrome DevTools which covers some of this in more detail.

Emulate vision deficiencies

Exactly as it says on the tin, you can use Chrome DevTools to emulate vision impairments. For example, we can view a site through the lens of blurred vision.

Screenshot of DevTools open on a CSS-Tricks page. The Rendering panel is open and the blurred vision option is selected. The CSS-Tricks page is blurry and difficult to read.
That’s a challenge to read!

How can you do this in DevTools? Like this:

  1. Open DevTools (right click and “Inspect” or Cmd + Shift + C).
  2. Open the DevTools Command menu (Cmd + Shift + P on Mac, Ctrl + Shift + P on Windows).
  3. Select Show Rendering in the Command menu.
  4. Select a deficiency in the Rendering pane.

We used blurred vision as an example, but DevTools has other options, including: protanopia, deuteranopia, tritanopia, and achromatopsia.

Like with any tool of this nature, it’s designed to be a complement to our (hopefully) existing accessibility skills. In other words, it’s not instructional, but rather, influential on the designs and user experiences we create.

Here are a couple of extra resources on low vision accessibility and emulation:

Get timing on performance

The Performance Panel in DevTools can sometimes look like a confusing mish-mash of shapes and colors.

This update to it is great because it does a better job surfacing meaningful performance metrics.

Screenshot of DevTools with the Performance panel open. A chart showing the timeline of page rendering is above a row of Timings, including DCL, FP, FCP, L, and LCP. Below that is a summary that provides a time range for the selected timing.

What we want to look at are those extra timing rectangles shown in the “Timings” in the Performance Panel recording. This highlights:

  • DOMContentLoaded: The event which triggers when the initial HTML loads
  • First Paint: When the browser first paints pixels to the screen
  • First Contentful Paint: The point at which the browser draws content from the DOM which indicates to the user that content is loading
  • Onload: When the page and all of its resources have finished loading
  • Largest Contentful Paint: The largest image or text element, which is rendered in the viewport

As a bonus, if you find the Largest Contentful Paint event in a Performance Panel recording, you can click on it to get additional information.

Nice work, CSS-Tricks! The Largest Contentful Paint happens early on in the page load.

While there is a lot of golden information here, the “Related Node” is potentially the most useful item because it specifies exactly which element contributed to the LCP event.

To try this feature out:

  1. Open up DevTools and navigate to the Performance panel
  2. Click “Start profiling and reload page”
  3. Observe the timing metrics in the Timings section of a recording
  4. Click the individual metrics to see what additional information you get

Monitor performance

If you want to quickly get started using DevTools to analyze performance and you’ve already tried Lighthouse, then I recommend the Performance Monitor feature. This is sort of like having WebPageTest.org right at your fingertips with things like CPU usage.

Screenshot of DevTools with the Performance Monitor pane open. Four timeline charts are stacked vertically, starting with CPU Usage,followed by JavaScript Heap Size, DOM Nodes, and JavaScript Event Listeners.

Here’s how to access it:

  1. Open DevTools
  2. Open up the Command menu (Cmd + Shift + P on Mac, Ctrl + Shift + P on Windows)
  3. Select “Show performance monitor” from the Command menu
  4. Interact and navigate around the website
  5. Observe the results

The Performance Monitor can give you interesting metrics, however, unlike Lighthouse, it’s for you to figure out how to interpret them and take action. No suggestions are provided. It’s up to you to study that CPU usage chart and ask whether something like 90% is an acceptable level for your site (it probably isn’t).

The Performance Monitor has an interactive legend, where you can toggle metrics on and off, such as:

  • CPU usage
  • JS heap size
  • DOM Nodes
  • JS event listeners
  • Documents
  • Document Frames
  • Layouts / sec
  • Style recalcs / sec 

CSS overview and local overrides

CSS-Tricks has already covered these features, so go and check them out!

  • CSS Overview: A handy DevTools panel that gives a bunch of interesting stats on the CSS your page is using
  • Local Overrides:  A powerful feature that lets you override production websites with your local resources, so you can easily preview changes 

So, what about DevTool in other browsers?

I’m sure you noticed that I’ve been using Chrome throughout this article. It’s the browser I use personally. That said, it’s worth considering that:

  • Firefox DevTools is looking pretty great right now
  • With Microsoft Edge extending from Chromium, it too will benefit from these DevTools features
  • As evident on the Safari Technology Preview Release Notes (search for Web Inspector on that page), Safari DevTools has come a long way 

In other words, keep an eye out because this is a quickly evolving space!


We covered a lot in a short amount of space!

  • Lighthouse: A panel that provides  tips and suggestions for performance, accessibility, SEO and best practices.
  • Inspect Element: An enhancement to the Inspect Element feature that provides accessibility information to the Inspect Element tooltip
  • Emulate vision deficiencies: A feature in the Rendering Pane to view a page through the lens of low vision.
  • Performance Panel Timings: Additional metrics in the Performance panel recording, showing user-orientated stats, like Largest Contentful Paint
  • Performance Monitor – A real-time visualization of performance metrics for the current website, such as CPU usage and DOM size

Please check out my mailing list, Dev Tips, if you want to stay keep up with the latest updates and get over 200 web development tips! I also have a premium video course over at ModernDevTools.com. And, I tend to post loads of bonus web development resources on Twitter.

The post A Look at What’s New in Chrome DevTools in 2020 appeared first on CSS-Tricks.

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


, , , ,

A First Look at `aspect-ratio`

Oh hey! A brand new property that affects how a box is sized! That’s a big deal. There are lots of ways already to make an aspect-ratio sized box (and I’d say this custom properties based solution is the best), but none of them are particularly intuitive and certainly not as straightforward as declaring a single property.

So, with the impending arrival of aspect-ratio (MDN, and not to be confused with the media query version), I thought I’d take a look at how it works and try to wrap my mind around it.

Shout out to Una where I first saw this. Boy howdy did it strike interest in folks:


Just dropping aspect-ratio on an element alone will calculate a height based on the auto width.

Without setting a width, an element will still have a natural auto width. So the height can be calculated from the aspect ratio and the rendered width.

.el {   aspect-ratio: 16 / 9; }

If the content breaks out of the aspect ratio, the element will still expand.

The aspect ratio becomes ignored in that situation, which is actually nice. Better to avoid potential data loss. If you prefer it doesn’t do this, you can always use the padding hack style.

If the element has either a height or width, the other is calculated from the aspect ratio.

So aspect-ratio is basically a way of seeing the other direction when you only have one (demo).

If the element has both a height and width, aspect-ratio is ignored.

The combination of an explicit height and width is “stronger” than the aspect ratio.

Factoring in min-* and max-*

There is always a little tension between width, min-width, and max-width (or the height versions). One of them always “wins.” It’s generally pretty intuitive.

If you set width: 100px; and min-width: 200px; then min-width will win. So, min-width is either ignored because you’re already over it, or wins. Same deal with max-width: if you set width: 100px; and max-width: 50px; then max-width will win. So, max-width is either ignored because you’re already under it, or wins.

It looks like that general intuitiveness carries on here: the min-* and max-* properties will either win or are irrelevant. And if they win, they break the aspect-ratio.

.el {   aspect-ratio: 1 / 4;   height: 500px;    /* Ignored, because width is calculated to be 125px */   /* min-width: 100px; */    /* Wins, making the aspect ratio 1 / 2 */   /* min-width: 250px; */ } 

With value functions

Aspect ratios are always most useful in fluid situations, or anytime you essentially don’t know one of the dimensions ahead of time. But even when you don’t know, you’re often putting constraints on things. Say 50% wide is cool, but you only want it to shrink as far as 200px. You might do width: max(50%, 200px);. Or constrain on both sides with clamp(200px, 50%, 400px);.

This seems to work inutitively:

.el {   aspect-ratio: 4 / 3;   width: clamp(200px, 50%, 400px); }

But say you run into that minimum 200px, and then apply a min-width of 300px? The min-width wins. It’s still intuitive, but it gets brain-bending because of how many properties, functions, and values can be involved.

Maybe it’s helpful to think of aspect-ratio as the weakest way to size an element?

It will never beat any other sizing information out, but it will always do its sizing if there is no other information available for that dimension.

The post A First Look at `aspect-ratio` appeared first on CSS-Tricks.


, ,

An Early Look at the Vue 3 Composition API in the Wild

I recently had an opportunity to try the new Vue Composition API in a real project to check where it might be useful and how we could use it in the future.

Until now, when we were creating a new component we were using Options API. That API forced us to separate the component’s code by options, meaning that we needed to have all reactive data in one place (data), all computed properties in one place (computed), all methods in one place (methods), and so on.

As it is handy and readable for smaller components, it becomes painful when the component gets more complicated and deals with multiple functionalities. Usually, logic related to one specific functionality contains some reactive data, computed property, a method or a few of them; sometimes it also involves using component lifecycle hooks. That makes you constantly jump between different options in the code when working on a single logical concern.

The other issue that you may have encountered when working with Vue is how to extract a common logic that can be reused by multiple components. Vue already has few options to do that, but all of them have their own drawbacks (e.g. mixins, and scoped-slots).

The Composition API brings a new way of creating component, separating code and extracting reusable pieces of code.

Let’s start with code composition within a component.

Code composition

Imagine you have a main component that sets up few things for your whole Vue app (like layout in Nuxt). It deals with the following things:

  • setting locale
  • checking if the user is still authenticated and redirects them if not
  • preventing the user from reloading the app too many times
  • tracking user activity and reacting when the user is inactive for specific period of time
  • listening on an event using EventBus (or window object event)

Those are just a few things the component can do. You can probably imagine a more complex component, but this will serve the purpose of this example. For the sake of readability, I am just using names of the props without the actual implementation.

This is how the component would look like using Options API:

<template>   <div id="app">     ...   </div> </template>  <script> export default {   name: 'App',    data() {     return {       userActivityTimeout: null,       lastUserActivityAt: null,       reloadCount: 0     }   },    computed: {     isAuthenticated() {...}     locale() {...}   },    watch: {     locale(value) {...},     isAuthenticated(value) {...}   },    async created() {     const initialLocale = localStorage.getItem('locale')     await this.loadLocaleAsync(initialLocale)   },    mounted() {     EventBus.$  on(MY_EVENT, this.handleMyEvent)      this.setReloadCount()     this.blockReload()      this.activateActivityTracker()     this.resetActivityTimeout()   },    beforeDestroy() {     this.deactivateActivityTracker()     clearTimeout(this.userActivityTimeout)     EventBus.$  off(MY_EVENT, this.handleMyEvent)   },    methods: {     activateActivityTracker() {...},     blockReload() {...},     deactivateActivityTracker() {...},     handleMyEvent() {...},     async loadLocaleAsync(selectedLocale) {...}     redirectUser() {...}     resetActivityTimeout() {...},     setI18nLocale(locale) {...},     setReloadCount() {...},     userActivityThrottler() {...},   } } </script>

As you can see, each option contains parts from all functionalities. There is no clear separation between them and that makes the code hard to read, especially if you are not the person who wrote it and you are looking at it for the first time. It is very hard to find which method is used by which functionality.

Let’s look at it again but identify the logical concerns as comments. Those would be:

  • Activity tracker
  • Reload blocker
  • Authentication check
  • Locale
  • Event Bus registration
<template>   <div id="app">     ...   </div> </template>  <script> export default {   name: 'App',    data() {     return {       userActivityTimeout: null, // Activity tracker       lastUserActivityAt: null, // Activity tracker       reloadCount: 0 // Reload blocker     }   },    computed: {     isAuthenticated() {...} // Authentication check     locale() {...} // Locale   },    watch: {     locale(value) {...},     isAuthenticated(value) {...} // Authentication check   },    async created() {     const initialLocale = localStorage.getItem('locale') // Locale     await this.loadLocaleAsync(initialLocale) // Locale   },    mounted() {     EventBus.$  on(MY_EVENT, this.handleMyEvent) // Event Bus registration      this.setReloadCount() // Reload blocker     this.blockReload() // Reload blocker      this.activateActivityTracker() // Activity tracker     this.resetActivityTimeout() // Activity tracker   },    beforeDestroy() {     this.deactivateActivityTracker() // Activity tracker     clearTimeout(this.userActivityTimeout) // Activity tracker     EventBus.$  off(MY_EVENT, this.handleMyEvent) // Event Bus registration   },    methods: {     activateActivityTracker() {...}, // Activity tracker     blockReload() {...}, // Reload blocker     deactivateActivityTracker() {...}, // Activity tracker     handleMyEvent() {...}, // Event Bus registration     async loadLocaleAsync(selectedLocale) {...} // Locale     redirectUser() {...} // Authentication check     resetActivityTimeout() {...}, // Activity tracker     setI18nLocale(locale) {...}, // Locale     setReloadCount() {...}, // Reload blocker     userActivityThrottler() {...}, // Activity tracker   } } </script>

See how hard it is to untangle all of those? 🙂

Now imagine you need to make a change in one functionality (e.g. activity tracking logic). Not only do you need to know which elements are related to that logic, but even when you know, you still need to jump up and down between different component options.

Let’s use the Composition API to separate the code by logical concerns. To do that we create a single function for each logic related to a specific functionality. This is what we call a composition function.

// Activity tracking logic function useActivityTracker() {   const userActivityTimeout = ref(null)   const lastUserActivityAt = ref(null)    function activateActivityTracker() {...}   function deactivateActivityTracker() {...}   function resetActivityTimeout() {...}   function userActivityThrottler() {...}    onBeforeMount(() => {     activateActivityTracker()     resetActivityTimeout()   })    onUnmounted(() => {     deactivateActivityTracker()     clearTimeout(userActivityTimeout.value)   }) }
// Reload blocking logic function useReloadBlocker(context) {   const reloadCount = ref(null)    function blockReload() {...}   function setReloadCount() {...}    onMounted(() => {     setReloadCount()     blockReload()   }) }
// Locale logic function useLocale(context) {   async function loadLocaleAsync(selectedLocale) {...}   function setI18nLocale(locale) {...}    watch(() => {     const locale = ...     loadLocaleAsync(locale)   })    // No need for a 'created' hook, all logic that runs in setup function is placed between beforeCreate and created hooks   const initialLocale = localStorage.getItem('locale')   loadLocaleAsync(initialLocale) }
// Event bus listener registration import EventBus from '@/event-bus'  function useEventBusListener(eventName, handler) {   onMounted(() => EventBus.$  on(eventName, handler))   onUnmounted(() => EventBus.$  off(eventName, handler)) }

As you can see, we can declare reactive data (ref / reactive), computed props, methods (plain functions), watchers (watch) and lifecycle hooks (onMounted / onUnmounted). Basically everything you normally use in a component.

We have two options when it comes to where to keep the code. We can leave it inside the component or extract it into a separate file. Since the Composition API is not officially there yet, there are no best practices or rules on how to deal with it. The way I see it, if the logic is tightly coupled to a specific component (i.e. it won’t be reused anywhere else), and it can’t live without the component itself, I suggest leaving it within the component. On the flip side, if it is general functionality that will likely be reused, I suggest extracting it to a separate file. However, if we want to keep it in a separate file, we need to remember to export the function from the file and import it in our component.

This is how our component will look like using newly created composition functions:

<template>   <div id="app">          </div> </template>  <script> export default {   name: 'App',    setup(props, context) {     useEventBusListener(MY_EVENT, handleMyEvent)     useActivityTracker()     useReloadBlocker(context)     useLocale(context)      const isAuthenticated = computed(() => ...)      watch(() => {       if (!isAuthenticated) {...}     })      function handleMyEvent() {...},      function useLocale() {...}     function useActivityTracker() {...}     function useEventBusListener() {...}     function useReloadBlocker() {...}   } } </script>

This gives us a single function for each logical concern. If we want to use any specific concern, we need to call the related composition function in the new setup function.

Imagine again that you need to make some change in activity tracking logic. Everything related to that functionality lives in the useActivityTracker function. Now you instantly know where to look and jump to the right place to see all the related pieces of code. Beautiful!

Extracting reusable pieces of code

In our case, the Event Bus listener registration looks like a piece of code we can use in any component that listens to events on Event Bus.

As mentioned before, we can keep the logic related to a specific functionality in a separate file. Let’s move our Event Bus listener setup into a separate file.

// composables/useEventBusListener.js import EventBus from '@/event-bus'  export function useEventBusListener(eventName, handler) {   onMounted(() => EventBus.$  on(eventName, handler))   onUnmounted(() => EventBus.$  off(eventName, handler)) }

To use it in a component, we need to make sure we export our function (named or default) and import it in a component.

<template>   <div id="app">     ...   </div> </template>  <script> import { useEventBusListener } from '@/composables/useEventBusListener'  export default {   name: 'MyComponent',    setup(props, context) {     useEventBusListener(MY_EVENT, myEventHandled)     useEventBusListener(ANOTHER_EVENT, myAnotherHandled)   } } </script>

That’s it! We can now use that in any component we need.

Wrapping up

There is an ongoing discussion about the Composition API. This post has no intention to promote any side of the discussion. It is more about showing when it might be useful and in what cases it brings additional value.

I think it is always easier to understand the concept on a real life example like above. There are more use cases and, the more you use the new API, the more patterns you will see. This post is merely a few basic patterns to get your started.

Let’s go again through the presented use cases and see where the Composition API can be useful:

General features that can live on its own without tight coupling with any specific component

  • All logic related to a specific feature in one file
  • Keep it in @/composables/*.js and import it in components
  • Examples: Activity Tracker, Reload Blocker, and Locale

Reusable features that are used in multiple components

  • All logic related to a specific feature in one file
  • Keep it in @/composables/*.js and import in components
  • Examples: Event Bus listener registration, window event registration, common animation logic, common library usage

Code organization within component

  • All logic related to a specific feature in one function
  • Keep the code in a composition function within the component
  • The code related to the same logical concern is in the same place (i.e. there’s no need to jump between data, computed, methods, lifecycle hooks, etc.)

Remember: This is all a work-in-progress!

The Vue Composition API is currently at work in progress stage and is subject to future changes. Nothing mentioned in the examples above is sure, and both syntax and use cases may change. It is intended to be shipped with Vue version 3.0. In the meantime, you can check out view-use-web for a collection of composition functions that are expected to be included in Vue 3 but can be used with the Composition API in Vue 2.

If you want to experiment with the new API you can use the @vue/composition library.

The post An Early Look at the Vue 3 Composition API in the Wild appeared first on CSS-Tricks.


, , ,

A Look at JAMstack’s Speed, By the Numbers

People say JAMstack sites are fast — let’s find out why by looking at real performance metrics! We’ll cover common metrics, like Time to First Byte (TTFB) among others, then compare data across a wide section of sites to see how different ways to slice those sites up compare.

First, I’d like to present a small analysis to provide some background. According to the HTTPArchive metrics report on page loading, users wait an average of 6.7 seconds to see primary content.

First Contentful Paint (FCP) – measures the point at which text or graphics are first rendered to the screen.

The FCP distribution for the 10th, 50th and 90th percentile values as reported on August 1, 2019.

If we are talking about engagement with a page (Time to Interactive), users wait even longer. The average time to interactive is 9.3 seconds.

Time to Interactive (TTI) – a time when user can interact with a page without delay.

TTI distribution for the 10th, 50th and 90th percentile values as reported on August 1, 2019.

State of the real user web performance

The data above is from lab monitoring and doesn’t fully represent real user experience. Real users data based taken from the Chrome User Experience Report (CrUX) shows an even wider picture.

​​I’ll use data aggregated from users who use mobile devices. Specifically, we will use metrics like:

Time To First Byte

TTFB represents the time browser waits to receive first bytes of the response from server. TTFB takes from 200ms to 1 second for users around the world. It’s a pretty long time to receive the first chunks of the page.

TTFB mobile speed distribution (CrUX, July 2019)

First Contentful Paint

FCP happens after 2.5 seconds for 23% of page views around the world.

FCP mobile speed distribution (CrUX, July 2019)

First Input Delay

FID metrics show how fast web pages respond to user input (e.g. click, scroll, etc.).

CrUX doesn’t have TTI data due to different restrictions, but has FID, which is even better can reflect page interactivity. Over 75% of mobile user experiences have input delay for 50ms and users didn’t experience any jank.

FID mobile speed distribution (CrUX, July 2019)

You can use the queries below and play with them on this site.

Data from July 2019
[     {       "date": "2019_07_01",       "timestamp": "1561939200000",       "client": "desktop",       "fastTTFB": "27.33",       "avgTTFB": "46.24",       "slowTTFB": "26.43",       "fastFCP": "48.99",       "avgFCP": "33.17",       "slowFCP": "17.84",       "fastFID": "95.78",       "avgFID": "2.79",       "slowFID": "1.43"     },     {       "date": "2019_07_01",       "timestamp": "1561939200000",       "client": "mobile",       "fastTTFB": "23.61",       "avgTTFB": "46.49",       "slowTTFB": "29.89",       "fastFCP": "38.58",       "avgFCP": "38.28",       "slowFCP": "23.14",       "fastFID": "75.13",       "avgFID": "17.95",       "slowFID": "6.92"     }   ]
#standardSQL   SELECT     REGEXP_REPLACE(yyyymm, '(d{4})(d{2})', '1_2_01') AS date,     UNIX_DATE(CAST(REGEXP_REPLACE(yyyymm, '(d{4})(d{2})', '1-2-01') AS DATE)) * 1000 * 60 * 60 * 24 AS timestamp,     IF(device = 'desktop', 'desktop', 'mobile') AS client,     ROUND(SUM(fast_fcp) * 100 / (SUM(fast_fcp) + SUM(avg_fcp) + SUM(slow_fcp)), 2) AS fastFCP,     ROUND(SUM(avg_fcp) * 100 / (SUM(fast_fcp) + SUM(avg_fcp) + SUM(slow_fcp)), 2) AS avgFCP,     ROUND(SUM(slow_fcp) * 100 / (SUM(fast_fcp) + SUM(avg_fcp) + SUM(slow_fcp)), 2) AS slowFCP,     ROUND(SUM(fast_fid) * 100 / (SUM(fast_fid) + SUM(avg_fid) + SUM(slow_fid)), 2) AS fastFID,     ROUND(SUM(avg_fid) * 100 / (SUM(fast_fid) + SUM(avg_fid) + SUM(slow_fid)), 2) AS avgFID,     ROUND(SUM(slow_fid) * 100 / (SUM(fast_fid) + SUM(avg_fid) + SUM(slow_fid)), 2) AS slowFID   FROM     `chrome-ux-report.materialized.device_summary`   WHERE     yyyymm = '201907'   GROUP BY     date,     timestamp,     client   ORDER BY     date DESC,     client

State of Content Management Systems (CMS) performance

CMSs should have become our saviors, helping us build faster sites. But looking at the data, that is not the case. The current state of CMS performance around the world is not so great.

TTFB mobile speed distribution comparison between all web and CMS (CrUX, July 2019)
Data from July 2019
[     {       "freq": "1548851",       "fast": "0.1951",       "avg": "0.4062",       "slow": "0.3987"     }   ]
#standardSQL   SELECT     COUNT(DISTINCT origin) AS freq,            ROUND(SUM(IF(ttfb.start < 200, ttfb.density, 0)) / SUM(ttfb.density), 4) AS fastTTFB,     ROUND(SUM(IF(ttfb.start >= 200 AND ttfb.start < 1000, ttfb.density, 0)) / SUM(ttfb.density), 4) AS avgTTFB,     ROUND(SUM(IF(ttfb.start >= 1000, ttfb.density, 0)) / SUM(ttfb.density), 4) AS slowTTFB      FROM     `chrome-ux-report.all.201907`,     UNNEST(experimental.time_to_first_byte.histogram.bin) AS ttfb   JOIN (     SELECT       url,       app     FROM       `httparchive.technologies.2019_07_01_mobile`     WHERE       category = 'CMS'     )   ON CONCAT(origin, '/') = url   ORDER BY     freq DESC

And here are the FCP results:

FCP mobile speed distribution comparison between all web and CMS (CrUX, July 2019)

At least the FID results are a bit better:

FID mobile speed distribution comparison between all web and CMS (CrUX, July 2019)
Data from July 2019
[     {       "freq": "546415",       "fastFCP": "0.2873",       "avgFCP": "0.4187",       "slowFCP": "0.2941",       "fastFID": "0.8275",       "avgFID": "0.1183",       "slowFID": "0.0543"     }   ]
#standardSQL   SELECT     COUNT(DISTINCT origin) AS freq,     ROUND(SUM(IF(fcp.start < 1000, fcp.density, 0)) / SUM(fcp.density), 4) AS fastFCP,     ROUND(SUM(IF(fcp.start >= 1000 AND fcp.start < 2500, fcp.density, 0)) / SUM(fcp.density), 4) AS avgFCP,     ROUND(SUM(IF(fcp.start >= 2500, fcp.density, 0)) / SUM(fcp.density), 4) AS slowFCP,     ROUND(SUM(IF(fid.start < 50, fid.density, 0)) / SUM(fid.density), 4) AS fastFID,     ROUND(SUM(IF(fid.start >= 50 AND fid.start < 250, fid.density, 0)) / SUM(fid.density), 4) AS avgFID,     ROUND(SUM(IF(fid.start >= 250, fid.density, 0)) / SUM(fid.density), 4) AS slowFID   FROM     `chrome-ux-report.all.201907`,     UNNEST(first_contentful_paint.histogram.bin) AS fcp,     UNNEST(experimental.first_input_delay.histogram.bin) AS fid   JOIN (     SELECT       url,       app     FROM       `httparchive.technologies.2019_07_01_mobile`     WHERE       category = 'CMS'     )   ON CONCAT(origin, '/') = url   ORDER BY     freq DESC

As you can see, sites built with a CMS perform not much better than the overall performance of sites on web.

You can find performance distribution across different CMSs on this HTTPArchive forum discussion.

E-Commerce websites, a good example of sites that are typically built on a CMS, have really bad stats for page views:

  • ~40% – 1second for TTFB
  • ~30% – more than 1.5 second for FCP
  • ~12% – lag for page interaction.

I faced clients who requested support of IE10-IE11 because the traffic from those users represented 1%, which equalled millions of dollars in revenue. Please, calculate your losses in case 1% of users leave immediately and never came back because of bad performance. If users aren’t happy, business will be unhappy, too.

To get more details about how web performance correlates with revenue, check out WPO Stats. It’s a list of case studies from real companies and their success after improving performance.

JAMstack helps improve web performance

Credit: Snipcart

With JAMstack, developers do as little rendering on the client as possible, instead using server infrastructure for most things. Not to mention, most JAMstack workflows are great at handling deployments, and helping with scalability, among other benefits. Content is stored statically on a static file hosts and provided to the users via CDN.

Read Mathieu Dionne’s “New to JAMstack? Everything You Need to Know to Get Started” for a great place to become more familiar with JAMstack.

I had two years of experience working with one of the popular CMSs for e-commerce and we had a lot of problems with deployments, performance, scalability. The team would spend days and fixing them. It’s not what customers want. These are the sorts of big issues JAMstack solves.

Looking at the CrUX data, JAMstack sites performance looks really solid. The following values are based on sites served by Netlify and GitHub. There is some discussion on the HTTPArchive forum where you can participate to make data more accurate.

Here are the results for TTFB:

TTFB mobile speed distribution comparison between all web, CMS and JAMstack sites (CrUX, July 2019)
Data from July 2019
[   {     "n": "7627",     "fastTTFB": "0.377",     "avgTTFB": "0.5032",     "slowTTFB": "0.1198"   } ]
#standardSQL SELECT   COUNT(DISTINCT origin) AS n,   ROUND(SUM(IF(ttfb.start < 200, ttfb.density, 0)) / SUM(ttfb.density), 4) AS fastTTFB,   ROUND(SUM(IF(ttfb.start >= 200 AND ttfb.start < 1000, ttfb.density, 0)) / SUM(ttfb.density), 4) AS avgTTFB,   ROUND(SUM(IF(ttfb.start >= 1000, ttfb.density, 0)) / SUM(ttfb.density), 4) AS slowTTFB FROM   `chrome-ux-report.all.201907`,   UNNEST(experimental.time_to_first_byte.histogram.bin) AS ttfb JOIN   (SELECT url, REGEXP_EXTRACT(LOWER(CONCAT(respOtherHeaders, resp_x_powered_by, resp_via, resp_server)),       '(netlify|x-github-request)')     AS platform   FROM `httparchive.summary_requests.2019_07_01_mobile`) ON   CONCAT(origin, '/') = url WHERE   platform IS NOT NULL ORDER BY   n DESC

Here’s how FCP shook out:

FCP mobile speed distribution comparison between all web, CMS and JAMstack sites (CrUX, July 2019)

Now let’s look at FID:

FID mobile speed distribution comparison between all web, CMS and JAMstack sites (CrUX, July 2019)
Data from July 2019
[     {       "n": "4136",       "fastFCP": "0.5552",       "avgFCP": "0.3126",       "slowFCP": "0.1323",       "fastFID": "0.9263",       "avgFID": "0.0497",       "slowFID": "0.024"     }   ]
#standardSQL   SELECT     COUNT(DISTINCT origin) AS n,     ROUND(SUM(IF(fcp.start < 1000, fcp.density, 0)) / SUM(fcp.density), 4) AS fastFCP,     ROUND(SUM(IF(fcp.start >= 1000 AND fcp.start < 2500, fcp.density, 0)) / SUM(fcp.density), 4) AS avgFCP,     ROUND(SUM(IF(fcp.start >= 2500, fcp.density, 0)) / SUM(fcp.density), 4) AS slowFCP,     ROUND(SUM(IF(fid.start < 50, fid.density, 0)) / SUM(fid.density), 4) AS fastFID,     ROUND(SUM(IF(fid.start >= 50 AND fid.start < 250, fid.density, 0)) / SUM(fid.density), 4) AS avgFID,     ROUND(SUM(IF(fid.start >= 250, fid.density, 0)) / SUM(fid.density), 4) AS slowFID   FROM     `chrome-ux-report.all.201907`,     UNNEST(first_contentful_paint.histogram.bin) AS fcp,     UNNEST(experimental.first_input_delay.histogram.bin) AS fid   JOIN     (SELECT url, REGEXP_EXTRACT(LOWER(CONCAT(respOtherHeaders, resp_x_powered_by, resp_via, resp_server)),         '(netlify|x-github-request)')       AS platform     FROM `httparchive.summary_requests.2019_07_01_mobile`)   ON     CONCAT(origin, '/') = url   WHERE     platform IS NOT NULL   ORDER BY     n DESC

The numbers show the performance of JAMstack sites is the best. The numbers are pretty much the same for mobile and desktop which is even more amazing!

Some highlights from engineering leaders

Let me show you a couple of examples from some prominent folks in the industry:

JAMstack sites are generally CDN-hosted and mitigate TTFB. Since the file hosting is handled by infrastructures like Amazon Web Services or similar, all sites performance can be improved in one fix.

One more real investigation says that it is better to deliver static HTML for better FCP.

Here’s a comparison for all results shown above together:

Mobile speed distribution comparison between all web, CMS and JAMstack sites (CrUX, July 2019)

JAMstack brings better performance to the web by statically serving pages with CDNs. This is important because a fast back-end that takes a long time to reach users will be slow, and likewise, a slow back-end that is quick to reach users will also be slow.

JAMstack hasn’t won the perf race yet, because the number of sites built with it not so huge as for example for CMS, but the intention to win it is really great.

Adding these metrics to a performance budget can be one way make sure you are building good performance into your workflow. Something like:

  • TTFB: 200ms
  • FCP: 1s
  • FID: 50ms

Spend it wisely 🙂

Editor’s note: Artem Denysov is from Stackbit, which is a service that helps tremendously with spinning up JAMstack sites and more upcoming tooling to smooth out some of the workflow edges with JAMstack sites and content. Artem told me he’d like to thank Rick Viscomi, Rob Austin, and Aleksey Kulikov for their help in reviewing the article.

The post A Look at JAMstack’s Speed, By the Numbers appeared first on CSS-Tricks.


, , ,

Awards That Look Beyond the Flashy

Dan Mall is judging the Communication Arts Interactive 2020 awards. These types of things are usually a celebration of flashy, short-lived, one-off designs. Those things are awesome, but Dan has more in mind:

I’d love to award work that demonstrates creative use of the highest level of color contrast ratios and works well on assistive devices. I’d love to award work that’s still useful when JavaScript fails. I’d love to award work that shows smart thinking and strategy in addition to flawless execution and art direction. I’d love to award work that serves business and user needs.

Direct Link to ArticlePermalink

The post Awards That Look Beyond the Flashy appeared first on CSS-Tricks.


, , ,