Developer News

Using Performant Next-Gen Images in CSS with image-set

Css Tricks - Wed, 06/23/2021 - 9:10am

The CSS image-set() function has been supported in Chromium-based browsers since 2012 and in Safari since version 6. Support recently landed in Firefox 88. Let’s dive in and see what we can and can’t do today with image-set().

Multiple resolutions of the same image

Here’s what the CSS spec has to say about image-set():

Delivering the most appropriate image resolution for a user’s device can be a difficult task. Ideally, images should be in the same resolution as the device they’re being viewed in, which can vary between users. However, other factors can factor into the decision of which image to send; for example, if the user is on a slow mobile connection, they may prefer to receive lower-res images rather than waiting for a large proper-res image to load.

It’s basically a CSS background equivalent to the HTML srcset attribute for img tags. By using image-set we can provide multiple resolutions of an image and trust the browser to make the best decision about which one to use. This can be used to specify a value for three different CSS properties: content, cursor, and most useful of all, background-image.

.hero { background-image: image-set("platypus.png" 1x, "platypus-2x.png" 2x); }

1x is used to identify the low-res image, while 2x is used to define the high-res image. x is an alias of dppx, which stands for dots per pixel unit.

Chrome/Edge/Opera/Samsung Internet currently require a -webkit- prefix. If you’re using Autoprefixer, this will be handled automatically. Safari no longer requires the prefix but uses an older syntax that requires a url() function to specify the image path. We could also include a regular old background-image: url() to support any browsers that don’t support image-set.

.hero { /* Fallback */ background-image: url("platypus.png"); /* Chrome/Edge/Opera/Samsung, Safari will fallback to this as well */ background-image: -webkit-image-set(url("platypus.png") 1x, url("platypus-2x.png") 2x); /* Standard use */ background-image: image-set("platypus.png" 1x, "platypus-2x.png" 2x); }

Now users on expensive fancy devices will see a super sharp image. Performance will be improved for users on slow connections or with cheaper screens as their browser will automatically request the lower-res image. If you wanted to be sure that the high-res image was used on high-res devices, even on slow connections, you could make use of the min-resolution media query instead of image-set. For more on serving sharp images to high density screens, check out Jake Archibald’s recent post over on his blog.

That’s pretty cool, but what I really want is to be able to adopt the latest image formats in CSS while still catering for older browsers…

New image formats

Safari 14 shipped support for WebP. It was the final modern browser to do so which means the image format is now supported everywhere (except Internet Explorer). WebP is useful in that it can make images that are often smaller than (but of the same quality as) JPG, PNG, or GIF.

There’s also a whole bunch of even newer image formats cropping up. AVIF images are shockingly tiny. Chrome, Opera and Samsung Internet have already shipped support for AVIF. It’s already in Firefox behind a flag. This image format isn’t supported by many design tools yet but you can convert images to AVIF using the Squoosh app built by the Chrome team at Google. WebP 2, HEIF and JPEG XL might also make it into browsers eventually. This is all rather exciting, but we want browsers that don’t support these newer formats to get some images. Fortunately image-set() has a syntax for that.

Using new image formats by specifying a type

Browser support note: The feature of image-set that I’m about to talk about currently has pretty terrible browser support. Currently it’s only supported in Firefox 89.

HTML has supported the <picture> element for years now.

<picture> <source srcset="./kitten.avif" type="image/avif"> <img src="./kitten.jpg" alt="a small kitten"> </picture>

image-set provides the CSS equivalent, allowing for the use of next-gen image formats by specifying the image’s MIME type:

.div1 { background-image: image-set( "kitten.avif" type("image/avif"), "kitten.jpg" type("image/jpeg") ); }

The next-gen image goes first while the fallback image for older browsers goes second. Only one image will be downloaded. If the browser doesn’t support AVIF it will ignore it and only download the second image you specify. If AVIF is supported, the fallback image is ignored.

In the above example we used an AVIF image and provided a JPEG as a fallback, but the fallback could be any widely supported image format. Here’s an example using a PNG.

.div2 { background-image: image-set( "puppy.webp" type("image/webp"), "puppy.png" type("image/png") ); }

In Chromium and Safari, specifying the type is not supported yet. That means you can use image-set today only to specify different resolutions of widely-supported image formats but not to add backwards-compatibility when using WebP or AVIF in those browsers. It should be possible to provide both multiple resolutions and multiple image formats, if you are so inclined:

.div2 { background-image: image-set( "puppy.webp" type("image/webp") 1x, "puppy2x.webp" type("image/webp") 2x, "puppy.png" type("image/png") 1x, "puppy2x.png" type("image/png") 2x ); } CodePen Embed Fallback

Hopefully browser support will improve soon.

Using <picture> for backgrounds instead

Maybe you don’t need background-image at all. If you want to use modern image formats, you might be able to use the <picture> element, which has better browser support. If you set the image to position: absolute it’s easy to display other elements on top of it.

CodePen Embed Fallback

As an alternative approach to using position: absolute, CSS grid is another easy way to overlap HTML elements.

CodePen Embed Fallback

The post Using Performant Next-Gen Images in CSS with image-set appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

“Weak declaration”

Css Tricks - Wed, 06/23/2021 - 4:27am

PPK looks at aspect-ratio, a CSS property for layout that, for the most part, does exactly what you would think it does. It’s getting more interesting as it’s behind a flag in Firefox and Safari now, so we’ll have universal support pretty darn soon. I liked how he called it a “weak declaration” which I’m fairly sure isn’t an official term but a good way to think about it.

This will produce a 16 / 9 element:

.movie-card { aspect-ratio: 16 / 9; }

This will too:

.movie-card { width: 50%; aspect-ratio: 16 / 9; }

But this won’t:

.movie-card { width: 150px; height: 150px; aspect-ratio: 16 / 9; }

Because you’ve explicitly set the height and width, that is what will be respected. The aspect-ratio is weak in that it will never override a dimension that is set in any other way.

And it’s not just height and width, it could be max-height that takes effect, so maybe the element follows the aspect ratio sometimes, but will always respect a max-* value and break the aspect ratio if it has to.

It’s so weak that not only can other CSS break the aspect ratio, but content inside the element can break it as well. For example, if you’ve got a ton of text inside an element where the height is only constrained by aspect-ratio, it actually won’t be constrained; the content will expand the element.

I think this is all… good. It feels intuitive. It feels like good default behavior that prevents unwanted side effects. If you really need to force an aspect ratio on a box with content, the padding trick still works for that. This is just a much nicer syntax that replaces the safe ways of doing the padding trick.

PPK’s article gets into aspect-ratio behavior in flexbox and grid, which definitely has some gotchas. For example, if you are doing align-content: stretch;—that’s one of those things that can break an aspect ratio. Like he said: weak declaration.

The post “Weak declaration” appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

inherit, initial, unset, revert

Css Tricks - Tue, 06/22/2021 - 11:20am

There are four keywords that are valid values for any CSS property (see the title). Of those, day to day, I’d say I see the inherit used the most. Perhaps because it’s been around the longest (I think?) but also because it makes logical sense (“please inherit your value from the next parent up that sets it”). You might see that with an override of a link color, for example.

<footer> ©2012 Website — <a href="/contact">Contact</a> </footer> /* General site styles */ a { color: blue; } footer { color: white; } footer a { color: inherit; }

That’s a decent and elegant way to handle the fact that you want the text and links in the footer to be the same color without having to set it twice.

The others behave differently though…

  • initial will reset the property back to the spec default.
  • unset is weird as heck. For a property that is inherited (e.g. color) it means inherit, and for a property that isn’t inherited (e.g. float) it means initial. That’s a brain twister for me such that I’ve never used it.
  • revert is similarly weird. Same deal for inherited properties, it means inherit. But for non-inherited properties it means to revert to the UA stylesheet. Kinnnnnda useful in that reverting display, for example, won’t make a <p> element display: inline; but it will remain a sensible display: block;.

PPK covered all this in more detail.

I’m glad he found my whining about all this:

Chris Coyier argues we need a new value which he calls default. It reverts to the browser style sheet in all cases, even for inherited properties. Thus it is a stronger version of revert. I agree. This keyword would be actually useful.

Amen. We have four properties for fiddling with the cascade on individual properties, but none that allow us to blast everything back to the UA stylesheet defaults. If we had that, we’d have a very powerful tool for starting fresh with styles on any given element. In one sense: scoped styles!

PPK has a fifth value he thinks would be useful: cascade. The idea (I suppose) is it kinda acts like currentColor except for any property. Sort of like a free variable you don’t have to define that gives you access to what the cascaded value would have been, except you’re going to use it in some other context (like a calculation).

The post inherit, initial, unset, revert appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Using Custom Elements in Svelte

Css Tricks - Tue, 06/22/2021 - 4:37am

Svelte fully supports custom elements (e.g. <my-component>) without any custom configuration or wrapper components and has a perfect score on Custom Elements Everywhere. However, there are still a few quirks you need to watch out for, especially around how Svelte sets data on custom elements. At Alaska Airlines, we experienced many of these issues first-hand as we integrated the custom elements from our design system into a Svelte application.

While Svelte supports compiling to custom elements, that is not within the scope of this post. Instead, I will focus on using custom elements built with the Lit custom element library in a Svelte application. These concepts should transfer to custom elements built with or without a supporting library.

Property or attribute?

To fully understand how to use custom elements in Svelte, you need to understand how Svelte passes data to a custom element.

Svelte uses a simple heuristic to determine whether to pass data to a custom element as a property or an attribute. If a corresponding property exists on the custom element at runtime, Svelte will pass the data as a property. Otherwise, it will pass it as an attribute. This seems simple, but has interesting implications.

For instance, let’s say you have a coffee-mug custom element that takes a size property. You can use it in a Svelte component like so:

<coffee-mug class="mug" size="large"></coffee-mug>

You can open this Svelte REPL to follow along. You should see the custom element render the text “This coffee mug’s size is: large ☕️.”

When writing the HTML inside the component, it seems like you’re setting both class and size as attributes. However, this is not the case. Right-click on the “This coffee mug’s size is” text in the REPL’s output and click “Inspect.” This will bring open the DevTools inspector. When you inspect the rendered HTML, you’ll notice that only class was set as an attribute — it’s as if size simply disappeared! However, size is getting set somehow, because “large” still appears in the element’s rendered text.

This is because size is a property on the element, but class is not. Because Svelte detects a size property, it chooses to set that property instead of an attribute. There is no class property, so Svelte sets it as an attribute instead. That’s not a problem or something that changes how we expect the component to behave, but can be very confusing if you’re unaware of it, because there’s a disconnect between the HTML you think you’re writing and what Svelte actually outputs.

Svelte isn’t unique in this behavior — Preact uses a similar method to determine whether to set an attribute or a property on custom elements. Because of that, the use cases I discuss will also occur in Preact, though the workarounds will be different. You will not run into these issues with Angular or Vue because they have a special syntax that lets you choose to set an attribute or a property.

Svelte’s heuristic makes it easy to pass complex data like arrays and objects which need to be set as properties. Consumers of your custom elements shouldn’t need to think about whether they need to set an attribute or a property — it just magically works. However, like any magic in web development, you eventually run into some cases that require you to dig a little deeper and understand what’s going on behind the scenes.

Let’s go through some use cases where custom elements behave strangely. You can find the final examples in this Svelte REPL.

Attributes used as styling hooks

Let’s say you have a custom-text element that displays some text. If the flag attribute is present, it prepends a flag emoji and the word “Flagged:” to the text. The element is coded as follows:

import { html, css, LitElement } from 'lit'; export class CustomText extends LitElement { static get styles() { return css` :host([flag]) p::before { content: '&#x1f6a9;'; } `; } static get properties() { return { flag: { type: Boolean } }; } constructor() { super(); this.flag = false; } render() { return html`<p> ${this.flag ? html`<strong>Flagged:</strong>` : ''} <slot></slot> </p>`; } } customElements.define('custom-text', CustomText);

You can see the element in action in this CodePen.

However, if you try to use the custom element the same way in Svelte, it doesn’t entirely work. The “Flagged:” text is shown, but the emoji is not. What gives?

<script> import './custom-elements/custom-text'; </script> <!-- This shows the "Flagged:" text, but not &#x1f6a9; --> <custom-text flag>Just some custom text.</custom-text>

The key here is the :host([flag]) selector. :host selects the element’s shadow root (i.e. the <custom-text> element), so this selector only applies if the flag attribute is present on the element. Since Svelte chooses to set the property instead, this selector doesn’t apply. The “Flagged:” text is added based on the property, which is why that still showed.

So what are our options here? Well, the custom element shouldn’t have assumed that flag would always be set as an attribute. It is a custom element best practice to keep primitive data attributes and properties in sync since you don’t know how the consumer of the element will interact with it. The ideal solution is for the element author to make sure any primitive properties are reflected to attributes, especially if those attributes are used for styling. Lit makes it easy to reflect your properties:

static get properties() { return { flag: { type: Boolean, reflect: true } }; }

With that change, the flag property is reflected back to the attribute, and everything displays as expected.

However, there may be cases where you don’t have control over the custom element definition. In that case, you can force Svelte to set the attribute using a Svelte action.

Using a Svelte action to force setting attributes

Actions are a powerful Svelte feature that run a function when a certain node is added to the DOM. For example, we can write an action that will set the flag attribute on our custom-text element:

<script> import './custom-elements/custom-text'; function setAttributes(node) { node.setAttribute('flag', ''); } </script> <custom-text use:setAttributes> Just some custom text. </custom-text>

Actions can also take parameters. For instance, we could make this action more generic and accept an object containing the attributes we want to set on a node.

<script> import './custom-elements/custom-text'; function setAttributes(node, attributes) { Object.entries(attributes).forEach(([k, v]) => { if (v !== undefined) { node.setAttribute(k, v); } else { node.removeAttribute(k); } }); } </script> <custom-text use:setAttributes={{ flag: true }}> Just some custom text. </custom-text>

Finally, if we want the attributes to react to state changes, we can return an object with an update method from the action. Whenever the parameters we pass to the action change, the update function will be called.

<script> import './custom-elements/custom-text'; function setAttributes(node, attributes) { const applyAttributes = () => { Object.entries(attributes).forEach(([k, v]) => { if (v !== undefined) { node.setAttribute(k, v); } else { node.removeAttribute(k); } }); }; applyAttributes(); return { update(updatedAttributes) { attributes = updatedAttributes; applyAttributes(); } }; } let flagged = true; </script> <label><input type="checkbox" bind:checked={flagged} /> Flagged</label> <custom-text use:setAttributes={{ flag: flagged ? '' : undefined }}> Just some custom text. </custom-text>

Using this approach, we don’t have to update the custom element to reflect the property — we can control setting the attribute from inside our Svelte app.

Lazy-loading custom elements

Custom elements are not always defined when the component first renders. For example, you may wait to import your custom elements until after the web component polyfills have loaded. Also, in a server-side rendering context such as Sapper or SvelteKit, the initial server render will take place without loading the custom element definition.

In either case, if the custom element is not defined, Svelte will set everything as attributes. This is because the property does not exist on the element yet. This is confusing if you’ve grown accustomed to Svelte only setting properties on custom elements. This can cause issues with complex data such as objects and arrays.

As an example, let’s look at the following custom element that displays a greeting followed by a list of names.

import { html, css, LitElement } from 'lit'; export class FancyGreeting extends LitElement { static get styles() { return css` p { border: 5px dashed mediumaquamarine; padding: 4px; } `; } static get properties() { return { names: { type: Array }, greeting: { type: String } }; } constructor() { super(); this.names = []; } render() { return html`<p> ${this.greeting}, ${this.names && this.names.length > 0 ? this.names.join(', ') : 'no one'}! </p>`; } } customElements.define('fancy-greeting', FancyGreeting);

You can see the element in action in this CodePen.

If we statically import the element in a Svelte application, everything works as expected.

<script> import './custom-elements/fancy-greeting'; </script> <!-- This displays "Howdy, Amy, Bill, Clara!" --> <fancy-greeting greeting="Howdy" names={['Amy', 'Bill', 'Clara']} />

However, if we dynamically import the component, the custom element does not become defined until after the component has first rendered. In this example, I wait to import the element until the Svelte component has been mounted using the onMount lifecycle function. When we delay importing the custom element, the list of names is not set properly and the fallback content is displayed instead.

<script> import { onMount } from 'svelte'; onMount(async () => { await import('./custom-elements/fancy-greeting'); }); </script> <!-- This displays "Howdy, no one!"--> <fancy-greeting greeting="Howdy" names={['Amy', 'Bill', 'Clara']} />

Because the custom element definition is not loaded when Svelte adds fancy-greeting to the DOM, fancy-greeting does not have a names property and Svelte sets the names attribute — but as a string, not as a stringified array. If you inspect the element in your browser DevTools, you’ll see the following:

<fancy-greeting greeting="Howdy" names="Amy,Bill,Clara"></fancy-greeting>

Our custom element tries to parse the names attribute as an array using JSON.parse, which throws an exception. This is handled automatically using Lit’s default array converter, but the same would apply to any element that expects an attribute to contain a valid JSON array.

Interestingly, once you update the data passed to the custom element Svelte will start setting the property again. In the below example, I moved the array of names to the state variable names so that I can update it. I also added an “Add name” button that will append the name “Rory” to the end of the names array when clicked.

Once the button is clicked, the names array is updated, which triggers a re-render of the component. Since the custom element is now defined, Svelte detects the names property on the custom element and sets that instead of the attribute. This causes the custom element to properly display the list of names instead of the fallback content.

<script> import { onMount } from 'svelte'; onMount(async () => { await import('./custom-elements/fancy-greeting'); }); let names = ['Amy', 'Bill', 'Clara']; function addName() { names = [...names, 'Rory']; } </script> <!-- Once the button is clicked, the element displays "Howdy, Amy, Bill, Clara, Rory!" --> <fancy-greeting greeting="Howdy" {names} /> <button on:click={addName}>Add name</button>

As in the previous example, we can force Svelte to set the data how we want using an action. This time, instead of setting everything as an attribute, we want to set everything as a property. We will pass an object as a parameter that contains the properties we want to set on the node. Here’s how our action will be applied to the custom element:

<fancy-greeting greeting="Howdy" use:setProperties={{ names: ['Amy', 'Bill', 'Clara'] }} />

Below is the the implementation of the action. We iterate over the properties object and use each entry to set the property on the custom element node. We also return an update function so that the properties are reapplied if the parameters passed to the action change. See the previous section if you want a refresher on how you can react to state changes with an action.

function setProperties(node, properties) { const applyProperties = () => { Object.entries(properties).forEach(([k, v]) => { node[k] = v; }); }; applyProperties(); return { update(updatedProperties) { properties = updatedProperties; applyProperties(); } }; }

By using the action, the names are displayed properly on first render. Svelte sets the property when first rendering the component, and the custom element picks that property up once the element has been defined.

Boolean attributes

The final issue we ran into is how Svelte handles boolean attributes on a custom element. This behavior has recently changed with Svelte 3.38.0, but we’ll explore pre- and post-3.38 behavior since not everyone will be on the latest Svelte version.

Suppose we have a <secret-box> custom element with a boolean property open that indicates whether the box is open or not. The implementation looks like this:

import { html, LitElement } from 'lit'; export class SecretBox extends LitElement { static get properties() { return { open: { type: Boolean } }; } render() { return html`<div>The box is ${ ? 'open &#x1f513;' : 'closed &#x1f512;'}</div>`; } } customElements.define('secret-box', SecretBox);

You can see the element in action in this CodePen.

As seen in the CodePen, you can set the open property to true multiple ways. Per the HTML spec, the presence of a boolean attribute represents the true value, and its absence represents false.

<secret-box open></secret-box> <secret-box open=""></secret-box> <secret-box open="open"></secret-box>

Interestingly, only the last of the above options shows “The box is open” when used inside a Svelte component. The first two show “The box is closed” despite setting the open attribute. What’s going on here?

As with the other examples, it all goes back to Svelte choosing properties over attributes. If you inspect the elements in the browser DevTools, no attributes are set — Svelte has set everything as properties. We can console.log the open property inside our render method (or query the element in the console) to discover what Svelte set the open property to.

// <secret-box open> logs '' // <secret-box open=""> logs '' // <secret-box open="open"> logs 'open' render() { console.log(; return html`<div>The box is ${ ? 'open &#x1f513;' : 'closed &#x1f512;'}</div>`; }

In the first two cases, open equals an empty string. Since an empty string is falsy in JavaScript, our ternary statement evaluates to the false case and shows that the box is closed. In the final case, the open property is set to the string “open” which is truthy. The ternary statement evaluates to the true case and shows that the box is open.

As a side note, you don’t run into this issue when you lazy load the element. Since the custom element definition is not loaded when Svelte renders the element, Svelte sets the attribute instead of the property. See the above section for a refresher.

There’s an easy way around this issue. If you remember that you’re setting the property, not the attribute, you can explicitly set the open property to true with the following syntax.

<secret-box open={true}></secret-box>

This way you know you’re setting the open property to true. Setting to a non-empty string also works, but this way is the most accurate since you’re setting true instead of something that happens to be truthy.

Until recently, this was the only way to properly set boolean properties on custom elements. However, with Svelte 3.38, I had a change released that updated Svelte’s heuristic to allow setting shorthand boolean properties. Now, if Svelte knows that the underlying property is a boolean, it will treat the open and open="" syntaxes the same as open={true}.

This is especially helpful since this is how you see examples in many custom element component libraries. This change makes it easy to copy-paste out of the docs without having to troubleshoot why a certain attribute isn’t working how you’d expect.

However, there is one requirement on the custom element author side — the boolean property needs a default value so that Svelte knows it’s of boolean type. This is a good practice anyway if you want that property to be a boolean.

In our secret-box element, we can add a constructor and set the default value:

constructor() { super(); = true; }

With that change, the following will correctly display “The box is open” in a Svelte component.

<secret-box open></secret-box> <secret-box open=""></secret-box> Wrapping up

Once you understand how Svelte decides to set an attribute or a property, a lot of these seemingly strange issues start to make more sense. Any time you run into issues passing data to a custom element inside a Svelte application, figure out if it’s being set as an attribute or a property and go from there. I’ve given you a few escape hatches in this article to force one or the other when you need to, but they should generally be unnecessary. Most of the time, custom elements in Svelte just work. You just need to know where to look if something does go wrong.

Special thanks to Dale Sande, Gus Naughton, and Nanette Ranes for reviewing an early version of this article.

The post Using Custom Elements in Svelte appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

If we’re gonna criticize utility-class frameworks, let’s be fair about it

Css Tricks - Tue, 06/22/2021 - 4:33am

I’m not here to raise a shield protecting CSS utility frameworks. I don’t even particularly like the approach, myself, and nothing is above fair criticism. But fair is a key word there. I can’t tell you how many times I’ve seen utility styles compared to inline styles. Sarah Dayan is weary of it:

[…] despite numerous attempts at debunking common fallacies, utility-first enthusiasts keep on having to reply to a staggering amount of misconceptions. And by far, the most tired, overused cliché is that utility classes are just inline styles.

I think this comparison will make it clear:

<div style="color: #3ea8ca;"></div> <div class="color-blue"></div>

The first div has a color set directly in the HTML that is an extremely specific blue color value. The second has a color that is set outside of the HTML, using class name you can use to configure the shade of blue in CSS. Sure, that second one is a fairly limited class name in that, as the name suggests, does one job, but it still offers some abstraction in that the blue color can be changed without changing the markup. It’s the same story with a sizing utility class, say size-xl. That’s also an abstraction we could use to define the padding of an element in CSS using that class name as a selector. But if we were to use style="padding: 10px;" directly on the element in the HTML, that is an absolute that requires changing the value in the markup.

To be fair though (which is what we’re after), there are quite a few classes in utility frameworks that are named in such a way that they are extremely close acting like inline styles. For example, top-0 in Tailwind means top: 0 and there is no configuration or abstraction about it. It’s not like that class will be updated in the CSS with any value other than zero because it’s in the name. “Utility” is a good way to describe that. It is very much like an inline style.

All that configurable-with-smart-defaults stuff puts utility-based frameworks in a different category. Inline styles offer no constraints on how you style things (other than hard limitations, like no pseudo selectors or media queries), while a limited set of utility classes offer quite a lot of styling constraints. Those constraints are often desirable in that they lead to a design that looks consistent and nice instead of inconsistent and sloppy.

To borrow a metaphor I heard in a slightly different context one time: Utility-class frameworks are like bumper bowling for styling. Use the classes and it’ll work out fine. You might not get a strike, but you won’t get a gutter ball either.

Another unfair criticism I hear in conversation about utility frameworks is that you ship way more CSS with them. If you are, then you’re definitely screwing up. In my mind, the main point of this approach is shipping less CSS (only the classes you use). I’m the first to tell you that a build process that accurately and perfectly does this is tricky and could lead to an unhealthy amount of technical debt, but I’ll cede that if you do it right, shipping less CSS is good for performance. Tailwind in particular highly encourages and helps you do this.

So all that said, I think there is all sorts of stuff to criticize about the approach. For example, I personally don’t like looking at all those classes. I just don’t. I’m not an absolutist about perfectly abstract classes, but seeing 10-20 classes on div after div gets in the way of what I’m trying to do when I’m templating HTML. It feels harder to refactor. It feels harder to see what’s going on semantically. It’s harder to parse that list for other classes that I need to do non-styling things. Some of the advantages that I would get from utilities, like scoping styles to exactly where I need them, I often get through other tooling.

I also think utility-frameworks work best in JavaScript component setups where you have Hot Module Reloading. Otherwise, HTML changes tend to trigger entire page refreshes. For example, a tool like Browsersync is pretty darn nice. It does CSS injection when your CSS changes. But it can’t do new-HTML injection; it just refreshes the page. So without Hot Module Reloading, which generally ain’t for your generic HTML site or Static Site Generator, you get worse DX while authoring.

The post If we’re gonna criticize utility-class frameworks, let’s be fair about it appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Are we in a new era of web design? What do we call it?

Css Tricks - Mon, 06/21/2021 - 11:22am

Una is calling it the new responsive. A nod to the era we were most certainly in, the era of responsive design. Where responsive design was fluid grids, flexible media, and media queries, the new responsive is those things too, but slotted into a wider scope: user preference queries, viewport and form factor, macro layouts, and container styles.

I like the thinking and grouping here and I kinda like the name. It alludes to an evolution and extension of responsive web design rather than a rejection and replacement.

This isn’t the first crack at identifying and naming a shift between eras. Back in 2018, Jen Simmons was doing a talked called “Everything You Know About Web Design Just Changed” where she identified that responsive design was a major shift in how we did layout on the web. And yet, it was firmly defined in an era where layout tools like flexbox and grid didn’t even exist. Now, they do exist, and with them a bevy of other new features that bring more capable graphic design to the web. She called it Intrinsic Design.

I almost like Intrinsic Design more now than I did in 2018, because now, if we attempt to lump in @container queries, the name makes more intuitive sense. We (hopefully will soon) make styling choices based on the intrinsic size of elements. We make styling choices based on the intrinsic nature of the individual users we serve. We make styling choices off the intrinsic qualities of the browser.

I wouldn’t say either of the terms have really caught on though. It’s hard to make a name stick. That little burst of ideating around CSS4 sure didn’t go anywhere.

Direct Link to ArticlePermalink

The post Are we in a new era of web design? What do we call it? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Links on Typography

Css Tricks - Fri, 06/18/2021 - 3:44am
  • I studied the fonts of the top 1000 websites. Here’s what I learned. — Michael Li brings the data. San-serif has total dominance. “[…] it is rare to go below 10px or above 24px.” And poor <h5> always being the same size as <p>. Makes me feel extra sad for <h6>, are you destined to be smaller than body copy?
  • How tracking and kerning improves all caps text — Oliver Schöndorfer gets into why ALL CAPS text generally looks better when spaced out (i.e. letter-spacing in CSS). I’m not exactly a renowned typographer, but this tracks with what I’ve always felt. All-caps looks good spaced out (sometimes quite a bit), and conversely, it almost never looks good to track out lowercase. Like the PG version of the famous quote.
  • Leveraging System Fonts on the Web — Jim Nielsen shared some of my confusion with “system fonts” in CSS. Like we have system-ui now, which I use pretty often because it actually works in Chrome and Safari for selecting the system font (i.e. getting to use San Francisco on macOS). Before that was a thing, to leverage the same kind of thing, you’d do a big long stack. But we kinda still need the stack for real production sites, since system-ui isn’t universally supported. There is a nice world going forward though, because we’re getting ui-sans-serif, ui-sans-serif, ui-monospace, and ui-rounded. Browser support is quite limited, but it’s gonna be nice.
  • Simpler Font Licensing: Introducing V2 — “The core of V2 is this: you buy a font, and then you can use it.” God bless ’em. As the owner of several sites that get a lot of page views but don’t have waterfalls of cash as a budget, I need web font pricing that is sane.
  • 5 steps to faster web fonts — Iain Bean goes through the biggies: WOFF2, font-display, <link rel="preload" ...>, Subsetting, and self-hosting. And never forget about the holy bible.
  • A New Way To Reduce Font Loading Impact: CSS Font Descriptors — Barry Pollard looks at new CSS descriptors (not just regular properties as they only work within @font-face blocks) like size-adjust and ascent-override. YES. THIS IS AWESOME. I declared perfect font fallbacks one of the all-time great CSS tricks, but alas, it required a smidge of JavaScript and hackery. This brings it all into CSS perfectly.
  • Why You Should Stop Using Times New Roman — Vanessa Hill was asked to reformat a research paper into Times New Roman. She was told it’s because it’s so readable and researches the truth: it’s not. Personally, I can’t stand the look of it because it looks like your font-stack failed in CSS.
  • Pixel font converter! — Looks like an ancient tool (via Remy Sharp) but I love it as it opens up the idea of creating a font to anyone who knows how to draw some letters and save a static image.

The post Links on Typography appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Start Serving Optimized Images in Vue

Css Tricks - Thu, 06/17/2021 - 4:38am

Images have become extremely important to the effectiveness of websites. They speak a 1000 words, attract attention, and stimulate emotions.

However, web performance is also a growing problem for most websites. And images are at the heart of many web performance issues. According to HTTP Archive, images are at least 50% of total website payloads.

If you have ever tried to lower the size of the images, then you know how painful the process is. Poorly optimized images can end up blurry or pixelated.

Thankfully, today we have a large number of external services just for this purpose. We will talk about the ImageEngine image CDN and how to implement it using Vue.js. But first, let’s learn about CDNs and ImageEngine.

What is an Image CDN?

A content delivery network (CDN) is a global network of servers that optimizes web performance by using the node closest to the user for faster delivery of assets. An Image CDN adds real-time image optimization prior to delivering images from the CDN. An Image CDN decreases image payload, and instantly sends images from the edge of the network. The result is faster page loading that drives higher SEO ranking and better user experience.

What is ImageEngine?

ImageEngine is an image CDN with automatic, real-time image optimization. What makes ImageEngine stand out from the crowd is the way ImageEngine analyses the request to dynamically provide a visually high-quality image at the lowest possible byte size based on the device or browser’s capabilities. ImageEngine is the only image CDN utilizing mobile device detection to specifically optimize images according to the requesting device or browsers capabilities.

With ImageEngine, You don’t need to worry about URL parameters with quirky syntax or JavaScript libraries. Instead, you can just prefix the image URL with an ImageEngine Delivery Address, and the service will automatically detect the mobile device, optimize the image appropriately, and deliver the most efficient image with good quality possible.

We will show you how to use ImageEngine CDN inside our Vue.js application to deliver optimized images. What’s great is that you don’t need to re-upload images to use the service. You only need to point ImageEngine service at the existing storage location of images (or, as ImageEngine calls this an “Origin”).

Integrating ImageEngine Into Your Website for Better Web Performance

If you’d like to follow along, sign up for a free trial account before moving on. After signing up for a trial account, you’ll get access to a control panel where you can further configure ImageEngine for your website or app.

I’ve built a demo app to demonstrate how effortless the integration is at the app level. It takes only a few easy steps to get rolling.

Step 1 – Point ImageEngine at Original Images

Before we get into the code, you will need to point ImageEngine’s “Engine” at the host where the images are stored. This can be your website, Amazon S3, or Google Cloud Storage.

As you can see in the above screenshot of the Dashboard configuration. We added the origin (named “default”) with host pointing to our demo sandbox Vue application

We don’t need to re-upload images to the CDN or anywhere else. If you take a look at the demo project’s structure, you’ll see that all the pictures sit inside the public/images folder:

ImageEngine will automatically take these pictures in the public/images folder, optimize them, and serve the optimized images from its own CDN. And with the Vue component we will show you, we don’t need to change URLs inside our code either.

Step 2 – Proxy Image Requests inside Vue application

Let’s start with Vue.js implementation. You have two implementation options. Both involve adding the Delivery Address ImageEngine provided. Your Delivery Address should end in, so have it copied and ready for use.

Once you have your Delivery Address we can proceed with code implementation. Our Example Delivery Address is

Demo app on sandbox Option 1: Prefix Image Tags

To send image requests to ImageEngine, you will need to prepend the existing image src path with the new Delivery Address found in your account’s dashboard.

<img :src="imgengHostConst + '/images/pic_2.jpg'" /> Option 2: Use ImageEngine Vue Component (Recommended)

We need to install the npm package @imageengine/vue and add it as a dependency.

In the terminal, write:

npm i @imageengine/vue

You have everything set, we are ready to do some coding.

You will need to import the ImageEngineProvider component from the npm package and pass the Delivery Address as the property deliveryAddress, and wrap it around your application or image components.

<ImageEngineProvider deliveryAddress=""> …APP… </ImageEngineProvider>

Replace all HTML image elements with ImageComponent components.

For example:

<img src="/images/pic_2.jpg"/> <!-- Changes to --> <ImageComponent src="/images/pic_2.jpg" />

The image component requires only the src property to be passed in. Optionally, you can pass the srcSet and directives properties which give you more control of your image optimization. Using directives, you can set output format, width, height, compression and more.

After using the ImageEngineProvider component, your images are optimized and delivered through the ImageEngine CDN.

You can see all image directives, properties and formats that you can pass that impact compression and optimization on the npm package documentation page.

Below is a table that shows the image format conversion and image payload reduction that ImageEngine provided for our sample app

Image NameOriginal FormatFormat With ImageEngineOriginal SizeSize after ImageEnginePayload Reductionpic_2.jpgJPEGWebP4.8 MB570 kB88%pic_2.jpgJPEGWebP3.3 MB141 kB96%pic_1_variation_1.jpgJPEGWebP2.8 MB72 kB97% Step 3 (optional). Best Practices And Customization

To get the most out of ImageEngine, consider going through their list of best practices. In case you need to tweak your images (resizing, cropping, etc.), ImageEngine lets you apply manipulations via directives.

Code example using directives:

<ImageComponent src="images/pic_2.jpg" :directives="{ outputFormat: 'webp', rotate: 45 }" />

Using directives attributes, we specified an outputFormat of WebP and rotated the image 45 degrees. You can combine directives, use when you need, and where you need. It is fully dynamic.

In most cases, ImageEngine’s automatic default settings generate impressive results, so directives are usually not needed except for some customized requirements.


When you look at the effort invested vs. the web performance gains, ImageEngine is one of the few examples where the quality of service really gets what you want. You no longer need to worry about image size, format, resolution, etc… We made a separate package for Vue.js developers because we know how important it is to have good CDN support for the Vue.js framework. You can start using our service inside Vue.js applications in just a few steps.

Give your web/app visitors the best experience they deserve.

The post Start Serving Optimized Images in Vue appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Inline Styles as Classes (lol)

Css Tricks - Wed, 06/16/2021 - 11:11am

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

I've revolutionized CSS.

— Sam Thorogood (@samthor) June 10, 2021

OK lemme dig in and totally ruin the joke.

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

Here’s all that playing around:

CodePen Embed Fallback

The post Inline Styles as Classes (lol) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Useful and Useless Code Comments

Css Tricks - Wed, 06/16/2021 - 9:08am

Jim Nielsen:

If somebody says a comment isn’t adding any value, I would ask: to whom?

Personally, I’ve never liked the advice that writing obvious comments is bad practice—probably because I write obvious comments all the time.

Jim showed off some examples of “code comments that are at the same level of fidelity as the code itself.” Those are the hardest calls with code comments.

// this function adds two numbers function add(a, b) { return a + b; }

Easy to point at that and call it not useful. I tend not to leave this type of comment, but it’s fair play for Jim to question that. Comments can be used for a wide swath of people whom may at some point interact with that code, so why gate-keep it?

[…] comments can serve a very different purpose when they’re being read vs. when they’re being written. Those are almost two different kinds of activities.

I’d add they serve a different purpose when re-visiting old code vs actively working. Also different when you’re trying to code review versus directly contribute.

Direct Link to ArticlePermalink

The post Useful and Useless Code Comments appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Detect Unused Classes in… HTML

Css Tricks - Tue, 06/15/2021 - 1:24pm

Usually, when “unused” comes up in conversation regarding CSS, it’s about removing chunks of CSS that are not used in your site or, at least, the styles not currently in use on a specific page. The minimal amount of CSS is best! I’ve written about how this is a hard problem in the past. In JavaScript-land, the equivalent is tree shaking (removing unusued JavaScript).

But what about the other way around, detecting classes in HTML that aren’t used in your CSS? If you knew this for sure, you could clean up your markup, removing classes that don’t do anything.

I saw Robert Kieffer post a Gist the other day with an interesting solution. The idea is to load up document.styleSheets and find all the rules (the ones that are classes). Then, use a MutationObserver to watch the DOM for all HTML, and check the classList of each node to see if it matches any from any stylesheet. If the HTML has a class not found in a stylesheet, report it.

I gave it a quick whirl and got it working and correctly reporting unused classes:

Your mileage may vary. For one thing, this script is set up as an ES Module. That means if you just import it and run it on a regular ol’ HTML document, it won’t find anything because your <script type="module"> is deferred and the MutationObserver won’t pick anything up. I just un-moduled it and put it in the <head> to make my demo work.

I Netlify Dropped the site online in case you wanna dig into it and check it out. I would have used CodePen, but CodePen doesn’t link up your styles as <link>ed stylesheets (by default, but you could use external resources to do that). I just thought it would be more clear as a deployed site.

Careful now.

Just like unused CSS is a hard problem because of how hard it is to know for sure know if a ruleset is unused, this approach may be an even harder problem. For one thing, classes might be used as a JavaScript hook for things. Styles might get injected onto the page in <style> blocks, which this script wouldn’t check. Heck, you might have integration tests that run in CI that use classes to do testing-related things.

I’d say this kind of thing is a useful tool for havin’ a looksie at classes that you have a hunch might be unused. But I wouldn’t say there’s a permission slip to run this thing and then yank out every reported class without further investigation.

The post Detect Unused Classes in… HTML appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Media Queries in Times of @container

Css Tricks - Tue, 06/15/2021 - 9:51am

Max Böck took me up on my challenge to look through a codebase and see how many of the @media queries could ultimately become @container queries.

I took the bait and had a look at some of my projects – and yes, most of what I use @media for today can probably be accomplished by @container at some point. Nevertheless, I came up with a few scenarios where I think media queries will still be necessary.

Max didn’t say exactly how many would be replaced, but I got the impression it was 50/50ish.

A combination of both techniques will probably be the best way forward. @media can handle the big picture stuff, user preferences and global styles; @container will take care of all the micro-adjustments in the components themselves.

A perfect team!

I also think there will be a big difference between what we do when refactoring an existing CSS codebase to what we do when we are building from scratch. And it will be different what we do when we first get container queries to what we do years from now when new patterns have settled in. I’ve long been bullish on components being the right abstraction for front-end development. It feels like everything lately pushes us in that direction, from JavaScript frameworks and to native components, to container queries and style scoping.

Direct Link to ArticlePermalink

The post Media Queries in Times of @container appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Just How Niche is Headless WordPress?

Css Tricks - Tue, 06/15/2021 - 4:31am

I wonder where headless WordPress will land. And by “headless” I mean only using the WordPress admin and building out the user-facing site through the WordPress REST API rather than the traditional WordPress theme structure.

Is it… big? The future of WordPress? Or relatively niche?

Where’s the demand?

Certainly, there is demand for it. I know plenty of people doing it. For instance, Gatsby has a gatsby-source-wordpress plugin that allows you to source content from a WordPress site in a way that consumes the WordPress REST API and caches it as GraphQL for use in a React-powered Gatsby site. It has been downloaded 59k times this month and 851k times overall, as I write. That’s a healthy chunk of usage for one particular site-building technology. Literally, every use of gatsby-source-wordpress is using WordPress headlessly—that’s just what it is/does. If you’re interested in this, here’s Ganesh Dahal digging deep into it.

And that’s just one thing, it doesn’t factor in entire companies and products dedicated to the idea.

What is headless an improve to?

The Gatsby integration makes a solid case for why anyone would consider a headless WordPress site. I’ll get to that.

Many advocate the reason is architectural. It de-couples the back end from the front end. It tears down the monolith. As a de-coupled system, the back and front ends can evolve independently. And yet, I’m less hot on that idea as years go by. For example, I’d argue that the chances of simply ripping out WordPress and replace it with another CMS in a headless setup like this is easier said than done. Also, the idea that I’m going to use the WordPress API not just to power a website, but also a native reading app, and some digital internet-connected highway billboard or something is not a use case that’s exploding in popularity as far as I can tell.

The real reason I think people reach for a WordPress back end for a Gatsby-driven front end is essentially this: they like React. They like building things from components. They like the fast page transitions. They like being able to host things somewhere Jamstack-y with all the nice developer previews and such. They like the hot module reloading. They like Prettier and JSX. They just like it, and I don’t blame them. When you enjoy that kind of developer experience, going back to authoring PHP templates where it takes manually refreshing the browser and maintaining some kind of hand-rolled build process isn’t exactly enticing.

Frontity is another product looking to hone in on React + WordPress. Geoff and Sarah shared how to do all this last year on the Vue/Nuxt side.

But will headless WordPress become more popular than the traditional theming model of WordPress that’s based on PHP templates that align to an explicit structure? Nah. Well, maybe it would if WordPress itself champions the idea and offers guidance, training, and documentation that make it easier for developers to adopt that approach. I’d buy into it if WordPress says that a headless architecture is the new course of direction. But none of those things are true. Consequently, to some degree, it’s a niche thing.

Just how niche is headless?

WP Engine is a big WordPress host and has a whole thing called Atlas. And that effort definitely looks like they are taking this niche market seriously. I’m not 100% sure what Atlas all is, but it looks like a dashboard for spinning up sites with some interesting looking code-as-config. One of the elephants in the room with headless WordPress is that, well, now you have two websites to deal with. You have wherever you are hosting and running WordPress, and wherever you are hosting and running the site that consumes the WordPress APIs. Maybe this brings those two things together somehow. The deploy-from-Git-commits thing is appealing and what I’d consider table stakes for modern hosting these days.

Another reason people are into headless WordPress is that the end result can be static, as in, pre-generated HTML pages. That means the server for your website can be highly CDN-ized, as it’s literally only static assets that are instantly available to download. There’s no PHP or database for server-side rendering things, which can be slow (and, to be fair, dealt with) since it adds a process ahead of rendering.

What’s “the WordPress way” for going headless?

I’d lump any service that builds a static version of your WordPress site into the headless WordPress bucket. That’s because, ultimately, those sites are using WordPress APIs to build those static files, just like Gatsby or whatever else would do.

That’s what Strattic does. They spin up a WordPress site for you that they consider staging. You do your WordPress work there, then use their publish system to push a static version of your site to production. That’s compelling because it solves for something that other headless WordPress usage doesn’t: just doing things the WordPress way.

For example, custom WordPress blocks or plugins might produce output that not only contains custom HTML, but CSS and JavaScript as well. Imagine a “carousel” block or plugin. That carousel isn’t going to work if all you’re doing is grabbing the post content from an API and dunking it onto a page. You’ll either need to go extract the CSS and JavaScript from elsewhere and include it, or somehow just know that isn’t how you do things anymore. You might attach the images as metadata somehow, pull them client-side, and do your own implementation of a carousel. With Strattic, theoretically, it’ll work as the HTML, CSS, and JavaScript is still present on the static site. But notably, you don’t have PHP, so Strattic had to hand-build form integrations, they use client-side Algolia for search, Disqus for comments, etc., because there is no server-side language available.

Shifter is another player here. It’s similar to Strattic where you work on your site in the WordPress admin, then publish to a static site. I believe Shifter even spins your WordPress site down when it’s not in use, which makes sense since the output is static and there is no reason a server with PHP and MySQL needs to be running. As an alternative, Shifter has a headless-only WordPress setup that presumably stays spun up all the time for outside API usage.

It’s fun to think about all this stuff

But as I do, I realize that the ideas and discussions around headless WordPress are mostly focused on the developer. WordPress has this huge market of people who just are not developers. Yet, they administer a WordPress site, taking advantage of the plugin and theme ecosystem. That’s kinda cool, and it’s impressive that WordPress serves both markets so well. There’s just a heck of a lot more WordPress site owners who aren’t developers than those who are, I reckon, so that alone will keep headless WordPress from being anything more than a relatively niche concept for some time. But, ya know, if they wanna put GraphQL in core, I’ll still take it kthxbye.

Related articles Article on Apr 9, 2021 Headless Form Submission With the WordPress REST API Mészáros Róbert Article on Feb 4, 2020 How To Create A Headless WordPress Site On The Jamstack Geoff Graham Article on Jun 7, 2018 Headless CMS: The Developers’ Best Friend Geoff Graham Article on Oct 5, 2020 ooooops I guess we’re* full-stack developers now Chris Coyier Article on Mar 11, 2016 What is a Headless CMS? Chris Coyier

The post Just How Niche is Headless WordPress? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Making Tables With Sticky Header and Footers Got a Bit Easier

Css Tricks - Mon, 06/14/2021 - 12:04pm

It wasn’t long ago when I looked at sticky headers and footers in HTML <table>s in the blog post A table with both a sticky header and a sticky first column. In it, I never used position: sticky on any <thead>, <tfoot>, or <tr> element, because even though Safari and Firefox could do that, Chrome could not. But it could do table cells like <th> and <td>, which was a decent-enough workaround.

Well that’s changed.

I heard through the Twitter that Chrome "rewrote tables" in v91.

I saw it dropped, upgraded, and did a quick test. HEY LOOK STICKY TABLE HEADERS AND FOOTERS.@CodePen

(Works in Safari and Firefox too)

— Chris Coyier (@chriscoyier) June 4, 2021

Sounds like a big effort went into totally revamping tables in the rendering engine in Chromium, bringing tables up to speed. It’s not just the stickiness that was fixed, but all sorts of things. I’ll just focus on the sticky thing since that’s what I looked at.

The headline to me is that <thead> and <tfoot> are sticky-able. That seems like it will be the most common use case here.

table thead, table tfoot { position: sticky; } table thead { inset-block-start: 0; /* "top" */ } table tfoot { inset-block-end: 0; /* "bottom" */ } CodePen Embed Fallback

That works in all three major browsers. You might want to get clever and only sticky them at certain minimum viewport heights or something, but the point is it works.

I heard several questions about table columns as well. My original article had a sticky first column (that was kind of the point). While there is a table <col> tag, it’s… weird. It doesn’t actually wrap columns, it’s more like a pointer thing to be able to style down the column if you need to. I hardly ever see it used, but it’s there. Anyway, you totally can’t position: sticky; a <col>, but you can make sticky columns. You need to select all the cells in that column and stick them to the left or right. Here’s that using logical properties…

table tr th:first-child { position: sticky; inset-inline-start: 0; /* "left" */ }

Here’s a sorta obnoxious table where the <thead>, <tfoot>, and the first and last columns are all sticky.

CodePen Embed Fallback

I’m sure you could do something tasteful with this. Like maybe:

A `sticky` table row/column can be such a nice touch. Here's an example from @destroytoday

— Chris Coyier (@chriscoyier) June 2, 2021

The post Making Tables With Sticky Header and Footers Got a Bit Easier appeared first on CSS-Tricks.

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

CSS-Tricks Chronicle XXXX

Css Tricks - Mon, 06/14/2021 - 8:20am

Just a little link roundup of some off-site stuff I’ve done recently. As I’m wont to do from time to time.

DevJourney Podcast

#151 Chris Coyier from ceramics to CSS-Tricks and CodePen

Chris took us from playing on his first C64 to his bachelor of arts in ceramics and back to web development. We talked about the different positions he held along the way and how they slowly but surely led him toward web development. We brushed over the creation and recreation of CSS-Tricks, learning in the open and what a good day looks like.

Podrocket Podcast

Rocket Surgery: Kaelan and Chris Coyier compare notes

I was asked to remove the audio embed from here, you’ll have to go there to listen to it if you want to.

Are you up to speed on all of this new CSS stuff? Chris Coyier and Kaelan compare notes on CSS and frontend development (they also discuss MDN plus).

CodePen Radio

I’ve been back to hosting the episodes of CodePen Radio this year, every week. Here are some recent episodes.

I will randomly do the podcast as a video some weeks and call it CodePen Radio on TV! Here’s Adam Kuhn and I doing one of those videos. You’ll see a new intro animation on it that Adam himself did.

ShopTalk Show

Dave and I are six months away from doing this weekly for 10 years!! Again, some recent shows:

Some of the shows have been centered around a series called JavaScript in 2021.

The biggest change around ShopTalk is our Patreon + Discord which has been very awesome.

The post CSS-Tricks Chronicle XXXX appeared first on CSS-Tricks.

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

Securing Your Website With Subresource Integrity

Css Tricks - Mon, 06/14/2021 - 3:30am

When you load a file from an external server, you’re trusting that the content you request is what you expect it to be. Since you don’t manage the server yourself, you’re relying on the security of yet another third party and increasing the attack surface. Trusting a third party is not inherently bad, but it should certainly be taken into consideration in the context of your website’s security.

A real-world example

This isn’t a purely theoretical danger. Ignoring potential security issues can and has already resulted in serious consequences. On June 4th, 2019, Malwarebytes announced their discovery of a malicious skimmer on the website Due to a compromised Amazon S3 bucket, attackers were able to alter a JavaScript library to steal credit card information from customers.

It’s not only JavaScript that’s worth worrying about, either. CSS is another resource capable of performing dangerous actions such as password stealing, and all it takes is a single compromised third-party server for disaster to strike. But they can provide invaluable services that we can’t simply go without, such as CDNs that reduce the total bandwidth usage of a site and serve files to the end-user much faster due to location-based caching. So it’s established that we need to sometimes rely on a host that we have no control over, but we also need to ensure that the content we receive from it is safe. What can we do?

Solution: Subresource Integrity (SRI)

SRI is a security policy that prevents the loading of resources that don’t match an expected hash. By doing this, if an attacker were to gain access to a file and modify its contents to contain malicious code, it wouldn’t match the hash we were expecting and not execute at all.

Doesn’t HTTPS do that already?

HTTPS is great for security and a must-have for any website, and while it does prevent similar problems (and much more), it only protects against tampering with data-in-transit. If a file were to be tampered with on the host itself, the malicious file would still be sent over HTTPS, doing nothing to prevent the attack.

How does hashing work?

A hashing function takes data of any size as input and returns data of a fixed size as output. Hashing functions would ideally have a uniform distribution. This means that for any input, x, the probability that the output, y, will be any specific possible value is similar to the probability of it being any other value within the range of outputs.

Here’s a metaphor:

Suppose you have a 6-sided die and a list of names. The names, in this case, would be the hash function’s “input” and the number rolled would be the function’s “output.” For each name in the list, you’ll roll the die and keep track of what name each number number corresponds to, by writing the number next to the name. If a name is used as input more than once, its corresponding output will always be what it was the first time. For the first name, Alice, you roll 4. For the next, John, you roll 6. Then for Bob, Mary, William, Susan, and Joseph, you get 2, 2, 5, 1, and 1, respectively. If you use “John” as input again, the output will once again be 6. This metaphor describes how hash functions work in essence.

Name (input)Number rolled (output)Alice4John6Bob2Mary2William5Susan1Joseph1

You may have noticed that, for example, Bob and Mary have the same output. For hashing functions, this is called a “collision.” For our example scenario, it inevitably happens. Since we have seven names as inputs and only six possible outputs, we’re guaranteed at least one collision.

A notable difference between this example and a hash function in practice is that practical hash functions are typically deterministic, meaning they don’t make use of randomness like our example does. Rather, it predictably maps inputs to outputs so that each input is equally likely to map to any particular output.

SRI uses a family of hashing functions called the secure hash algorithm (SHA). This is a family of cryptographic hash functions that includes 128, 256, 384, and 512-bit variants. A cryptographic hash function is a more specific kind of hash function with the properties being effectively impossible to reverse to find the original input (without already having the corresponding input or brute-forcing), collision-resistant, and designed so a small change in the input alters the entire output. SRI supports the 256, 384, and 512-bit variants of the SHA family.

Here’s an example with SHA-256:

For example. the output for hello is:


And the output for hell0 (with a zero instead of an O) is:


You’ll notice that the slightest change in the input will produce an output that is completely different. This is one of the properties of cryptographic hashes listed earlier.

The format you’ll see most frequently for hashes is hexadecimal, which consists of all the decimal digits (0-9) and the letters A through F. One of the benefits of this format is that every two characters represent a byte, and the evenness can be useful for purposes such as color formatting, where a byte represents each color. This means a color without an alpha channel can be represented with only six characters (e.g., red = ff0000)

This space efficiency is also why we use hashing instead of comparing the entirety of a file to the data we’re expecting each time. While 256 bits cannot represent all of the data in a file that is greater than 256 bits without compression, the collision resistance of SHA-256 (and 384, 512) ensures that it’s virtually impossible to find two hashes for differing inputs that match. And as for SHA-1, it’s no longer secure, as a collision has been found.

Interestingly, the appeal of compactness is likely one of the reasons that SRI hashes don’t use the hexadecimal format, and instead use base64. This may seem like a strange decision at first, but when we take into consideration the fact that these hashes will be included in the code and that base64 is capable of conveying the same amount of data as hexadecimal while being 33% shorter, it makes sense. A single character of base64 can be in 64 different states, which is 6 bits worth of data, whereas hex can only represent 16 states, or 4 bits worth of data. So if, for example, we want to represent 32 bytes of data (256 bits), we would need 64 characters in hex, but only 44 characters in base64. When we using longer hashes, such as sha384/512, base64 saves a great deal of space.

Why does hashing work for SRI?

So let’s imagine there was a JavaScript file hosted on a third-party server that we included in our webpage and we had subresource integrity enabled for it. Now, if an attacker were to modify the file’s data with malicious code, the hash of it would no longer match the expected hash and the file would not execute. Recall that any small change in a file completely changes its corresponding SHA hash, and that hash collisions with SHA-256 and higher are, at the time of this writing, virtually impossible.

Our first SRI hash

So, there are a few methods you can use to compute the SRI hash of a file. One way (and perhaps the simplest) is to use, but if you prefer a more programmatic way, you can use:

sha384sum [filename here] | head -c 96 | xxd -r -p | base64
  • sha384sum Computes the SHA-384 hash of a file
  • head -c 96 Trims all but the first 96 characters of the string that is piped into it
    • -c 96 Indicates to trim all but the first 96 characters. We use 96, as it’s the character length of an SHA-384 hash in hexadecimal format
  • xxd -r -p Takes hex input piped into it and converts it into binary
    • -r Tells xxd to receive hex and convert it to binary
    • -p Removes the extra output formatting
  • base64 Simply converts the binary output from xxd to base64

If you decide to use this method, check the table below to see the lengths of each SHA hash.

Hash algorithmBitsBytesHex CharactersSHA-2562563264SHA-3843844896SHA-51251264128

For the head -c [x] command, x will be the number of hex characters for the corresponding algorithm.

MDN also mentions a command to compute the SRI hash:

shasum -b -a 384 FILENAME.js | awk '{ print $1 }' | xxd -r -p | base64

awk '{print $1}' Finds the first section of a string (separated by tab or space) and passes it to xxd. $1 represents the first segment of the string passed into it.

And if you’re running Windows:

@echo off set bits=384 openssl dgst -sha%bits% -binary %1% | openssl base64 -A > tmp set /p a= < tmp del tmp echo sha%bits%-%a% pause
  • @echo off prevents the commands that are running from being displayed. This is particularly helpful for ensuring the terminal doesn’t become cluttered.
  • set bits=384 sets a variable called bits to 384. This will be used a bit later in the script.
  • openssl dgst -sha%bits% -binary %1% | openssl base64 -A > tmp is more complex, so let’s break it down into parts.
    • openssl dgst computes a digest of an input file.
    • -sha%bits% uses the variable, bits, and combines it with the rest of the string to be one of the possible flag values, sha256, sha384, or sha512.
    • -binary outputs the hash as binary data instead of a string format, such as hexadecimal.
    • %1% is the first argument passed to the script when it’s run.
    • The first part of the command hashes the file provided as an argument to the script.
    • | openssl base64 -A > tmp converts the binary output piping through it into base64 and writes it to a file called tmp. -A outputs the base64 onto a single line.
    • set /p a= <tmp stores the contents of the file, tmp, in a variable, a.
    • del tmp deletes the tmp file.
    • echo sha%bits%-%a% will print out the type of SHA hash type, along with the base64 of the input file.
    • pause Prevents the terminal from closing.
SRI in action

Now that we understand how hashing and SRI hashes work, let’s try a concrete example. We’ll create two files:

// file1.js alert('Hello, world!');


// file2.js alert('Hi, world!');

Then we’ll compute the SHA-384 SRI hashes for both:

FilenameSHA-384 hash (base64)file1.js3frxDlOvLa6GGEUwMh9AowcepHRx/rwFT9VW9yL1wv/OcerR39FEfAUHZRrqaOy2file2.jshtr1LmWx3PQJIPw5bM9kZKq/FL0jMBuJDxhwdsMHULKybAG5dGURvJIXR9bh5xJ9

Then, let’s create a file named index.html:

<!DOCTYPE html> <html> <head> <script type="text/javascript" src="./file1.js" integrity="sha384-3frxDlOvLa6GGEUwMh9AowcepHRx/rwFT9VW9yL1wv/OcerR39FEfAUHZRrqaOy2" crossorigin="anonymous"></script> <script type="text/javascript" src="./file2.js" integrity="sha384-htr1LmWx3PQJIPw5bM9kZKq/FL0jMBuJDxhwdsMHULKybAG5dGURvJIXR9bh5xJ9" crossorigin="anonymous"></script> </head> </html>

Place all of these files in the same folder and start a server within that folder (for example, run npx http-server inside the folder containing the files and then open one of the addresses provided by http-server or the server of your choice, such as You should get two alert dialog boxes. The first should say “Hello, world!” and the second, “Hi, world!”

If you modify the contents of the scripts, you’ll notice that they no longer execute. This is subresource integrity in effect. The browser notices that the hash of the requested file does not match the expected hash and refuses to run it.

We can also include multiple hashes for a resource and the strongest hash will be chosen, like so:

<!DOCTYPE html> <html> <head> <script type="text/javascript" src="./file1.js" integrity="sha384-3frxDlOvLa6GGEUwMh9AowcepHRx/rwFT9VW9yL1wv/OcerR39FEfAUHZRrqaOy2 sha512-cJpKabWnJLEvkNDvnvX+QcR4ucmGlZjCdkAG4b9n+M16Hd/3MWIhFhJ70RNo7cbzSBcLm1MIMItw 9qks2AU+Tg==" crossorigin="anonymous"></script> <script type="text/javascript" src="./file2.js" integrity="sha384-htr1LmWx3PQJIPw5bM9kZKq/FL0jMBuJDxhwdsMHULKybAG5dGURvJIXR9bh5xJ9 sha512-+4U2wdug3VfnGpLL9xju90A+kVEaK2bxCxnyZnd2PYskyl/BTpHnao1FrMONThoWxLmguExF7vNV WR3BRSzb4g==" crossorigin="anonymous"></script> </head> </html>

The browser will choose the hash that is considered to be the strongest and check the file’s hash against it.

Why is there a “crossorigin” attribute?

The crossorigin attribute tells the browser when to send the user credentials with the request for the resource. There are two options to choose from:

Value (crossorigin=)DescriptionanonymousThe request will have its credentials mode set to same-origin and its mode set to cors.use-credentialsThe request will have its credentials mode set to include and its mode set to cors. Request credentials modes mentioned Credentials modeDescriptionsame-originCredentials will be sent with requests sent to same-origin domains and credentials that are sent from same-origin domains will be used.includeCredentials will be sent to cross-origin domains as well and credentials sent from cross-origin domains will be used. Request modes mentioned Request modeDescriptioncorsThe request will be a CORS request, which will require the server to have a defined CORS policy. If not, the request will throw an error. Why is the “crossorigin” attribute required with subresource integrity?

By default, scripts and stylesheets can be loaded cross-origin, and since subresource integrity prevents the loading of a file if the hash of the loaded resource doesn’t match the expected hash, an attacker could load cross-origin resources en masse and test if the loading fails with specific hashes, thereby inferring information about a user that they otherwise wouldn’t be able to.

When you include the crossorigin attribute, the cross-origin domain must choose to allow requests from the origin the request is being sent from in order for the request to be successful. This prevents cross-origin attacks with subresource integrity.

Using subresource integrity with webpack

It probably sounds like a lot of work to recalculate the SRI hashes of each file every time they are updated, but luckily, there’s a way to automate it. Let’s walk through an example together. You’ll need a few things before you get started.

Node.js and npm

Node.js is a JavaScript runtime that, along with npm (its package manager), will allow us to use webpack. To install it, visit the Node.js website and choose the download that corresponds to your operating system.

Setting up the project

Create a folder and give it any name with mkdir [name of folder]. Then type cd [name of folder] to navigate into it. Now we need to set up the directory as a Node project, so type npm init. It will ask you a few questions, but you can press Enter to skip them since they’re not relevant to our example.


webpack is a library that allows you automatically combine your files into one or more bundles. With webpack, we will no longer need to manually update the hashes. Instead, webpack will inject the resources into the HTML with integrity and crossorigin attributes included.

Installing webpack

Yu’ll need to install webpack and webpack-cli:

npm i --save-dev webpack webpack-cli

The difference between the two is that webpack contains the core functionalities whereas webpack-cli is for the command line interface.

We’ll edit our package.json to add a scripts section like so:

{ //... rest of package.json ..., "scripts": { "dev": "webpack --mode=development" } //... rest of package.json ..., }

This enable us to run npm run dev and build our bundle.

Setting up webpack configuration

Next, let’s set up the webpack configuration. This is necessary to tell webpack what files it needs to deal with and how.

First, we’ll need to install two packages, html-webpack-plugin, and webpack-subresource-integrity:

npm i --save-dev html-webpack-plugin webpack-subresource-integrity style-loader css-loader Package nameDescriptionhtml-webpack-pluginCreates an HTML file that resources can be injected intowebpack-subresource-integrityComputes and inserts subresource integrity information into resources such as <script> and <link rel=…>style-loaderApplies the CSS styles that we importcss-loaderEnables us to import css files into our JavaScript

Setting up the configuration:

const path = require('path'), HTMLWebpackPlugin = require('html-webpack-plugin'), SriPlugin = require('webpack-subresource-integrity'); module.exports = { output: { // The output file's name filename: 'bundle.js', // Where the output file will be placed. Resolves to // the "dist" folder in the directory of the project path: path.resolve(__dirname, 'dist'), // Configures the "crossorigin" attribute for resources // with subresource integrity injected crossOriginLoading: 'anonymous' }, // Used for configuring how various modules (files that // are imported) will be treated modules: { // Configures how specific module types are handled rules: [ { // Regular expression to test for the file extension. // These loaders will only be activated if they match // this expression. test: /\.css$/, // An array of loaders that will be applied to the file use: ['style-loader', 'css-loader'], // Prevents the accidental loading of files within the // "node_modules" folder exclude: /node_modules/ } ] }, // webpack plugins alter the function of webpack itself plugins: [ // Plugin that will inject integrity hashes into index.html new SriPlugin({ // The hash functions used (e.g. // <script integrity="sha256- ... sha384- ..." ... hashFuncNames: ['sha384'] }), // Creates an HTML file along with the bundle. We will // inject the subresource integrity information into // the resources using webpack-subresource-integrity new HTMLWebpackPlugin({ // The file that will be injected into. We can use // EJS templating within this file, too template: path.resolve(__dirname, 'src', 'index.ejs'), // Whether or not to insert scripts and other resources // into the file dynamically. For our example, we will // enable this. inject: true }) ] }; Creating the template

We need to create a template to tell webpack what to inject the bundle and subresource integrity information into. Create a file named index.ejs:

<!DOCTYPE html> <html> <body></body> </html>

Now, create an index.js in the folder with the following script:

// Imports the CSS stylesheet import './styles.css' alert('Hello, world!'); Building the bundle

Type npm run build in the terminal. You’ll notice that a folder, called dist is created, and inside of it, a file called index.html that looks something like this:

<!DOCTYPE HTML> <html><head><script defer src="bundle.js" integrity="sha384-lb0VJ1IzJzMv+OKd0vumouFgE6NzonQeVbRaTYjum4ql38TdmOYfyJ0czw/X1a9b" crossorigin="anonymous"> </script></head> <body> </body> </html>

The CSS will be included as part of the bundle.js file.

This will not work for files loaded from external servers, nor should it, as cross-origin files that need to constantly update would break with subresource integrity enabled.

Thanks for reading!

That’s all for this one. Subresource integrity is a simple and effective addition to ensure you’re loading only what you expect and protecting your users; and remember, security is more than just one solution, so always be on the lookout for more ways to keep your website safe.

The post Securing Your Website With Subresource Integrity appeared first on CSS-Tricks.

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

Safari 15: New UI, Theme Colors, and… a CSS-Tricks Cameo!

Css Tricks - Fri, 06/11/2021 - 11:38am

There’s a 33-minute video (and resources) over on covering the upcoming Safari changes we saw in the WWDC keynote this year in much more detail. Look who’s got a little cameo in there:

Perhaps the most noticeable thing there in Safari 15 on iOS is URL bar at the bottom! Dave was speculating in our little Discord watch party that this probably fixes the weird issues with 100vh stuff on iOS. But I really just don’t know, we’ll have to see when it comes out and we can play with it. I’d guess the expectation is that, in order for us to do our own fixed-bottom-UI stuff, we’d be doing:

.bottom-nav { position: fixed; /* maybe sticky is better if part of overall page layout? */ bottom: 100vh; /* fallback? */ bottom: calc(100vh - env(safe-area-inset-bottom)); /* new thing */ }

On desktop, the most noticeable visual feature is probably the theme-color meta tags.

This isn’t even a brand new Apple-only thing. This is the same <meta> tag that Chrome’s Android app has used since 2014, so you might already be sporting it on your own site. The addition is that it supports media queries.

<meta name="theme-color" content="#ecd96f" media="(prefers-color-scheme: light)"> <meta name="theme-color" content="#0b3e05" media="(prefers-color-scheme: dark)">

It’s great to see Safari get aspect-ratio and the new fancy color systems like lab() and lch() as well. Top-level await in JavaScript is great as it makes patterns like conditional imports easier.

I don’t think all this would satisfy Alex. We didn’t exactly get alternative browser engines on iOS or significant PWA enhancements (both of which would be really great to see). But I applaud it all—it’s good stuff. While I do think Google generally takes privacy more seriously than what general internet chatter would have to believe, it’s notable to compare each company’s newly-released features. If you’ll forgive a bit of cherry-picking, Google is working on FLoC, a technology very specifically designed to help targeted advertising. Apple is working on Private Relay, a technology very specifically to making web browsing untrackable.

The post Safari 15: New UI, Theme Colors, and… a CSS-Tricks Cameo! appeared first on CSS-Tricks.

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

Put a Background on Open Details Elements

Css Tricks - Thu, 06/10/2021 - 2:16pm

One thing that can be just a smidge funky about the <details> element is that, when open, it’s not always 100% clear what is inside that element and what isn’t. I’m not saying that always matters or that it’s a particularly hard problem to solve, I’m just noting it as it came up recently for me.

Here’s a visual example:

What text here is inside a <details> and what isn’t?

The solution is… CSS. Style the <details> somewhat uniquely, and that problem goes away. Even if you want the typography to be the same, or you don’t want any exclusive styling until the <details> is opened, it’s still possible. Using an alpha-transparent fill, you can even make sure that deeper-nested <details> remain clear.

For <details> that you just slug into inline content (like a "spoiler" UI or something) I like the idea of some kind of border or background showing where the content ends. Nice for nested details as well.@CodePen

— Chris Coyier (@chriscoyier) June 3, 2021

Here’s that CSS:

details[open] { --bg: rgb(0 0 0 / 0.2); background: var(--bg); outline: 1rem solid var(--bg); margin: 0 0 2rem 0; }

And the demo:

CodePen Embed Fallback

The post Put a Background on Open Details Elements appeared first on CSS-Tricks.

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

Building a Headless CMS with Fauna and Vercel Functions

Css Tricks - Thu, 06/10/2021 - 4:26am

This article introduces the concept of the headless CMS, a backend-only content management system that allows developers to create, store, manage and publish the content over an API using the Fauna and Vercel functions. This improves the frontend-backend workflow, that enables developers to build excellent user experience quickly.

In this tutorial, we will learn and use headless CMS, Fauna, and Vercel functions to build a blogging platform, Blogify&#x1f680;. After that, you can easily build any web application using a headless CMS, Fauna and Vercel functions.


According to MDN, A content management system (CMS) is a computer software used to manage the creation and modification of digital content. CMS typically has two major components: a content management application (CMA), as the front-end user interface that allows a user, even with limited expertise, to add, modify, and remove content from a website without the intervention of a webmaster; and a content delivery application (CDA), that compiles the content and updates the website.

The Pros And Cons Of Traditional vs Headless CMS

Choosing between these two can be quite confusing and complicated. But they both have potential advantages and drawbacks.

Traditional CMS Pros
  • Setting up your content on a traditional CMS is much easier as everything you need (content management, design, etc) are made available to you.
  • A lot of traditional CMS has drag and drop, making it easy for a person with no programming experience to work easily with them. It also has support for easy customization with zero to little coding knowledge.
Traditional CMS Cons
  • The plugins and themes which the traditional CMS relies on may contain malicious codes or bugs and slow the speed of the website or blog.
  • The traditional coupling of the front-end and back-end definitely would more time and money for maintenance and customization.
Headless CMS Pros
  • There’s flexibility with choice of frontend framework to use since the frontend and backend are separated from each other, it makes it possible for you to pick which front-end technology suits your needs. It gives the freewill to choose the tools need to build the frontend—flexibility during the development stage.
  • Deploying works easier with headless CMS. The applications (blogs, websites, etc) built with headless CMS can be easily be deployed to work on various displays such as web device, mobile devices, AR/VR devices.
Headless CMS Cons
  • You are left with the worries of managing your back-end infrastructures, setting up the UI component of your site, app.
  • Implementation of headless CMS are known to be more costly against the traditional CMS. Building headless CMS application that embodies analytics are not cost-effective.

Fauna uses a preexisting infrastructure to build web applications without the usually setting up a custom API server. This efficiently helps to save time for developers, and the stress of choosing regions and configuring storage that exists among other databases; which is global/multi-region by default, are nonexistent with Fauna. All maintenance we need are actively taken care of by engineers and automated DevOps at Fauna. We will use Fauna as our backend-only content management system.

Pros Of Using Fauna
  • The ease to use and create a Fauna database instance from within development environment of the hosting platforms like Netlify or Vercel.
  • Great support for querying data via GraphQL or use Fauna’s own query language. Fauna Query Language (FQL), for complex functions.
  • Access data in multiple models including relational, document, graph and temporal.
  • Capabilities like built-in authentication, transparent scalability and multi-tenancy are fully available on Fauna.
  • Add-on through Fauna Console as well as Fauna Shell makes it easy to manage database instance very easily.

Vercel Functions, also known as Serverless Functions, according to the docs are pieces of code written with backend languages that take an HTTP request and provide a response.


To take full advantage of this tutorial, ensure the following tools are available or installed on your local development environment:

  • Access to Fauna dashboard
  • Basic knowledge of React and React Hooks
  • Have create-react-app installed as a global package or use npx to bootstrap the project.
  • Node.js version >= 12.x.x installed on your local machine.
  • Ensure that npm or yarn is also installed as a package manager
Database Setup With Fauna

Sign in into your fauna account to get started with Fauna, or first register a new account using either email credentials/details or using an existing Github account as a new user. You can register for a new account here. Once you have created a new account or signed in, you are going to be welcomed by the dashboard screen. We can also make use of the fauna shell if you love the shell environment. It easily allows you to create
and/or modify resources on Fauna through the terminal.

Using the fauna shell, the command is:

npm install --global fauna-shell fauna cloud-login

But we will use the website throughout this tutorial. Once signed in, the dashboard screen welcomes you:

Now we are logged in or have our accounts created, we can go ahead to create our Fauna. We’ll go through following simple steps to create the new fauna database using Fauna services. We start with naming our database, which we’ll use as our content management system. In this tutorial, we will name our database blogify.

With the database created, next step is to create a new data collection from the Fauna dashboard. Navigate to the Collection tab on the side menu and create a new collection by clicking on the NEW COLLECTION button.

We’ll then go ahead to give whatever name well suiting to our collection. Here we will call it blogify_posts.

Next step in getting our database ready is to create a new index. Navigate to the Indexes tab to create an index. Searching documents in Fauna can be done by using indexes, specifically by matching inputs against an index’s terms field. Click on the NEW INDEX button to create an index. Once in create index screen, fill out the form: selecting the collection we’ve created previously, then giving a name to our index. In this tutorial, we will name ours all_posts. We can now save our index.

After creating an index, now it’s time to create our DOCUMENT, this will contain the contents/data we want to use for our CMS website. Click on the NEW DOCUMENT button to get started. With the text editor to create our document, we’ll create an object data to serve our needs for the website.

The above post object represents the unit data we need to create our blog post. Your choice of data can be so different from what we have here, serving the purpose whatever you want it for within your website. You can create as much document you may need for your CMS website. To keep things simple, we just have three blog posts.

Now that we have our database setup complete to our choice, we can move on to create our React app, the frontend.

Create A New React App And Install Dependencies

For the frontend development, we will need dependencies such as Fauna SDK, styled-components and vercel in our React app. We will use the styled-components for the UI styling, use the vercel within our terminal to host our application. The Fauna SDK would be used to access our contents at the database we had setup. You can always replace the styled-components for whatever library you decide to use for your UI styling. Also use any UI framework or library you preferred to others.

npx create-react-app blogify # install dependencies once directory is done/created yarn add fauna styled-components # install vercel globally yarn global add vercel

The fauna package is Fauna JavaScript driver for Fauna. The library styled-components allows you to write actual CSS code to style your components. Once done with all the installation for the project dependencies, check the package.json file to confirm all installation was done

Now let’s start an actual building of our blog website UI. We’ll start with the header section. We will create a Navigation component within the components folder inside the src folder, src/components, to contain our blog name, Blogify&#x1f680;.

import styled from "styled-components"; function Navigation() { return ( <Wrapper> <h1>Blogify&#x1f680;</h1> </Wrapper> ); } const Wrapper = styled.div` background-color: #23001e; color: #f3e0ec; padding: 1.5rem 5rem; & > h1 { margin: 0px; } `; export default Navigation;

After being imported within the App components, the above code coupled with the stylings through the styled-components library, will turn out to look like the below UI:

Now time to create the body of the website, that will contain the post data from our database. We structure a component, called Posts, which will contains our blog posts created on the backend.

import styled from "styled-components"; function Posts() { return ( <Wrapper> <h3>My Recent Articles</h3> <div className="container"></div> </Wrapper> ); } const Wrapper = styled.div` margin-top: 3rem; padding-left: 5rem; color: #23001e; & > .container { display: flex; flex-wrap: wrap; } & > .container > div { width: 50%; padding: 1rem; border: 2px dotted #ca9ce1; margin-bottom: 1rem; border-radius: 0.2rem; } & > .container > div > h4 { margin: 0px 0px 5px 0px; } & > .container > div > button { padding: 0.4rem 0.5rem; border: 1px solid #f2befc; border-radius: 0.35rem; background-color: #23001e; color: #ffffff; font-weight: medium; margin-top: 1rem; cursor: pointer; } & > .container > div > article { margin-top: 1rem; } `; export default Posts;

The above code contains styles for JSX that we’ll still create once we start querying for data from the backend to the frontend.

Integrate Fauna SDK Into Our React App

To integrate the fauna client with the React app, you have to make an initial connection from the app. Create a new file db.js at the directory path src/config/. Then import the fauna driver and define a new client.
The secret passed as the argument to the fauna.Client() method is going to hold the access key from .env file:

import fauna from 'fauna'; const client = new fauna.Client({ secret: process.env.REACT_APP_DB_KEY, }); const q = fauna.query; export { client, q };

Inside the Posts component create a state variable called posts using useState React Hooks with a default value of an array. It is going to store the value of the content we’ll get back from our database using the setPosts function. Then define a second state variable, visible, with a default value of false, that we’ll use to hide or show more post content using the handleDisplay function that would be triggered by a button we’ll add later in the tutorial.

function App() { const [posts, setPosts] = useState([]); const [visible, setVisibility] = useState(false); const handleDisplay = () => setVisibility(!visible); // ... } Creating A Serverless Function By Writing Queries

Since our blog website is going to perform only one operation, that’s to get the data/contents we created on the database, let’s create a new directory called src/api/ and inside it, we create a new file called index.js. Making the request with ES6, we’ll use import to import the client and the query instance from the config/db.js file:

export const getAllPosts = client .query(q.Paginate(q.Match(q.Ref('indexes/all_posts')))) .then(response => { const expenseRef =; const getAllDataQuery = => { return q.Get(ref); }); return client.query(getAllDataQuery).then(data => data); }) .catch(error => console.error('Error: ', error.message)); }) .catch(error => console.error('Error: ', error.message));

The query above to the database is going to return a ref that we can map over to get the actual results need for the application. We’ll make sure to append the catch that will help check for an error while querying the database, so we can log it out.

Next is to display all the data returned from our CMS, database—from the Fauna collection. We’ll do so by invoking the query getAllPosts from the ./api/index.js file inside the useEffect Hook inside our Posts component. This is because when the Posts component renders for the first time, it iterates over the data, checking if there are any post in the database:

useEffect(() => { getAllPosts.then((res) => { setPosts(res); console.log(res); }); }, []);

Open the browser’s console to inspect the data returned from the database. If all things being right, and you’re closely following, the return data should look like the below:

With these data successfully returned from the database, we can now complete our Posts components, adding all necessary JSX elements that we’ve styled using styled-components library. We’ll use JavaScript map to loop over the posts state, array, only when the array is not empty:

import { useEffect, useState } from "react"; import styled from "styled-components"; import { getAllPosts } from "../api"; function Posts() { useEffect(() => { getAllPosts.then((res) => { setPosts(res); console.log(res); }); }, []); const [posts, setPosts] = useState([]); const [visible, setVisibility] = useState(false); const handleDisplay = () => setVisibility(!visible); return ( <Wrapper> <h3>My Recent Articles</h3> <div className="container"> {posts && => ( <div key={} id={}> <h4>{}</h4> <em>{}</em> <article> {} <p style={{ display: visible ? "block" : "none" }}> {} </p> </article> <button onClick={handleDisplay}> {visible ? "Show less" : "Show more"} </button> </div> ))} </div> </Wrapper> ); } const Wrapper = styled.div` margin-top: 3rem; padding-left: 5rem; color: #23001e; & > .container { display: flex; flex-wrap: wrap; } & > .container > div { width: 50%; padding: 1rem; border: 2px dotted #ca9ce1; margin-bottom: 1rem; border-radius: 0.2rem; } & > .container > div > h4 { margin: 0px 0px 5px 0px; } & > .container > div > button { padding: 0.4rem 0.5rem; border: 1px solid #f2befc; border-radius: 0.35rem; background-color: #23001e; color: #ffffff; font-weight: medium; margin-top: 1rem; cursor: pointer; } & > .container > div > article { margin-top: 1rem; } `; export default Posts;

With the complete code structure above, our blog website, Blogify&#x1f680;, will look like the below UI:

Deploying To Vercel

Vercel CLI provides a set of commands that allow you to deploy and manage your projects. The following steps will get your project hosted from your terminal on vercel platform fast and easy:

vercel login

Follow the instructions to login into your vercel account on the terminal


Using the vercel command from the root of a project directory. This will prompt questions that we will provide answers to depending on what’s asked.

vercel ? Set up and deploy “~/Projects/JavaScript/React JS/blogify”? [Y/n] ? Which scope do you want to deploy to? ikehakinyemi ? Link to existing project? [y/N] n ? What’s your project’s name? (blogify) # click enter if you don't want to change the name of the project ? In which directory is your code located? ./ # click enter if you running this deployment from root directory ? ? Want to override the settings? [y/N] n

This will deploy your project to vercel. Visit your vercel account to complete any other setup needed for CI/CD purpose.


I’m glad you followed the tutorial to this point, hope you’ve learnt how to use Fauna as Headless CMS. The combination of Fauna with Headless CMS concepts you can build great web application, from e-commerce application to Notes keeping application, any web application that needs data to be stored and retrieved for use on the frontend. Here’s the GitHub link to code sample we used within our tutorial, and the live demo which is hosted on vercel.

Related Resources

The post Building a Headless CMS with Fauna and Vercel Functions appeared first on CSS-Tricks.

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


Css Tricks - Wed, 06/09/2021 - 4:37am

Does that make your eye twitch a little bit? Like… it’s a typo. It should be target="_blank" with an underscore to start the value. As in…

<a target="_blank" href=""> Open CodePen in a New Tab </a>

Welp, that’s correct syntax!

In the case of the no-underscore target="blank", the blank part is just a name. It could be anything. It could be target="foo" or, perhaps to foreshadow the purpose here: target="open-new-links-in-this-space".

The difference:

  • target="_blank" is a special keyword that will open links in a new tab every time.
  • target="blank" will open the first-clicked link in a new tab, but any future links that share target="blank" will open in that same newly-opened tab.

I never knew this! I credit this tweet explanation.

I created a very basic demo page to show off the functionality (code). Watch as a new tab opens when I click the first link. Then, subsequent clicks from either also open tab open that link in that new second tab.


I think use cases here are few and far between. Heck, I’m not even that big of a fan of target="_blank". But here’s one I could imagine: documentation.

Say you’ve got a web app where people actively do work. It might make sense to open links to documentation from within that app in a new tab, so they aren’t navigating away from active work. But, maybe you think they don’t need a new tab for every documentation link. You could do like…

<a target="codepen-documentation" href=""> View CodePen Documentation </a> <!-- elsewhere --> <a target="codepen-documentation" href=""> About Asset Hosting </a>

The post target=blank appeared first on CSS-Tricks.

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

Syndicate content
©2003 - Present Akamai Design & Development.