Css Tricks

Syndicate content CSS-Tricks
Tips, Tricks, and Techniques on using Cascading Style Sheets.
Updated: 15 hours 5 min ago

Can Include (a Certain HTML element within another Certain HTML Element)

Fri, 10/22/2021 - 7:57am

A single-serving website from Alexander Vishnyakov for testing if it’s valid to put any particular HTML element within another type of HTML element. Kinda neat to have a quick reference for this.

Some combinations feel fairly obvious: can you put a <video> inside an <input>? Uh, no. Some are trickier: can you put a <div> inside an <h1>? Nope — that one bit me this week (derp). Some are a little confusing, like <div> is an invalid child of an <ol> but a valid child of a <dl>.

Direct Link to ArticlePermalink

The post Can Include (a Certain HTML element within another Certain HTML Element) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Exploring the CSS Paint API: Rounding Shapes

Fri, 10/22/2021 - 4:02am

Adding borders to complex shapes is a pain, but rounding the corner of complex shapes is a nightmare! Luckily, the CSS Paint API is here to the rescue! That’s what we’re going to look at as part of this “Exploring the CSS Paint API” series.

Exploring the CSS Paint API series:

Here’s what we’re aiming for. Like everything else we’ve looked at in this series, note that only Chrome and Edge support this for now.

Live Demo

You may have noticed a pattern forming if you’ve followed along with the rest of the articles. In general, when we work with the CSS Paint API:

  • We write some basic CSS that we can easily adjust.
  • All the complex logic is done behind the scene inside the paint() function.
We can actually do this without the Paint API

There are probably a lot of ways to put rounded corners on complex shapes, but I will share with you three methods I’ve used in my own work.

I already hear you saying: If you already know three methods, then why are you using the Paint API? Good question. I’m using it because the three methods I’m aware of are difficult, and two of them are specifically related to SVG. I have nothing against SVG, but a CSS-only solution makes thing easier to maintain, plus it’s easier for someone to walk into and understand when grokking the code.

Onto those three methods…

Using clip-path: path()

If you are an SVG guru, this method is for you. the clip-path property accepts SVG paths. That means we can easily pass in the path for a complex rounded shape and be done. This approach is super easy if you already have the shape you want, but it’s unsuitable if you want an adjustable shape where, for example, you want to adjust the radius.

Below an example of a rounded hexagon shape. Good luck trying to adjust the curvature and the shape size! You’re gonna have to edit that crazy-looking path to do it.

CodePen Embed Fallback

I suppose you could refer to this illustrated guide to SVG paths that Chris put together. But it’s still going to be a lot of work to plot the points and curves just how you want it, even referencing that guide.

Using an SVG filter

I discovered this technique from Lucas Bebber’s post about creating a gooey effect. You can find all the technical details there, but the idea is to apply an SVG filter to any element to round its corners.

CodePen Embed Fallback

We simply use clip-path to create the shape we want then apply the SVG filter on a parent element. To control the radius, we adjust the stdDeviation variable.

This is a good technique, but again, it requires a deep level of SVG know-how to make adjustments on the spot.

Using Ana Tudor’s CSS-only approach

Yes, Ana Tudor found a CSS-only technique for a gooey effect that we can use to round the corner of complex shapes. She’s probably writing an article about it right now. Until then, you can refer to the slides she made where she explain how it works.

Below a demo where I am replacing the SVG filter with her technique:

CodePen Embed Fallback

Again, another neat trick! But as far as being easy to work with? Not so much here, either, especially if we’re considering more complex situations where we need transparency, images, etc. It’s work finding the correct combination of filter, mix-blend-mode and other properties to get things just right.

Using the CSS Paint API instead

Unless you have a killer CSS-only way to put rounded borders on complex shapes that you’re keeping from me (share it already!), you can probably see why I decided to reach for the CSS Paint API.

The logic behind this relies on the same code structure I used in the article covering the polygon border. I’m using the --path variable that defines our shape, the cc() function to convert our points, and a few other tricks we’ll cover along the way. I highly recommend reading that article to better understand what we’re doing here.

First, the CSS setup

We first start with a classic rectangular element and define our shape inside the --path variable (shape 2 above). The --path variable behaves the same way as the path we define inside clip-path: polygon(). Use Clippy to generate it. 

.box { display: inline-block; height: 200px; width: 200px; --path: 50% 0,100% 100%,0 100%; --radius: 20px; -webkit-mask: paint(rounded-shape); }

Nothing complex so far. We apply the custom mask and we define both the --path and a --radius variable. The latter will be used to control the curvature.

Next, the JavaScript setup

In addition to the points defined by the path variable (pictured as red points above), we’re adding even more points (pictured as green points above) that are simply the midpoints of each segment of the shape. Then we use the arcTo() function to build the final shape (shape 4 above).

Adding the midpoints is pretty easy, but using arcTo() is a bit tricky because we have to understand how it works. According to MDN:

[It] adds a circular arc to the current sub-path, using the given control points and radius. The arc is automatically connected to the path’s latest point with a straight line, if necessary for the specified parameters.

This method is commonly used for making rounded corners.

The fact that this method requires control points is the main reason for the extra midpoints points. It also require a radius (which we are defining as a variable called --radius).

If we continue reading MDN’s documentation:

One way to think about arcTo() is to imagine two straight segments: one from the starting point to a first control point, and another from there to a second control point. Without arcTo(), these two segments would form a sharp corner: arcTo() creates a circular arc that fits this corner and smooths is out. In other words, the arc is tangential to both segments.

Each arc/corner is built using three points. If you check the figure above, notice that for each corner we have one red point and two green points on each side. Each red-green combination creates one segment to get the two segments detailed above.

Let’s zoom into one corner to better understand what is happening:

We have both segments illustrated in black.
The circle in blue illustrates the radius.

Now imagine that we have a path that goes from the first green point to the next green point, moving around that circle. We do this for each corner and we have our rounded shape.

Here’s how that looks in code:

// We first read the variables for the path and the radius. const points = properties.get('--path').toString().split(','); const r = parseFloat(properties.get('--radius').value); var Ppoints = []; var Cpoints = []; const w = size.width; const h = size.height; var N = points.length; var i; // Then we loop through the points to create two arrays. for (i = 0; i < N; i++) { var j = i-1; if(j<0) j=N-1; var p = points[i].trim().split(/(?!\(.*)\s(?![^(]*?\))/g); // One defines the red points (Ppoints) p = cc(p[0],p[1]); Ppoints.push([p[0],p[1]]); var pj = points[j].trim().split(/(?!\(.*)\s(?![^(]*?\))/g); pj = cc(pj[0],pj[1]); // The other defines the green points (Cpoints) Cpoints.push([p[0]-((p[0]-pj[0])/2),p[1]-((p[1]-pj[1])/2)]); } /* ... */ // Using the arcTo() function to create the shape ctx.beginPath(); ctx.moveTo(Cpoints[0][0],Cpoints[0][1]); for (i = 0; i < (Cpoints.length - 1); i++) { ctx.arcTo(Ppoints[i][0], Ppoints[i][1], Cpoints[i+1][0],Cpoints[i+1][1], r); } ctx.arcTo(Ppoints[i][0], Ppoints[i][1], Cpoints[0][0],Cpoints[0][1], r); ctx.closePath(); /* ... */ ctx.fillStyle = '#000'; ctx.fill();

The last step is to fill our shape with a solid color. Now we have our rounded shape and we can use it as a mask on any element.

That’s it! Now all we have to do is to build our shape and control the radius like we want — a radius that we can animate, thanks to @property which will make things more interesting!

Live Demo Are there any drawbacks with this method?

Yes, there are drawbacks, and you probably noticed them in the last example. The first drawback is related to the hover-able area. Since we are using mask, we can still interact with the initial rectangular shape. Remember, we faced the same issue with the polygon border and we used clip-path to fix it. Unfortunately, clip-path does not help here because it also affects the rounded corner.

Let’s take the last example and add clip-path. Notice how we are losing the “inward” curvature.

CodePen Embed Fallback

There’s no issue with the hexagon and triangle shapes, but the others are missing some curves. It could be an interesting feature to keep only the outward curvature — thanks to clip-path— and at the same time we fix the hover-able area. But we cannot keep all the curvatures and reduce the hover-able area at the same time.

The second issue? It’s related to the use of a big radius value. Hover over the shapes below and see the crazy results we get:

CodePen Embed Fallback

It’s actually not a “major” drawback since we have control over the radius, but it sure would be good to avoid such a situation in case we wrongly use an overly large radius value. We could fix this by limiting the value of the radius to within a range that caps it at a maximum value. For each corner, we calculate the radius that allows us to have the biggest arc without any overflow. I won’t dig into the math logic behind this (&#x1f631;), but here is the final code to cap the radius value:

var angle = Math.atan2(Cpoints[i+1][1] - Ppoints[i][1], Cpoints[i+1][0] - Ppoints[i][0]) - Math.atan2(Cpoints[i][1] - Ppoints[i][1], Cpoints[i][0] - Ppoints[i][0]); if (angle < 0) { angle += (2*Math.PI) } if (angle > Math.PI) { angle = 2*Math.PI - angle } var distance = Math.min( Math.sqrt( (Cpoints[i+1][1] - Ppoints[i][1]) ** 2 + (Cpoints[i+1][0] - Ppoints[i][0]) ** 2), Math.sqrt( (Cpoints[i][1] - Ppoints[i][1]) ** 2 + (Cpoints[i][0] - Ppoints[i][0]) ** 2) ); var rr = Math.min(distance * Math.tan(angle/2),r);

r is the radius we are defining and rr is the radius we’re actually using. It equal either to r or the maximum value allowed without overflow.

CodePen Embed Fallback

If you hover the shapes in that demo, we no longer get strange shapes but the “maximum rounded shape” (I just coined this) instead. Notice that the regular polygons (like the triangle and hexagon) logically have a circle as their “maximum rounded shape” so we can have cool transitions or animations between different shapes.

Can we have borders?

Yes! All we have to do is to use stroke() instead of fill() inside our paint() function. So, instead of using:

ctx.fillStyle = '#000'; ctx.fill();

…we use this:

ctx.lineWidth = b; ctx.strokeStyle = '#000'; ctx.stroke();

This introduces another variable, b, that controls the border’s thickness.

CodePen Embed Fallback

Did you notice that we have some strange overflow? We faced the same issue in the previous article, and that due to how stroke() works. I quoted MDN in that article and will do it again here as well:

Strokes are aligned to the center of a path; in other words, half of the stroke is drawn on the inner side, and half on the outer side.

Again, it’s that “half inner side, half outer side” that’s getting us! In order to fix it, we need to hide the outer side using another mask, the first one where we use the fill(). First, we need to introduce a conditional variable to the paint() function in order to choose if we want to draw the shape or only its border.

Here’s what we have:

if(t==0) { ctx.fillStyle = '#000'; ctx.fill(); } else { ctx.lineWidth = 2*b; ctx.strokeStyle = '#000'; ctx.stroke(); }

Next, we apply the first type of mask (t=0) on the main element, and the second type (t=1) on a pseudo-element. The mask applied on the pseudo-element produces the border (the one with the overflow issue). The mask applied on the main element addresses the overflow issue by hiding the outer part of the border. And if you’re wondering, that’s why we are adding twice the border thickness to lineWidth.

Live Demo

See that? We have perfect rounded shapes as outlines and we can adjust the radius on hover. And can use any kind of background on the shape.

And we did it all with a bit of CSS:

div { --radius: 5px; /* Defines the radius */ --border: 6px; /* Defines the border thickness */ --path: /* Define your shape here */; --t: 0; /* The first mask on the main element */ -webkit-mask: paint(rounded-shape); transition: --radius 1s; } div::before { content: ""; background: ..; /* Use any background you want */ --t: 1; /* The second mask on the pseudo-element */ -webkit-mask: paint(rounded-shape); /* Remove this if you want the full shape */ } div[class]:hover { --radius: 80px; /* Transition on hover */ }

Let’s not forget that we can easily introduce dashes using setLineDash() the same way we did in the previous article.

Live Demo Controlling the radius

In all the examples we’ve looked at, we always consider one radius applied to all the corners of each shape. It would be interesting if we could control the radius of each corner individually, the same way the border-radius property takes up to four values. So let’s extend the --path variable to consider more parameters.

Actually, our path can be expressed as a list of [x y] values. We’ll make a list of [x y r] values where we introduce a third value for the radius. This value isn’t mandatory; if omitted, it falls back to the main radius.

.box { display: inline-block; height: 200px; width: 200px; --path: 50% 0 10px,100% 100% 5px,0 100%; --radius: 20px; -webkit-mask: paint(rounded-shape); }

Above, we have a 10px radius for the first corner, 5px for the second, and since we didn’t specify a value for the third corner, it inherits the 20px defined by the --radius variable.

Here’s our JavaScript for the values:

var Radius = []; // ... var p = points[i].trim().split(/(?!\(.*)\s(?![^(]*?\))/g); if(p[2]) Radius.push(parseInt(p[2])); else Radius.push(r);

This defines an array that stores the radius of each corner. Then, after splitting the value of each point, we test whether we have a third value (p[2]). If it’s defined, we use it; if not, we use the default radius. Later on, we’re using Radius[i] instead of r.

Live Demo

This minor addition is a nice feature for when we want to disable the radius for a specific corner of the shape. In fact, let’s look at a few different examples next.

More examples!

I made a series of demos using this trick. I recommend setting the radius to 0 to better see the shape and understand how the path is created. Remember that the --path variable behaves the same way as the path we define inside clip-path: polygon(). If you’re looking for a path to play with, try using Clippy to generate one for you.

Example 1: CSS shapes

A lot of fancy shapes can be created using this technique. Here are a few of them done without any extra elements, pseudo-elements, or hack-y code.

CodePen Embed Fallback Example 2: Speech bubble

In the previous article, we added border to a speech bubble element. Now we can improve it and round the corners using this new method.

CodePen Embed Fallback

If you compare with this example with the original implementation, you may notice the exact same code. I simply made two or three changes to the CSS to use the new Worklet.

Example 3: Frames

Find below some cool frames for your content. No more headaches when we need gradient borders!

CodePen Embed Fallback

Simply play with the --path variable to create your own responsive frame with any coloration your want.

Example 4: Section divider

SVG is no longer needed to create those wavy section dividers that are popular these days.

CodePen Embed Fallback

Notice that the CSS is light and relatively simple. I only updated the path to generate new instances of the divider.

Example 5: Navigation menu

Here’s a classic design pattern that I’m sure many of us have bumped into at some time: How the heck do we invert the radius? You’ve likely seen it in navigation designs.

CodePen Embed Fallback

A slightly different take on it:

CodePen Embed Fallback Example 6: Gooey effect

If we play with the path values we can reach for some fancy animation.
Below an idea where I am applying a transition to only one value of the path and yet we get a pretty cool effect

CodePen Embed Fallback

This one’s inspired by Ana Tudor’s demo.

Another idea with a different animation

CodePen Embed Fallback

Another example with a more complex animation:

CodePen Embed Fallback

What about a bouncing ball

CodePen Embed Fallback Example 7: Shape morphing

Playing with big radius values allows us to create cool transitions between different shapes, especially between a circle and a regular polygon.

CodePen Embed Fallback

If we add some border animation, we get “breathing” shapes!

CodePen Embed Fallback Let’s round this thing up

I hope you’ve enjoyed getting nerdy with the CSS Paint API. Throughout this series, we’ve applied paint() to a bunch of real-life examples where having the API allows us to manipulate elements in a way we’ve never been able to do with CSS — or without resorting to hacks or crazy magic numbers and whatnot. I truly believe the CSS Paint API makes seemingly complicated problems a lot easier to solve in a straightforward way and will be a feature we reach for time and again. That is, when browser support catches up to it.

If you’ve followed along with this series, or even just stumbled into this one article, I’d love to know what you think of the CSS Paint API and how you imagine using it in your work. Are there any current design trends that would benefit from it, like the wavy section dividers? Or blobby designs? Experiment and have fun!

CodePen Embed Fallback

This one’s taken from my previous article

Exploring the CSS Paint API series:

The post Exploring the CSS Paint API: Rounding Shapes appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

What if… you could use Visual Studio Code as the editor of in-browser Developer Tools?

Thu, 10/21/2021 - 12:14pm

It’s not uncommon for my front-end workflow to go something like this:

  1. Work on thing.
  2. See that thing in an automatically refreshed browser.
  3. See something wrong with that thing.
  4. Inspect and correct the thing in DevTools.
  5. Apply the correct code in my code editor.
  6. See that correct code automatically refreshed in the browser.

I know, it’s not always great. But I’d bet the lint in my pocket you do something similar, at least every now and then.

That’s why I was drawn to the title of Chris Heilman’s post: “What if… you could use Visual Studio Code as the editor of in-browser Developer Tools?”

The idea is that VS Code can be used as the editor for DevTools and we can do it today by enabling it as an experimental feature, alongside Microsoft Edge. So, no, this is not like a prime-time ready universal thing, but watch Chris as he activates the feature, connects VS Code to DevTools, gives DevTools access to write files, then inspects the page of a local URL.

Now, those changes I make in DevTools can be synced back to VS Code, and I have direct access to open and view specific files from DevTools to see my code in context. Any changes I make in DevTools get reflected back in the VS Code files, and any changes I make in VS Code are updated live in the browser. Brilliant.

I’m not sure if this will become a thing beyond Edge but that sort of cross-over work between platforms is something that really excites me.

Direct Link to ArticlePermalink

The post What if… you could use Visual Studio Code as the editor of in-browser Developer Tools? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

The CSS-in-React Landscape

Thu, 10/21/2021 - 12:13pm

(This is a sponsored post.)

I only half-jokingly refer to the CSS-in-JS world as CSS-in-React. Many of the libraries listed below theoretically work in non-React situations — they generally call that “framework-agnostic”) — but I’d guess the vast majority of their usage is on React projects. That’s because React, despite being a UI-focused library, has no particular blessed styling solution. Vue has style tags built right into Single File Components. Same with Svelte. Angular also has a built-in component-scoped styles solution. With React, it’s bring-your-own.

Perhaps not venturing too far out of their core strengths is a strength of React. I dunno. But you do have to make a choice on how to style things on your React projects. For example, you can simply write (and there is literally no problem with this), regular ol’ flat-file vanilla CSS to style your React projects. I’d recommend that over using inline style={{ }} on everything any day. But truth be told, there are some pretty nice advantages to choosing a library to help with styles. Things like:

  • Co-locating styles and components
  • Scoping styles to components
  • Using props in styling variations
  • Using JavaScript abilities within CSS syntax
  • Theming

Each library has its own set of fancy things that might be variations on the above, or might be totally unique to that library.

It’s also notable that by using a library where you author styles in your JavaScript, it’s not a 100% guarantee that you have to ship your styles in JavaScript. The libraries that use the term “zero runtime” are typically referring to the idea that the styles are compiled to CSS during a build process, so you use that CSS like any other, which is likely better for performance.

This research is brought to you by support from Frontend Masters, CSS-Tricks’ official learning partner.

Need front-end development training?

Frontend Masters is the best place to get it. They have courses on all the most important front-end technologies, including a Front-End Developer Learning Roadmap that can help you on your journey, which includes a section on CSS in JavaScript.

A couple of caveats before we go through the list:

  • I’m not deeply experienced in every single one of these libraries. I’ve used several of them on real projects, most heavily CSS Modules. I can’t quite speak to the nuances of each. The demos below are basic demonstrations of basic styntax.
  • If I get any facts wrong or you want to add more detail, hit me in the comments or via our contact form and I’ll improve things.
  • The point of this, partially, is to have a working code example of each for easy reference.
Included frameworks styled-components
  • Super popular — probably the most-used option.
  • Popularized the possibilities of dynamic styling and the magic of using props for variations.
  • Template literal syntax feels comfortably CSS-like. It seems like it really encourages using it that way, but it is possible (docs) to use Object syntax.
  • Supports SSR, but it’s not the same as “zero runtime” libraries (which means “compiles to static CSS.” It still ships a JavaScript runtime for any dynamic styles.
CodePen Embed Fallback CSS Modules
  • Very simple. All it does is scope styles and encourage co-location of styles and components.
  • It’s fancy feature is composition, which is basically mixins based on existing classes.
  • Not a runtime thing at all — it requires a build process. But it still works with HMR and such. You can ship it with styles in JavaScript, or extract them into static CSS files. It does nothing dynamic, so it’s extremely “zero runtime” if you extract the CSS.
  • Can be combined with Sass.
  • Built into Next.js

Emotion is a library designed for writing css styles with JavaScript. It provides powerful and predictable style composition in addition to a great developer experience with features such as source maps, labels, and testing utilities. Both string and object styles are supported.

  • It doesn’t seem terribly different than styled-components, TBQH, but this podcast gets into some of the performance differences.
  • Supports SSR, but is not zero-runtime.
  • Glamorous is totally deprecated (and I suppose Glam and Glamor as well?) in favor of Emotion, so that’s something.
CodePen Embed Fallback Stitches
  • The Variants API is very useful and well done.
  • The TypeScript editor experience¹.
  • Supports theming and encourages using a design token approach.
  • Utilities allow you to build your own custom shorthands for styling.
  • Supports SSR — and it’s closer to zero-runtime, though not entirely. Also doesn’t seem to actually generate CSS files, rather it has a function that spits out CSS so you can use SSR via a <style> tag (which I can’t imagine is ideal for caching).
  • Here’s a Twitter thread with an honest review. Also see all the reactions to this.
CodePen Embed Fallback vanilla-extract
  • I’d say vanilla-extract supports SSR but it’s more than that because that’s the only way it is used, unless you opt into specific “runtime” features, like dynamic theming. That’s the meaning of “zero runtime”: it only compiles to static CSS files.
  • The TypeScript editor experience¹. (Also see our recent article.)
  • Variants API, including a Recipes API that is like like the Stiches framework above.
  • Supports a theme and utility-class-like approach via Sprinkles
  • I was gonna put Aphrodite on this list, but the creators of it are moving to vanilla-extract, so it probably isn’t a great choice these days, even though it seems to do pretty much all the same stuff as these other libraries.
  • Has a React-specific integration
  • Has an extend syntax
  • Plugin architecture
CodePen Embed Fallback Linaria
  • The OG of “Zero Runtime” CSS-in-JS libraries.
  • Compiles to actual CSS files, but still has a runtime if you do dynamic things (at least I think so).
  • Feels similar to styled-components API-wise.
  • Supports Critical CSS.
Styled JSX
  • Babel plugin, so definitely a build-process thing.
  • Using a <style jsx> tag right in the component at the level of nesting you want to scope to is a clever API.
  • Lack of nesting isn’t great — you have to repeat the selector name a lot.
  • Goober is notable because it has an awesome name and is a mere 1.25KB, which is like an order of magnitude smaller than anything else.
  • Pretty much the same feature set of styled-components and Emotion.
CodePen Embed Fallback Interesting resources:
  • Shopify’s research on which library they wanted to switch to.
  • Facebook has something cooking (conference video), but hasn’t open-sourced anything. It’s apparently called “StyleX” — and there is already a library called “Style9” that attempts to match the features, including near-zero runtime, atomic CSS output, and the TypeScript experience.
  • If you’re into authoring in the atomic style, a lot of people think that using Tailwind (probably with just-in-time mode) is the way to go.
  • Probably a more React-y version of Tailwind is Styled System, which provides styles as a bunch of preconfigured props.
  • Twin is along the lines of authoring with atomic styles in a React-y way.
  • I could not get Compiled to work for me. I’m sure it was just me, but I gave up. It looks exactly like the styled-components API to me, except the output is atomic CSS classes, which does seem kinda cool.
  • The site CSS in JS Playground shows a bunch of examples, including a few libraries not mentioned here, like Fela, Radium, and more. My god, are there a lot of these things.
  1. By “the TypeScript editor experience,” I mean the library is written in TypeScript to take advantage of one of TypeScript’s best features: help autocompleting code in code editors. For example, if you’re using VS Code as your code editor, and you write a set of “color” variants, then type <Button color=" in your JSX file, you should get a list of your own compatible variants in the contextual VS Code autocomplete menu to choose from.

The post The CSS-in-React Landscape appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

How to Create a Contact Form With Next.js and Netlify

Thu, 10/21/2021 - 4:26am

We’re going to create a contact form with Next.js and Netlify that displays a confirmation screen and features enhanced spam detection.

Next.js is a powerful React framework for developing performant React applications that scale. By integrating a Next.js site with Netlify’s technology, we can quickly get a working contact form up and running without having to write any server-side code.

Not only is it a relatively fast process to set up forms to be processed by Netlify, but it’s also free to get started (with up to 100 free submissions/per site hosted on Netlify). Form submissions automatically go through Netlify’s built-in spam filter which uses Akismet and there are also options that can be configured to increase the level of spam detection.

Creating the contact form

Within the Next.js application we should create a ContactForm component to render the contact form inside of the contact page. If you’d like for this form to render at /contact, then the ContactForm component below with labels and input fields should be used within the pages/contact.js file.

const ContactForm = ( <form name="contact-form" method="POST" action="contact/?success=true" > <label htmlFor="name">Name *</label> <input id="name" name="name" required type="text" /> <label htmlFor="company">Company *</label> <input id="company" name="company" required type="text" /> <label htmlFor="email">E-mail Address *</label> <input id="email" type="email" name="email" required /> <label htmlFor="message">Message *</label> <textarea id="message" name="message" required></textarea> <button type="submit">Submit</button> </form> );

The above markup is required to render a form with a field for Name, Company, Email address and message with a submit button. When submitting the form, based on the value of the form’s action, it should redirect to contact/?success=true from /contact. Right now there is not yet a difference between the page’s appearance with and without the success query parameter, but we will update that later.

Our Contact.js file looks like this so far:

import React from "react"; const ContactPage = () => { const ContactForm = (/* code in above code sample*/) return ( <div> <h1>Contact Us</h1> {ContactForm} </div> ); }; export default ContactPage;

Now that we have the basic form set up, the real magic will happen after we add additional information for Netlify to auto-recognize the form during future site deployments. To accomplish this we should update the form to have the attribute data-netlify="true" and a hidden input field that contains the name of our contact form. In Netlify, once we navigate to our site in the dashboard and then click on the “forms” tab  we will be able to view our form responses based on the name that we’ve put in our hidden field. It’s important that if you have multiple forms within a site that they have unique names so that they are recorded properly in Netlify.

<form method="POST" name="contact-form" action="contact/?success=true" data-netlify="true" > <input type="hidden" name="form-name" value="contact-form" />

After successfully deploying the site to Netlify with the data-netlify attribute and the form-name field  then we can go to the deployed version of the site and fill out the form. Upon submitting the form and navigating to https://app.netlify.com/sites/site-name/forms (where site-name is the name of your site) then our most recent form submission should appear if we have successfully set up the form. 

Redirect to confirmation screen 

In order to improve the user experience, we should add some logic to redirect to a confirmation screen on form submission when the URL changes to /contact/?success=true. There is also the option to redirect to an entirely different page as the action when the form is submitted but using query params we can achieve something similar with the Next Router. We can accomplish this by creating a new variable to determine if the confirmation screen or the form should be visible based on the query parameter. The next/router which is imported with import { useRouter } from "next/router"; can be used to retrieve the current query params. 

const router = useRouter(); const confirmationScreenVisible = router.query?.success && router.query.success === "true";

In our case, the confirmation screen and form can never be visible at the same time; therefore, the following statement can be used to determine if the form is visible or not.

const formVisible = !confirmationScreenVisible;

To give users the option to resubmit the form, we can add a button to the confirmation screen to reset the form by clearing the query params. Using router.replace (instead of router.push) not only updates the page but replaces the current page in the history to the version without query params. 

<button onClick={() => router.replace("/contact", undefined, { shallow: true })}> Submit Another Response </button>

We can then conditionally render the form based on whether or not the form is visible with:

{formVisible ? ContactForm : ConfirmationMessage}

Putting it all together, we can use the following code to conditionally render the form based on the query params (which are updated when the form is submitted):

import React, { useState } from "react"; import { useRouter } from "next/router"; const ContactPage = () => { const [submitterName, setSubmitterName] = useState(""); const router = useRouter(); const confirmationScreenVisible = router.query?.success && router.query.success === "true"; const formVisible = !confirmationScreenVisible; const ConfirmationMessage = ( <React.Fragment> <p> Thank you for submitting this form. Someone should get back to you within 24-48 hours. </p> <button onClick={() => router.replace("/contact", undefined, { shallow: true })}> Submit Another Response </button> </React.Fragment> ); const ContactForm = (/* code in first code example */); return ( <div> <h1>Contact Us</h1> {formVisible ? ContactForm : ConfirmationMessage} </div> ); }; export default ContactPage; Adding a hidden bot field

Now that the core functionality of our form is working, we can add additional spam detection to our form in addition to the base spam detection because Akismet is included with all Netlify Forms by default. We can enable this by adding data-netlify-honeypot="bot-field" to our form.

<form className="container" method="POST" name="contact-form" action="contact/?success=true" data-netlify="true" data-netlify-honeypot="bot-field" >

We also need to create a new hidden paragraph that contains a label named bot-field that contains the input. This field is “visible” to bots, but not humans. When this honeypot form field is filled, Netlify detects a bot and then the submission is flagged as spam.

<p hidden> <label> Don’t fill this out: <input name="bot-field" /> </label> </p> Further customizations
  • We could explore another spam prevention option that Netlify supports by adding reCAPTCHA 2 to a Netlify form.
  • We could update the form to allow uploaded files with input <input type="file">.
  • We could set up notifications for form submissions. That happens over at https://app.netlify.com/sites/[your-site-name]/settings/forms where we can include a custom subject field (which can be hidden) for email notifications.
Full code

The code for the full site code is available over at GitHub.


The following code includes everything we covered as well as the logic for setting a custom subject line with what was submitted in the name field.

import React, { useState } from "react"; import { useRouter } from "next/router"; const ContactPage = () => { const [submitterName, setSubmitterName] = useState(""); const router = useRouter(); const confirmationScreenVisible = router.query?.success && router.query.success === "true"; const formVisible = !confirmationScreenVisible; const ConfirmationMessage = ( <React.Fragment> <p> Thank you for submitting this form. Someone should get back to you within 24-48 hours. </p> <button onClick={() => router.replace("/contact", undefined, { shallow: true })}> Submit Another Response </button> </React.Fragment> ); const ContactForm = ( <form className="container" method="POST" name="contact-form" action="contact/?success=true" data-netlify="true" data-netlify-honeypot="bot-field" > <input type="hidden" name="subject" value={`You've got mail from ${submitterName}`} /> <input type="hidden" name="form-name" value="contact-form" /> <p hidden> <label> Don’t fill this out: <input name="bot-field" /> </label> </p> <label htmlFor="name">Name *</label> <input id="name" name="name" required onChange={(e) => setSubmitterName(e.target.value)} type="text" /> <label htmlFor="company">Company *</label> <input id="company" name="company" required type="text" /> <label htmlFor="email">E-mail Address *</label> <input id="email" type="email" name="email" required /> <label htmlFor="message">Message *</label> <textarea id="message" name="message" required/> <button type="submit">Submit</button> </form> ); return ( <div> <h1>Contact Us</h1> {formVisible ? ContactForm : ConfirmationMessage} </div> ); }; export default ContactPage;

The post How to Create a Contact Form With Next.js and Netlify appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Some Typography Links VIII

Wed, 10/20/2021 - 1:25pm

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

@supports selector()

Tue, 10/19/2021 - 11:12am

I didn’t realize the support for @supports determining selector support was so good! I usually think of @supports as a way to test for property: value pair support. But with the selector() function, we can test for selector support as well. It looks like this:

@supports selector(:nth-child(1 of .foo)) { }

You just drop the selector right between the parens and that’s what it tests for.

That selector above is a pretty good test, actually. It’s a “selector list argument” that works for the :nth-child ‘n’ friends selectors. As I write, it’s only supported in Safari.

So let’s say your ideal situation is that the browser supports this selector. Here’s an example. You know that with <ol> and <ul> the only valid child element is <li>. But also say this list needs separators, so you (and I’m not saying this is a great idea) did this kind of thing:

<ul> <li class="list-item">List item</li> <li class="list-item">List item</li> <li class="separator"></li> /* ... */ </ul>

Then you also want to zebra-stripe the list. And, if you want zebra striping, you need to select every other .list-item, ignoring the .separator. So…

li:nth-child(odd of .list-item) { background: lightgoldenrodyellow; }

But only Safari supports that… so you can do:

@supports selector(:nth-child(1 of .foo)) { li:nth-child(odd of .list-item) { background: lightgoldenrodyellow; } }

If you didn’t care what the fallback was, you wouldn’t even have to bother with the @supports at all. But say you do care about the fallback. Perhaps in the supported situation, the zebra striping does the heavy lifting of the UX you are shooting for, so all you need for the seperator is a bit of space. But for non-supporting browsers, you’ll need something beefier because you don’t have the zebra striping.

So now you can style both situations:

@supports selector(:nth-child(1 of .foo)) { li { padding: 0.25em; } li:nth-child(odd of .list-item) { background: lightgoldenrodyellow; } li.separator { list-style: none; margin: 0.25em 0; } } @supports not selector(:nth-child(1 of .foo)) { li.separator { height: 1px; list-style: none; border-top: 1px dashed purple; margin: 0.25em 0; } }

If we get the @when syntax, then we can write it a little cleaner:

/* Maybe? */ @when supports(selector(:nth-child(1 of .foo))) { } @else { }

Anyway. The end result is…

Supported Not Supported

There is a JavaScript API for testing support as well. I wasn’t sure if this would actually work, but it appears to! This fails in Chrome and passes in Safari as I write:

CSS.supports("selector(:nth-child(1 of .foo))")

While I was putting this together, I was thinking… hmmmmmmm — what CSS selectors are out there that have weird cross-browser support? It’s really not that many. And even of those that do have weird cross-browser support, thinking of the number of use-cases where you care to actually wrap it in an @supports (rather than just let it fail) is fairly few.

The ::marker pseudo-element would have been a great one, but it’s pretty well supported now. I was thinking the case-insensitive attribute selector, like [href$="pdf" i], would have been a good one, but nope, also well supported. Same deal with the comma-separated :not(a, .b, [c]). Maybe something like :fullscreen / :-webkit-full-screen would be interesting and useful because it’s uniquely not supported in iOS Safari?

The post @supports selector() appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

An Interview With Elad Shechter on “The New CSS Reset”

Tue, 10/19/2021 - 4:48am

Hey folks! Elad reached out to me to show me his new CSS reset project called the-new-css-reset. It’s quite interesting! I thought a neat way to share it with you is not only to point you toward it, but to ask Elad some questions about it for your reading pleasure.

Here’s the entire code for the reset up front:

/*** The new CSS Reset - version 1.2.0 (last updated 23.7.2021) ***/ /* Remove all the styles of the "User-Agent-Stylesheet", except for the 'display' property */ *:where(:not(iframe, canvas, img, svg, video):not(svg *)) { all: unset; display: revert; } /* Preferred box-sizing value */ *, *::before, *::after { box-sizing: border-box; } /* Remove list styles (bullets/numbers) in case you use it with normalize.css */ ol, ul { list-style: none; } /* For images to not be able to exceed their container */ img { max-width: 100%; } /* Removes spacing between cells in tables */ table { border-collapse: collapse; } /* Revert the 'white-space' property for textarea elements on Safari */ textarea { white-space: revert; } Hey Elad! I want to get to the code bits really quick here, but first, I’d love to hear the why about this project. CSS resets have been popular for ages because they wipe out the differences that exist between different browsers default styles and then you build up your styles from there. There are some widely-used resets out there already — why a new one?

First, when talking about “CSS resets” we have two approaches:

  • Nicolas Gallagher’s Normalize.css is the gentle approach. Normalize.css is fixing differences between the implementation in different browsers.
  • Eric Meyer’s CSS Reset is the hard approach, saying that in most cases we don’t want basic styles from the browsers, like the font-size value we get from elements like <h1> through <h6>, or the default styles for the <ul> and <ol> list elements. For example, we use the list only for the semantic meaning, and because it helps in other ways for accessibility and SEO.

I love Normalize.css. I think it’s a must-have in any project, even if you prefer the CSS Reset idea.

And why is Normalize.css so important? Normalize.css touches shadow DOM elements that the CSS Reset doesn’t. When looking at Normalize.css, you will find special pseudo-classes like ::-moz-focus-inner, ::-webkit-file-upload-button, and more. It covers so many bases and that’s why I believe Normalize.css is a must-have in any project.

I love the hard CSS Reset as well. I think in most cases we don’t want the basic styles of the browser, and if we need it in a specific place, we will define it according to our need. This brings me to the point that I’m using both Normalize.css and CSS Reset combined. So, Normalize.css is first to load, followed by the hard CSS Reset.

So, why we need a new CSS reset? The CSS resets we have are built on old CSS features. But in the last several years, we’ve gotten new features built specifically for resetting things in CSS, and this got me thinking that now we can create a much more valid CSS reset using these new cutting-edge CSS features.

It seems to me the juiciest bit here is that very first ruleset. Let’s start with that first CSS property and value: all: unset;. That’s what is doing the heavy lifting in this CSS reset yes? How does that work?

all is the most exceptional CSS property because it allows us to reset all the properties that exist in the CSS all at once.

The property accepts several keywords. The two basics are initial and inherit; there are two smarter ones, which are unset and revert. To understand what all: unset does, we need to jump to the fundamental behavior of our CSS properties.

In CSS, we have two groups of properties:

  • Inherited properties group: These are properties that have inheritance by default — mainly typography properties.
  • Non-inherited properties group: These are all other properties that don’t inherit by default, for example, the Box Model properties that include padding, border, and margin.

Like typography properties, we want to keep the inherit behavior when we try to reset them. So, that’s where we’re able to use the inherit keyword value.

/* Will get values from the parent element value */ font-size: inherit; line-height: inherit; color: inherit;

For the other properties in the non-inherited properties group, we want to get their initial value in most cases. It is worth mentioning that the initial keyword computes differently for different properties.

max-width: initial; /* = none */ width: initial; /* auto */ position: initial; /* = static */

After we understand the fundamentals as well as the inherit and initial keyword values, we understand that if we want to reset all of properties together, we can’t use them directly with the all property. That’s because, if we reset all of the properties to the initial value, i.e. all: initial, we lose the inherent behavior on the inherited properties group. And suppose we reset all properties with the inherit value. In that case, all the properties get an inheritance — even Box Model properties, which we want to avoid.

That’s why we have the unset value. unset resets the property according to its type. If we use it on an inherited property, it’s equal to inherit; if we use it on a natural non-inherited, it equals initial.

max-width: unset; /* = initial = none */ font-size: unset; /* = inherit = get parent element value */

This brings us back to the main feature of my CSS reset. What all: unset does is reset all the inherited properties to the inherit value, and all the other properties in the non-inherited properties group to their initial value.

This operation removes all the default user-agent-stylesheet styles that the browser is adding. To understand these substantial new CSS powers, all of this happened while I was doing only one operation to all HTML elements.

/* Reset all: - Inherited properties to inherit value - Non-inherited properties to initial value */ * { all: unset; } And then you follow it up with display: revert; — does all: unset; do things to the display property that would be undesirable?

Short answer: yes. The display property represents the basic structure which we do want to get from our user-agent stylesheet. As we saw in most of our properties, the unset value is doing an excellent job for us, and we reset all properties in one operation.

Now, to understand what the unique revert keyword value is doing for the display property, let’s talk about the two types of styles that we are getting from our browsers. The styles we are getting from our browsers are built from two layers:

  • Layer 1, the CSS initial value: As we already saw, the first layer is the initial values of all our properties in CSS, including the inherit behavior on some of the properties.
  • Layer 2, the user-agent stylesheet: These are the styles that the browser defines for specific HTML elements.

In most cases, when we want to reset things, we want to remove the basics styles of Layer 2. And when we do reset with all: unset, we remove all the styles of the user-agent stylesheet.

But the display property is exceptional. As we already saw, every property in CSS has only one initial value. This means that if we reset the display property to its initial, like on a <div> element or any other HTML element, it always returns the inline value.

Continuing with this logic, we connect the <div> element to the default display: block declaration, which we get from browsers. But we only get this behavior because of Layer 2, the user-agent stylesheet, which defines them. It’s built on the same idea that the font-size is bigger on heading elements, <h1> to <h6>, than any other HTML elements.

div { display: unset; /* = inline */ } span { display: unset; /* = inline */ } table { display: unset; /* = inline */ } /* or any other HTML element will get inline value */

This is, of course, unwanted behavior. The display property is the only exception we want to get from our browser. Because of that, I’m using the unique keyword value revert to bring back the default display value from the user-agent stylesheet..

The revert value is unique. First, it checks if there is a default style for the specific property in the user-agent stylesheet for the specific HTML element it is sitting on, and if it finds it, it takes it. If it doesn’t find it, revert works like the unset value, which means that if the property is an inherited property by default, it uses the inherit value; if not, it uses the initial value.

A diagram of all the CSS reset keywords Then those two rules are within a ruleset with a selector where you select almost everything. It looks like you’re selecting everything on the page via the universal tag selector (*), but then removing a handful of things. Is that right? Why target those specific things?

When I started to imagine “The New CSS Reset” I didn’t think I would need exceptions. It was a lot more straightforward in my imagination.


But when I started to create experiences, I was replacing my old CSS reset with my new CSS reset (without all the exceptions), and I saw some things that broke my old projects, which I tested.

The main things that broke were elements that can get sizes via width and height attributes — elements like <iframe>, <canvas>, <img>, <svg>, and <video>. Unfortunately, when I reset everything, the width and height of those elements are defined by the auto value, which is stronger and removes the effect of the elements’ width and the height attributes.

This can be problematic because we want the exact size to come from the HTML element in cases where we add the dimensions via the HTML width and height attributes. We prefer to get it from the HTML, not from the CSS, because when it comes from the CSS, it can cause glitches when the page is loading.

The only way I found to remove the reset effect for all those particular elements is to put them under the :not() selector. In this case, my new CSS reset is harmful and not helpful, and because of that, I removed the effect for these specific elements.

Keeping specificity at a minimum seems important in a reset, so you don’t find yourself fighting the reset itself. Is that the idea behind :where()?

Yes, the idea of the :where() is to remove the specificity. We don’t need to describe more significant specificity in our CSS only to override the CSS reset.

In general, I think we will soon see a lot more cases of :where() wrapping things to remove their specificity, and not only to replace multiple selectors.

It looks like some extra special care for children of <svg> is in there. What is that about?

The second case, :not(svg *) is done with a separate :not() only because it is for a different issue. Touching the inner elements of an SVG can break the visual image, and this is one of those things that there isn’t any reasonable cause to interrupt the browser.

Let the image be an image. I say.

After the big resetting part, it goes into some bits that are more opinionated. For example, there are no browser disagreements about the initial value of box-sizing, but you’re changing it anyway. I’m a fan of that one myself, but I’m curious about the philosophy of what goes into a reset and what doesn’t.

In general, when it comes to a CSS reset, I think it is an opinion thing. For example, Eric Meyer’s CSS Reset chooses to remove the styles of specific things, and other things like the display property, are uninterrupted, which as you already saw, I totally agree with.

About box-sizing, yes, that is opinionated. I have been a web developer for 15 years. In that time, I’ve seen many web developers struggling to understand the default behavior of box-sizing, which I got so used to in the past. When there were talking about adding it to the CSS Reset many years ago, web developers, many of whom had been in the industry for a long time, were afraid of this change because, in general, people are scared of change.

But these days, I almost do not see any project that isn’t resetting all elements to box-sizing: border-box. A browser’s engines can’t fix the default awkward behavior of the default box-sizing: content-box, because if they do so, they will break support for older websites. But for newer projects, including this piece is a must since we’re left to solve it on our own.

And again, this is totally opinionated.

Two other rulesets, the removing of list styles and collapsing borders, are also in the Eric Meyer’s reset, so they have been around a long time! Starting with the list styles, I can see wanting to wipe those out as lists are often used for things that don’t need a marker, like navigation. But it feels a bit contentious these days, as list-style: none; wipes out the semantics of a list, as well on iOS. Any concerns there?

The short answer: no. No concerns on my end. Here’ why.

If we choose not to reset list-style, it means we can’t use list elements for navigation. This also means that we won’t get any semantics for any other browsers.

And now, if I need to choose between most browsers gaining these semantics, and no browsers gaining these semantics, I’m choosing the former, as more browsers gain from it than they lose.

Can you see yourself adding to this over time? Like if you find yourself doing the same things on projects over and over? Setting the max-width on images feels like that to me. Again, it’s not something browsers disagree on now, but also something that pretty much every project does.

Of course. If this reset is missing something that I didn’t consider, I will add it and release a new version. But it needs to be like your example of max-width where there is no good case where we want an image to overflow its container.

Have you seen this new Cascade Layers stuff? Any thoughts on how that might factor in to CSS resets down the road?

I didn’t think about it until you asked me. The Cascade Layers module is an exciting feature. It still doesn’t have any support, but most browser engines have already put this feature under a flag, and this means that there is a good chance that we will see this feature one year from now supported in all evergreen browsers.

For those who haven’t heard about Cascade Layers yet, the idea is that @layer can override styles without creating stronger specificity because every layer that loads after it is automatically stronger than the previous layers.

When this feature arrives, we will load the “reset” layers first. For example: first, Normalize.css, then the-new-css-reset, and then the @layer styles of the project.

@layer normalize; /* Create 1st layer named “normalize” */ @layer the-new-css-reset; /* Create 2nd layer named “the-new-css-reset” */ @layer project-styles; /* Create 3rd layer named “project-styles” */

This should make sure that the bottom layer always beats the top. This also means that removing specificity with :where(), like I did, will no longer be necessary.

@layer is one of the most exciting future features coming to CSS, thanks to Miriam Suzanne, who is doing, as always, a fantastic job.

Thanks for taking the time Elad!

The post An Interview With Elad Shechter on “The New CSS Reset” appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Three-Digit Browser Versions in March 2022

Mon, 10/18/2021 - 12:53pm

We aren’t supposed to be doing any sort of decision-making based on browser User-Agent Strings. But, ya know, collectively, we do.

Karl Dubost notes that there is a significant change coming to them, notably moving the version integer past two digits:

According to the Firefox release calendar, during the first quarter of 2022 (probably March), Firefox Nightly will reach version 100. It will set Firefox stable release version around May 2022 (if it doesn’t change until then).

And Chrome release calendar sets a current date of March 29, 2022.

So, we’ll be looking at UAs like:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 KHTML, like Gecko) Chrome/ Safari/537.36

A bad RegEx will be getting some people for sure. But even string comparison will catch people, as Karl notes:

"80" < "99" // true "80" < "100" // false parseInt("80", 10) < parseInt("99", 10) // true parseInt("80", 10) < parseInt("100", 10) // true

Might wanna search the ol’ codebase for navigator.userAgent and see what you’re doing.

The post Three-Digit Browser Versions in March 2022 appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

How to Build a Nearly Headless WordPress Website

Mon, 10/18/2021 - 4:48am

I believe that a traditional WordPress theme should be able to work as effectively as a static site or a headless web app. The overwhelming majority of WordPress websites are built with a good ol’ fashioned WordPress theme. Most of them even have good caching layers, and dependency optimizations that make these sites run reasonably fast. But as developers, we have accomplished ways to create better results for our websites. Using a headless WordPress has allowed many sites to have faster load speeds, better user interactions, and seamless transitions between pages.

The problem? Maintenance. Let me show you another possibility!

Let’s start by defining what I mean by “Traditional” WordPress, “Headless” WordPress, and then “Nearly Headless” WordPress.

Traditional WordPress websites

Traditionally, a WordPress website is built using PHP to render the HTML markup that is rendered on the page. Each time a link is clicked, the browser sends another request to the server, and PHP renders the HTML markup for the site that was clicked.

This is the method that most sites use. It’s the easiest to maintain, has the least complexity in the tech, and with the right server-side caching tools it can perform fairly well. The issue is, since it is a traditional website, it feels like a traditional website. Transitions, effects, and other stylish, modern features tend to be more difficult to build and maintain in this type of site.


  1. The site is easy to maintain.
  2. The tech is relatively simple.
  3. There is great compatibility with WordPress plugins.


  1. Your site may feel a little dated as society expects app-like experiences in the browser.
  2. JavaScript tends to be a little harder to write and maintain since the site isn’t using a JavaScript framework to control the site’s behavior.
  3. Traditional websites tend to run slower than headless and nearly headless options.
Headless WordPress websites

A headless WordPress website uses modern JavaScript and some kind of server-side RESTful service, such as the WordPress REST API or GraphQL. Instead of building, and rendering the HTML in PHP, the server sends minimal HTML and a big ol’ JavaScript file that can handle rendering any page on the site. This method loads pages much faster, and opens up the opportunity to create really cool transitions between pages, and other interesting things.

No matter how you spin it, most headless WordPress websites require a developer on-hand to make any significant change to the website. Want to install a forms plugin? Sorry, you probably need a developer to set that up. Want to install a new SEO plugin? Nope, going to need a developer to change the app. Wanna use that fancy block? Too bad — you’re going to need a developer first.


  1. The website itself will feel modern, and fast.
  2. It’s easy to integrate with other RESTful services outside of WordPress.
  3. The entire site is built in JavaScript, which makes it easier to build complex websites.


  1. You must re-invent a lot of things that WordPress plugins do out of the box for you.
  2. This set up is difficult to maintain.
  3. Compared to other options, hosting is complex and can get expensive.

See “WordPress and Jamstack” for a deeper comparison of the differences between WordPress and static hosting.

I love the result that headless WordPress can create. I don’t like the maintenance. What I want is a web app that allows me to have fast load speeds, transitions between pages, and an overall app-like feel to my site. But I also want to be able to freely use the plugin ecosystem that made WordPress so popular in the first place. What I want is something headless-ish. Nearly headless.

I couldn’t find anything that fit this description, so I built one. Since then, I have built a handful of sites that use this approach, and have built the JavaScript libraries necessary to make it easier for others to create their own nearly headless WordPress theme.

Introducing Nearly Headless WordPress

Nearly headless is a web development approach to WordPress that gives you many of the app-like benefits that come with a headless approach, as well as the ease of development that comes with using a traditional WordPress theme. It accomplishes this with a small JavaScript app that will handle the routing and render your site much like a headless app, but has a fallback to load the exact same page with a normal WordPress request instead. You can choose which pages load using the fallback method, and can inject logic into either the JavaScript or the PHP to determine if the page should be loaded like this.

You can see this in action on the demo site I built to show off just what this approach can do.

For example, one of the sites implementing this method uses a learning management system called LifterLMS to sell WordPress courses online. This plugin has built-in e-commerce capabilities, and sets up the interface needed to host and place course content behind a paywall. This site uses a lot of LifterLMS’s built-in functionality to work — and a big part of that is the checkout cart. Instead of re-building this entire page to work inside my app, I simply set it to load using the fallback method. Because of this, this page works like any old WordPress theme, and works exactly as intended as a result — all without me re-building anything.


  1. This is easy to maintain, when set-up.
  2. The hosting is as easy as a typical WordPress theme.
  3. The website feels just as modern and fast as a headless website.


  1. You always have to think about two different methods to render your website.
  2. There are limited choices for JavaScript libraries that are effective with this method.
  3. This app is tied very closely to WordPress, so using third party REST APIs is more-difficult than headless.
How it works

For something to be nearly headless, it needs to be able to do several things, including:

  1. load a page using a WordPress request,
  2. load a page using JavaScript,
  3. allow pages to be identical, regardless of how they’re rendered,
  4. provide a way to know when to load a page using JavaScript, or PHP, and
  5. Ensure 100% parity on all routed pages, regardless of if it’s rendered with JavaScript or PHP.

This allows the site to make use of progressive enhancement. Since the page can be viewed with, or without JavaScript, you can use whichever version makes the most sense based on the request that was made. Have a trusted bot crawling your site? Send them the non-JavaScript version to ensure compatibility. Have a checkout page that isn’t working as-expected? Force it to load without the app for now, and maybe fix it later.

To accomplish each of these items, I released an open-source library called Nicholas, which includes a pre-made boilerplate.

Keeping it DRY

The biggest concern I wanted to overcome when building a nearly-headless app is keeping parity between how the page renders in PHP and JavaScript. I did not want to have to build and maintain my markup in two different places — I wanted a single source for as much of the markup as possible. This instantly limited which JavaScript libraries I could realistically use (sorry React!). With some research, and a lot of experimentation, I ended up using AlpineJS. This library kept my code reasonably DRY. There’s parts that absolutely have to be re-written for each one (loops, for example), but most of the significant chunks of markup can be re-used.

A single post template rendered with PHP might look like something like this:

<?php if ( have_posts() ) { while ( have_posts() ) { the_post(); if ( is_singular() ) { echo nicholas()->templates()->get_template( 'index', 'post', [ 'content' => Nicholas::get_buffer( 'the_content' ), 'title' => Nicholas::get_buffer( 'the_title' ), ] ); } } } ?>

That same post template rendered in JavaScript, using Alpine:

<template x-for="(post, index) in $store.posts" :key="index"> <?= nicholas()->templates()->get_template( 'index', 'post' ) ?> </template>

Both of them use the same PHP template, so all of the code inside the actual loop is DRY:

$title = $template->get_param( 'title', '' ); // Get the title that was passed into this template, fallback to empty string. $content = $template->get_param( 'content', '' ); // Get the cotent passed into this template, fallback to empty string. ?> <article x-data="theme.Post(index)"> <!-- This will use the alpine directive to render the title, or if it's in compatibility mode PHP will fill in the title directly --> <h1 x-html="title"><?= $title ?></h1> <!-- This will use the Alpine directive to render the post content, or if it's in compatibility mode, PHP will fill in the content directly --> <div class="content" x-html="content"><?= $content ?></div> </article>

Related: This Alpine.js approach is similar in spirit to the Vue.js approach covered in “How to Build Vue Components in a WordPress Theme” by Jonathan Land.

Detect when a page should run in compatibility mode

“Compatibility mode” allows you to force any request to load without the JavaScript that runs the headless version of the site. When a page is set to load using compatibility mode, the page will be loaded using nothing but PHP, and the app script never gets enqueued. This allows “problem pages” that don’t work as-expected with the app to run without needing to re-write anything.

There are several different ways you can force a page to run in compatibility mode — some require code, and some don’t. Nicholas adds a toggle to any post type that makes it possible to force a post to load in compatibility mode.

Along with this, you can manually add any URL to force it to load in compatibility mode inside the Nicholas settings.

These are a great start, but I’ve found that I can usually detect when a page needs to load in compatibility mode automatically based on what blocks are stored in a post. For example, let’s say you have Ninja Forms installed on your site, and you want to use the validation JavaScript they provide instead of re-making your own. In this case, you would have to force compatibility mode on any page that has a Ninja Form on it. You could manually go through and add each URL as you need them, or you can use a query to get all of the content that has a Ninja Forms block on the page. Something like this:

add_filter( 'nicholas/compatibility_mode_urls', function ( $urls ) { // Filter Ninja Forms Blocks $filtered_urls = Nicholas::get_urls_for_query( [ 'post_type' => 'any', 's' => 'wp:ninja-forms/form', // Find Ninja Forms Blocks ] ); return array_merge( $urls, $filtered_urls ); } );

That automatically adds any page with a Ninja Forms block to the list of URLs that will load using compatibility mode. This is just using WP_Query arguments, so you could pass anything you want here to determine what content should be added to the list.

Extending the app

Under the hood, Nicholas uses a lightweight router that can be extended using a middleware pattern much like how an Express app handles middleware. When a clicked page is routed, the system runs through each middleware item, and eventually routes the page. By default, the router does nothing; however, it comes with several pre-made middleware pieces that allows you to assemble the router however you see-fit.

A basic example would look something like this:

// Import WordPress-specific middleware import { updateAdminBar, validateAdminPage, validateCompatibilityMode } from 'nicholas-wp/middlewares' // Import generic middleware import { addRouteActions, handleClickMiddleware, setupRouter, validateMiddleware } from "nicholas-router"; // Do these actions, in this order, when a page is routed. addRouteActions( // First, validate the URL validateMiddleware, // Validate this page is not an admin page validateAdminPage, // Validate this page doesn't require compatibility mode validateCompatibilityMode, // Then, we Update the Alpine store updateStore, // Maybe fetch comments, if enabled fetchComments, // Update the history updateHistory, // Maybe update the admin bar updateAdminBar ) // Set up the router. This also uses a middleware pattern. setupRouter( // Setup event listener for clicks handleClickMiddleware )

From here, you could extend what happens when a page is routed. Maybe you want to scan the page for code to highlight, or perhaps you want to change the content of the <head> tag to match the newly routed page. Maybe even introduce a caching layer. Regardless of what you need to-do, adding the actions needed is as simple as using addRouteAction or setupRouter.

Next steps

This was a brief overview of some of the key components I used to implement the nearly headless approach. If you’re interested in going deeper, I suggest that you take my course at WP Dev Academy. This course is a step-by-step guide on how to build a nearly headless WordPress website with modern tooling. I also suggest that you check out my nearly headless boilerplate that can help you get started on your own project.

The post How to Build a Nearly Headless WordPress Website appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Expandable Sections Within a CSS Grid

Fri, 10/15/2021 - 6:18am

I love CSS Grid. I love how, with just a few lines of code, we can achieve fully responsive grid layouts, often without any media queries at all. I’m quite comfortable wrangling CSS Grid to produce interesting layouts, while keeping the HTML markup clean and simple.

But recently, I was presented with a unique UI conundrum to solve. Essentially, any given grid cell could have a button that would open up another, larger area that is also part of the grid. But this new larger grid cell needed to be:

  1. right below the cell that opened it, and
  2. full width.

Turns out there is a nice solution to it, and in the spirit of CSS Grid itself, it only involves a couple of lines of code. In this article, I’ll combine three one-line CSS Grid “tricks” to solve this. No JavaScript needed at all.

An explanation of the actual problem I need to solve

Here’s a minimalist UI example of what I needed to do:

This is our actual product card grid, as rendered in our Storybook component library:

Each product card needed a new “quick view” button added such that, when clicked, it would:

  • dynamically “inject” a new full-width card (containing more detailed product information) immediately below the product card that was clicked,
  • without disrupting the existing card grid (i.e. retain the DOM source order and the visual order of the rendered cards in the browser), and
  • still be fully responsive.

Hmmm… was this even possible with our current CSS Grid implementation?

Surely I would need to resort to JavaScript to re-calculate the card positions, and move them around, especially on browser resize? Right?

Google was not my friend. I couldn’t find anything to help me. Even a search of “quick view” implementations only resulted in examples that used modals or overlays to render the injected card. After all, a modal is usually the only choice in situations like this, as it focuses the user on the new content, without needing to disrupt the rest of the page.

I slept on the problem, and ultimately came to a workable solution by combining some of CSS Grid’s most powerful and useful features.

CSS Grid Trick #1

I was already employing the first trick for our default grid system, and the product card grid is a specific instance of that approach. Here’s some (simplified) code:

.grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, 20rem); }

The “secret sauce” in this code is the grid-template-columns: repeat(auto-fit, 20rem); which gives us a grid with columns (20rem wide in this example) that are arranged automatically in the available space, wrapping to the next row when there’s not enough room.

Curious about auto-fit vs auto-fill? Sara Soueidan has written a wonderful explanation of how this works. Sara also explains how you can incorporate minmax() to enable the column widths to “flex” but, for the purposes of this article, I wanted to define fixed column widths for simplicity.

CSS Grid Trick #2

Next, I had to accommodate a new full-width card into the grid:

.fullwidth { grid-column: 1 / -1; }

This code works because grid-template-columns in trick #1 creates an “explicit” grid, so it’s possible to define start and end columns for the .fullwidth card, where 1 / -1 means “start in column 1, and span every column up to the very last one.”

Great. A full-width card injected into the grid. But… now we have gaps above the full-width card.

CSS Grid Trick #3

Filling the gaps — I’ve done this before with a faux-masonry approach:

.grid { grid-auto-flow: dense; }

That’s it! Required layout achieved.

The grid-auto-flow property controls how the CSS Grid auto-placement algorithm works. In this case, the dense packing algorithm tries to fills in holes earlier in the grid.

  • All our grid columns are the same width. Dense packing also works if the column widths are flexible, for example, by using minmax(20rem, 1f).
  • All our grid “cells” are the same height in each row. This is the default CSS Grid behavior. The grid container implicitly has align-items: stretch causing cells to occupy 100% of the available row height.

The result of all this is that the holes in our grid are filled — and the beautiful part is that the original source order is preserved in the rendered output. This is important from an accessibility perspective.

See MDN for a complete explanation of how CSS Grid auto-placement works.

The complete solution CodePen Embed Fallback

These three combined tricks provide a simple layout solution that requires very little CSS. No media queries, and no JavaScript needed.

But… we do still need JavaScript?

Yes, we do. But not for any layout calculations. It is purely functional for managing the click events, focus state, injected card display, etc.

For demo purposes in the prototype, the full-width cards have been hard-coded in the HTML in their correct locations in the DOM, and the JavaScript simply toggles their display properties.

In a production environment, however, the injected card would probably be fetched with JavaScript and placed in the correct location. Grid layouts for something like products on an eCommerce site tend to have very heavy DOMs, and we want to avoid unnecessarily bloating the page weight further with lots of additional “hidden” content.

Quick views should be considered as a progressive enhancement, so if JavaScript fails to load, the user is simply taken to the appropriate product details page.

Accessibility considerations

I’m passionate about using correct semantic HTML markup, adding aria- properties when absolutely necessary, and ensuring the UI works with just a keyboard as well as in a screen reader.

So, here’s a rundown of the considerations that went into making this pattern as accessible as possible:

  • The product card grid uses a <ul><li> construct because we’re displaying a list of products. Assistive technologies (e.g. screen readers) will therefore understand that there’s a relationship between the cards, and users will be informed how many items are in the list.
  • The product cards themselves are <article> elements, with proper headings, etc.
  • The HTML source order is preserved for the cards when the .fullwidth card is injected, providing a good natural tab order into the injected content, and out again to the next card.
  • The whole card grid is wrapped in an aria-live region so that DOM changes are announced to screen readers.
  • Focus management ensures that the injected card receives keyboard focus, and on closing the card, keyboard focus is returned to the button that originally triggered the card’s visibility.

Although it isn’t demonstrated in the prototype, these additional enhancements could be added to any production implementation:

  • Ensure the injected card, when focused, has an appropriate label. This could be as simple as having a heading as the first element inside the content.
  • Bind the ESC key to close the injected card.
  • Scroll the browser window so that the injected card is fully visible inside the viewport.
Wrapping up

So, what do you think?

This could be a nice alternative to modals for when we want to reveal additional content, but without hijacking the entire viewport in the process. This might be interesting in other situations as well — think photo captions in an image grid, helper text, etc. It might even be an alternative to some cases where we’d normally reach for <details>/<summary> (as we know those are only best used in certain contexts).

Anyway, I’m interested in how you might use this, or even how you might approach it differently. Let me know in the comments!


Firstly, I’m really glad that this article has proved helpful to other front-end developers. I knew I couldn’t have been the only one to face a similar conundrum.

Secondly, following some constructive feedback, I’ve added a strikethrough to some of the specific accessibility considerations above, and updated my CodePen demo with the following changes:

  • There is no need for the card grid to be wrapped in an aria-live region. Instead, I have made the quick view open and close buttons behave as “toggle” buttons, with appropriate aria-expanded and aria-controls attributes. I do use this pattern for disclosure widgets (show/hide, tabs, accordions) but in this case, I was imagining a behavior more similar to a modal interface, albeit inline rather than an overlay. (Thanks to Adrian for the tip!)
  • I am no longer programatically focusing on the injected card. Instead, I simply add a tabindex="0" so a keyboard user can choose whether or not to move to the injected card, or they can simply close the “toggle” button again.
  • I still believe that using a <ul><li> construct for the grid is a suitable approach for a list of product cards. The afforded semantics indicate an explicit relationship between the cards.

The post Expandable Sections Within a CSS Grid appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Different Degrees of Custom Property Usage

Thu, 10/14/2021 - 4:35am

One way to work with Custom Properties is to think of them as design tokens. Colors, spacings, fonts, and whatnot. You set them at the root of the page and use them throughout your CSS. Very useful, and the classic use case for not only Custom Properties but for preprocessor variables for the last one million years.

Another way to work with Custom Properties, which can be done in addition to the design tokens approach, is to go a lot harder and use them for every major unique styling choice on any given element.

Imagine you have a Card like this (simplified for demonstration sake, of course):

.card { background: hsl(200deg 15% 73%); border: 4px solid rgb(255 255 255 / 0.5); padding: 2rem; border-radius: 8px; } .card > h2 { margin: 0 0 1rem 0; border-bottom: 3px solid rgba(0 0 0 / 0.2); }


But then when you inevitably make variations of the card, you’re on your own to override these rulesets. A CSS Custom Property approach can be like:

.card { --card-background: hsl(200deg 15% 73%); --card-border: 4px solid rgb(255 255 255 / 0.5); --card-padding: 2rem; --card-border-radius: 8px; --card-title-margin: 0 0 1rem 0; --card-title-border: 3px solid rgba(0 0 0 / 0.2); background: var(--card-background); border: var(--card-border); padding: var(--card-padding); border-radius: var(--card-border-radius); } .card > h2 { margin: var(--card-title-margin); border-bottom: var(--card-title-border); }

A little more verbose, for now, but look what happens when we want to do a variation:

.card-variation { --card-background: purple; --card-padding-block: 2.5rem; --card-title-margin: 0 0 2rem 0; }

Here are three clear advantages right off the bat:

  • I’m only changing values that I’ve clearly set up to be changed. My main Card prototype maintains the integrity I want it to keep.
  • I can style children of the variation without having to re-write those selectors correctly.
  • I can now pass in styling overrides from the style attribute in the HTML for quick, one-off variations.
CodePen Embed Fallback Less verbose with fallbacks

Rather than declaring the Custom Properties at the top and then using them right below, I can do both at the same time like this:

.card { background: var(--card-background, hsl(200deg 15% 73%)); border: var(--card-border, 4px solid rgb(255 255 255 / 0.5)); padding: var(--card-padding, 2rem); border-radius: var(--card-border-radius, 8px); } .card > h2 { margin: var(--card-title-margin, 0 0 1rem 0); border-bottom: var(--card-title-border, 3px solid rgba(0 0 0 / 0.2)); }

Now if something like --card-background does happen to get set, it will override the fallback value here. I don’t completely love this, because it means elements above .card can override it. That might be what you want, but it’s not exactly the same as declaring the values at the .card level to begin with. No strong opinions here.

Breaking it up even more

An example here is that you might want to individually control padding.

.card { --card-padding-block: 2rem; --card-padding-inline: 2rem; --card-padding: var(--card-padding-block) var(--card-padding-inline); padding: var(--card-padding); }

Now a variation can control just a part of the padding if I want:

.card-variation { --card-padding-inline: 3rem; }

You gotta be careful of the big gotcha though. Meaning if you declare all these at the root, this isn’t going to work, because those nested properties have already been resolved. But so long as it’s first declared on .card, you’ll be fine here.

Too far?

Say you wanted super ultimate control over every part of a value. For example:

html { --color-1-h: 200deg; --color-1-s: 15%; --color-1-l: 73%; --color-1-hsl: var(--color-1-h) var(--color-1-s) var(--color-1-l); --color-1: hsl(var(--color-1-hsl)); }

That’s kinda neat, but it’s likely too far. Colors are almost certainly going to be declared at the root and left alone, so the great gotcha is going to make overriding the low-level child properties impossible. Besides, if you have a --color-1, you probably have a 2-9 (or more) as well, which is all well and good because there is far more delicate design magic to a color system than simple mathematical manipulations of color parts.

Deliverable design systems?

There is no doubt that Tailwind has enjoyed a lot of popularity. It uses an atomic approach where a slew of HTML classes control one property each. I’d argue some of that popularity is driven by the fact that if you choose from these pre-configured classes, that the design ends up fairly nice. You can’t go off the rails. You’re choosing from a limited selection of values that have been designed to look good.

I wouldn’t go as far as to say that a Custom Properties heavy-based approach to styling is exactly the same. For example, you’ll still need to think of a class name abstraction rather than apply styling directly to the HTML element. But, it might enjoy some of the same constraints/limitations that make Tailwind and other atomic class approaches desirable. If you can only pick from a pre-defined set of --spacing-x values, --color-x values, and --font-x values, you might achieve a more cohesive design than you would have otherwise.

Personally, I’ve found inching toward a design system that is more heavily based on Custom Properties feels good — if nothing else to make variations and overrides more sensible to manage.

What about third-party design systems delivering what they deliver as… nothing but a big ol’ set of Custom Properties to use at your leisure?


Third-party deliverables don’t even have to be the entire kitchen sink like this. For example, Adam Argyle’s transition.style provides a “Hackpack” that is nothing but Custom Properties of transition animation helpers.

Understandabilty cost

One pushback I’ve heard against this more all-in approach on Custom Properties is newcomer understandability. If you wrote the system, it probably makes perfect sense to you. But it’s an additional abstraction on top of CSS. CSS knowledge is shared by all, bespoke systems knowledge is only shared by the people actively working on it.

MY code that uses CSS custom properties to abstract things: perfect, good, readable

YOUR code that uses CSS custom properties to abstract things: messy, impossible to understand, i die

— Chris Coyier (@chriscoyier) February 16, 2021

Rolling in fresh to a system heavily using Custom Properties is going to have a heck of a learning curve.


The post Different Degrees of Custom Property Usage appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Bonsai Browser

Wed, 10/13/2021 - 12:50pm

Web-browser for research that helps programmers think clearly.

With Bonsai, rather than being like, I’m going to go use my web browser now, you hit Option + Space and it brings up a browser. It’s either full-screen or a very minimal float-over-everything window. You can visually organize things into Workspaces. I can see it being quite good for research, but also just getting you to think differently about what a “web browser” interface can be and do for you.

Perhaps for what we’re losing in browser engine diversity, we’ll gain in browser UI/UX diversity.

Direct Link to ArticlePermalink

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

CSS is Going Gosh-Darned Hog Wild, I Tell Ya What

Wed, 10/13/2021 - 5:36am

As someone just sittin’ back watching CSS evolve, it feels like we’re at one of the hottest moments of innovation in CSS history. It was really something when we got flexbox across all browsers, and not terribly long after, grid. They changed the game from CSS feeling like an awkward collection of tricks to something more sensible and of the times.

That feeling keeps getting more and more true all the time. Just in the last short while, here’s a list of things happening.

⚠️&#x1f937; The syntax might not be exactly like any of the snippets below this when they ship for real. &#x1f937;⚠️

Native Nesting

Native Nesting has become a First Public Working Draft, meaning it’s a lot closer to becoming a real thing than ever before. A lot of people use preprocessors just for the convenience of nesting and this should be helpful for those looking to simplify their build tools to avoid that.

I especially like how you can nest conditional rules.

.card { & .title { } & .body { } @media (min-inline-size > 1000px) { & { } } @nest body.dark & { } }

I’ve heard whispers of this being a workable idea too, which avoids requiring the & on simple selectors and also avoids @nest at all.

.card {{ .title { } .body { } body.dark & { } }}

Container Queries

Container Queries is just an Editor’s Draft (CSS Containment Module Level 3) at the moment, but they already have an implementation in Chrome (with a flag). These are a huge deal as they allow us to make styling decisions based on the size of the container itself, which in today’s component-driven world, is just a absolutely good idea.

See the code for a simple example site (might look weird if you don’t have the flag on in Chrome).

/* Set containment on the parent you're querying */ .card-container { /* Both work right now, not sure which is right */ contain: style layout inline-size; container: inline-size; } .card { display: flex; } @container (max-width: 500px) { /* Must style a child, not the container */ .card { flex-direction: column; } } Container Units

Container Units have a draft spec as well, which, to me, nearly doubles the usefulness of container queries. The idea is that you have a unit that is based on the size of the container (width, height, or “inline-size” / “block-size”). I imagine the qi unit is the most useful.

Hopefully soon, we’ll be writing container-scoped CSS that styles itself based on the size of itself and can pass that size for other properties to use inside. The font-size property is an easy example of how useful this is (fonts that scale in size based on their container), but I’m sure container units will be used for all sorts of stuff, like gap, padding, and who knows what all else.

/* Set containment on the parent you're querying */ .card-container { /* Both work right now, not sure which is right */ contain: style layout inline-size; container: inline-size; } .card h2 { font-size: 1.5rem; /* fallback */ } @container type(inline-size) { .card h2 { font-size: clamp(14px, 1rem + 2qi, 56px) } } Cascade Layers

Cascade Layers (in Working Draft spec) introduces a whole new paradigm for which CSS selectors win in the Cascade. Right now it’s mostly a specificity contest. Selectors with the highest specificity win, only losing out to inline styles and specific rules with !important clauses. But with layers, any matching selector on a higher layer would win.

@layer base; @layer theme; @layer utilities; /* Reset styles with no layer (super low) */ * { box-sizing: border-box; } @layer theme { .card { background: var(--card-bg); } } @layer base { /* Most styles? */ } @layer utilities { .no-margin { margin: 0; } } @when

Tab Atkins’ proposal for CSS When/Else Rules has been accepted and is a way of expressing @media and @supports queries in such a way that you can much more easily express else conditions. While media queries already have some ability to do logic, doing mutually exclusive queries has long been hard to express and this makes it very simple.

@when media(width >= 400px) and media(pointer: fine) and supports(display: flex) { /* A */ } @else supports(caret-color: pink) and supports(background: double-rainbow()) { /* B */ } @else { /* C */ } Scoping

The idea of Scoped Styles (this one is an Editor’s Draft) is that it provides a syntax for writing a block of styles that only apply to a certain selector and within, but also have the ability to stop the scope, creating a scope donut.

My favorite part of all this is the “proximity” strength stuff. Miriam explains like this:

.light-theme a { color: purple; } .dark-theme a { color: plum; } <div class="dark-theme"> <a href="#">plum</a> <div class="light-theme"> <a href="#">also plum???</a> </div> </div>

Good point right?! There is no great way to express that you want the proximity of that link to the .light-theme to win. Right now, the fact that the specificity of both themes is the same, but .dark-theme comes after — so it wins. Hopefully scoped styles helps with this angle, too.

@scope (.card) to (.content) { :scope { display: grid; grid-template-columns: 50px 1fr; } img { filter: grayscale(100%); border-radius: 50%; } .content { ... } } /* Proximity help! */ @scope (.light-theme) { a { color: purple; } } @scope (.dark-theme) { a { color: plum; } }

You can’t use anything on this list right now on your production websites. After all these years attempting to follow this kind of thing, I remain ignorant to how it all ultimately goes. I think the specs need to be finished and agreed upon first. Then browsers pick them up, hopefully more than one. And once they have, then I think the specs can be finalized?

I dunno. Maybe some of it will die. Maybe some of it will happen super fast, and some super slow. Likely, some of it will drop in some browsers but not others. Hey, at least we have evergreen browsers now so that when things do drop, it happens fast. I feel like right now we’re in a stage where most of the biggest and best CSS features are supported across all browsers, but with all this coming, we’ll be headed into a phase where support for the latest-and-greatest will be much more spotty.

The post CSS is Going Gosh-Darned Hog Wild, I Tell Ya What appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Less Absolute Positioning With Modern CSS

Wed, 10/13/2021 - 4:42am

Ahmad Shadeed blogs the sentiment that we might not need to lean on position: absolute as much as we might have in the past. For one thing: stacking elements. For example, if you have a stack of elements that should all go on top of each other…

.stack { display: grid; } .stack > * { grid-area: 1 / -1; }

All the elements occupy the same grid cell at that point, but you can still use alignment and justification to move stuff around and get it looking and behaving how you want.

What you are really saying with position: absolute is I want this element to be entirely removed from the flow such that it doesn’t affect other elements and other elements don’t affect it. Sometimes you do, but arguably less often than your existing CSS muscle memory would have you believe.

I’ll snag one of Ahmad’s idea here:

CodePen Embed Fallback

Both the tag and the title are positioned in a way we might automatically think of using absolute positioning. But again, something like CSS Grid has all of the alignment features we need to not only stack them vertically, but place them right where we want.

Direct Link to ArticlePermalink

The post Less Absolute Positioning With Modern CSS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Jamstack Developers’ Favorite Frameworks of 2021

Tue, 10/12/2021 - 4:30am

Which new framework should I learn this year? Is it time to ditch my CMS? What tools should I pick up if I want to scale my site to an audience of millions? The 2021 Jamstack Community Survey is here with answers to those questions and more. 

For the past two years, Netlify has conducted the Jamstack Community Survey to better understand our growing group of developers—the insights inform our services, and they also help developers learn from one another. Our survey data provides a sense of best practices as well as an idea of what else is happening in the community.

What we’re seeing this year: it’s never been a better time to be a developer in the Jamstack community! Jamstack has gone mainstream and the ecosystem is thriving. Jamstack is becoming the default choice for web developers at all stages of their careers across different geographies and touching all industries, and the community is only getting bigger. We also saw a huge rise in the percentage of students in our community over the last year, a great sign for a growing ecosystem.

In 2021, Netlify received more than 7,000 responses to the Jamstack Community Survey. This is more than double the number of responses we received in 2020, confirming the growth of the Jamstack community. 

Here are a few of the highlights from our more technical findings…

Jamstack developers work differently at scale.

32% of Jamstack developers are building sites for audiences of millions of users, but the tools they use and their development priorities are different: for instance, they are more likely to specialize in front-end or back-end work, and they are more likely to consider mobile devices a key target.

JavaScript dominates programming languages for the web—but TypeScript is giving it a run for its money.

For 55% of developers, JavaScript is their primary language. But TypeScript is coming from behind with a growing share.

Figma is almost the only design tool that matters.

When it comes to design tools, more than 60% of survey respondents use Figma and are happier with it than the users of any other design tool we asked about.

React remains in the lead.

React continues to top the charts comparing major frameworks in usage and satisfaction, and Next.js continues to grow alongside it. But we also saw growth and higher satisfaction from a challenger framework, Vue.

WordPress leads in CMS usage.

WordPress remains the clear leader as a content management system, but it’s not well-liked as a standalone solution. When used in a headless configuration, users reported much higher satisfaction. This was a breakout year for other headless CMSs like Sanity and Strapi.

And that’s just a taste of what we learned. To view the complete findings of the 2021 Jamstack Community Survey, visit our survey website

The post Jamstack Developers’ Favorite Frameworks of 2021 appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

WooCommerce + Google Analytics

Tue, 10/12/2021 - 4:28am

Google Analytics is powerful analytics software. A common way to use it is to just slap the JavaScript snippet on every page template you have and let it collect basic data about unique visitors and pageviews and such. That’s useful, but it’s also the bare minimum. Say there is an important button on your site. Leveling up, you could send custom events to track users clicking on that button. Those are the analytics that matter the most.

Further down that road is tracking eCommerce analytics. This is extra-tricky, as it requires you sending events to Google Analytics for sales, instances of adding/removing things from cart, views on products… all sorts of stuff. If you don’t do all that (and do it right), you don’t get good analytics information.

Yet another reason I like WooCommerce! Instead of this analytics integration being a monumental effort and a substantial bit of technical debt to maintain, you just install the WooCommerce Google Analytics plugin and… that’s it. Also: it’s free.

I’ve had this integrated for months right here on CSS-Tricks, and I can confirm:

  1. It was close to zero effort.
  2. It just works.
The plugin installed and activated! The one bit of config is adding this ID, which is easy to find in Google Analytics, your own code, or another Google Analytics plugin.

Now, to be clear, WooCommerce has its own analytics built right in. If what you are interested in is sales reports and top sellers and stuff like that, those are the dashboards I’d be looking at. But there are some things that the built-in WooCommerce analytics just don’t do. For example, I can check out the sales funnel on Google Analytics now:

30 days of traffic starting from all unique visitors to sessions where they actually bought something.

CSS-Tricks isn’t exactly an eCommerce-focused website, so the funnel there starts super wide and gets super (super) tiny — but hey, at least I can confirm that and see it with my own eyes. Plus, I can glean some insights here, like the fact that 66/70 people completed checkout once they got there (pretty good), but only 70/525 even proceeded to checkout after adding to cart, so I’m losing a lot of people at that stage.

Here is some more interesting data that only Google Analytics knows:

Of 66 sales, 56 of them came from returning visitors, not new visitors. So people tend to not buy on first look, but do come back later. I’m not sure if that means I should be making things more enticing for those new visitors or if I should lean into reminding people about it after they’ve looked at it. Either way, now I know because I have the data.

There is data in the WooCommerce analytics that I’d normally have to go to Google Analytics to see. I can see individual orders. I can see what the top sellers are and compare product sales over different time periods. All useful stuff, and you might appreciate having all this in one place.

Again, my favorite part about this is having all this data. It feels like it should have been hard-won to get, but all it took was clicking a few buttons. That’s why I never regret just doing things the standard WordPress and WooCommerce way! Things tend to just work!

The post WooCommerce + Google Analytics appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Those “Get The App” Banners

Mon, 10/11/2021 - 12:12pm

Why would a company promote a native app over their perfectly usable website?

We’d have to ask them, I suppose. But it’s hard not to see this push to native as a matter of priorities: that these companies consider native applications worthy of their limited time, resources, and money. They’re a worthy investment, to hear these banners tell it.

—Ethan Marcotte, “Locus.”

Ethan shows off that the web is absolutely covered in amazingly obtrusive “get the app” banners, often covering up perfectly usable websites.

What I always think of when I see banners like this is a comment I remember Tim Holman making one time that was something like, What would it feel like to work on the web team at a company like this? There are a bunch of people in the world that work on websites that only exist behind big stupid banners telling people not to use the thing they work on all day.

Sure would be nice to get to a point where companies didn’t really care which method you used, because it’s probably all built with one technology anyway and is fully capable of anything the device can do.

The post Those “Get The App” Banners appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Cash (Tiny jQuery Alternative)

Mon, 10/11/2021 - 10:28am

The README for Cash is straightforward:

Cash is an absurdly small jQuery alternative for modern browsers (IE11+) that provides jQuery-style syntax for manipulating the DOM. Utilizing modern browser features to minimize the codebase, developers can use the familiar chainable methods at a fraction of the file size. 100% feature parity with jQuery isn’t a goal, but Cash comes helpfully close, covering most day to day use cases.

6 KB minified and gzipped, which is even smaller than Zepto. Zepto’s whole point was a smaller jQuery, but it hasn’t been touched in a bunch of years, so there is that too.

I wonder how much smaller Cash would be if it dropped IE 11 support.

jQuery is still used on an absolute ton of sites, only just recently having peaked in usage and showing signs of decline. That must be because it comes on most WordPress sites, right?! That’s like 42% of all sites right there.

Anyway, if you tend to reach for jQuery just for some convenient APIs, Cash looks like a nice alternative. I don’t blame you either. Typing $ instead of document.querySelectorAll still feels good to me, not to mention all the other fanciness tucked behind that dollar sign function.

CodePen Embed Fallback

Also worth mentioning: if you’re looking to straight-up remove jQuery from a project, replace-jquery might be worth a look:

Automatically find jQuery methods from existing projects and generate vanilla js alternatives.

The post Cash (Tiny jQuery Alternative) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Better Collaboration With Pull Requests

Mon, 10/11/2021 - 4:29am

This article is part of our “Advanced Git” series. Be sure to follow us on Twitter or sign up for our newsletter to hear about the next articles!

In this third installment of our “Advanced Git” series, we’ll look at pull requests — a great feature which helps both small and larger teams of developers. Pull requests not only improve the review and the feedback process, but they also help tracking and discussing code changes. Last, but not least, pull requests are the ideal way to contribute to other repositories you don’t have write access to.

Advanced Git series: What are pull requests?

First of all, it’s important to understand that pull requests are not a core Git feature. Instead, they are provided by the Git hosting platform you’re using: GitHub, GitLab, Bitbucket, AzureDevops and others all have such a functionality built into their platforms.

Why should I create a pull request?

Before we get into the details of how to create the perfect pull request, let’s talk about why you would want to use this feature at all.

Imagine you’ve just finished a new feature for your software. Maybe you’ve been working in a feature branch, so your next step would be merging it into the mainline branch (master or main). This is totally fine in some cases, for example, if you’re the only developer on the project or if you’re experienced enough and know for certain your team members will be happy about it.

By the way: If you want to know more about branches and typical branching workflows, have a look at our second article in our “Advanced Git” series: “Branching Strategies in Git.”

However, what if your changes are a bit more complex and you’d like someone else to look at your work? This is what pull requests were made for. With pull requests you can invite other people to review your work and give you feedback. 

Once a pull request is open, you can discuss your code with other developers. Most Git hosting platforms allow other users to add comments and suggest changes during that process. After your reviewers have approved your work, it might be merged into another branch.

Having a reviewing workflow is not the only reason for pull requests, though. They come in handy if you want to contribute to other repositories you don’t have write access to. Think of all the open source projects out there: if you have an idea for a new feature, or if you want to submit a patch, pull requests are a great way to present your ideas without having to join the project and become a main contributor.

This brings us to a topic that’s tightly connected to pull requests: forks.

Working with forks

A fork is your personal copy of an existing Git repository. Going back to our Open Source example: your first step is to create a fork of the original repository. After that, you can change code in your own, personal copy.

After you’re done, you open a pull request to ask the owners of the original repository to include your changes. The owner or one of the other main contributors can review your code and then decide to include it (or not).

Important Note: Pull requests are always based on branches and not on individual commits! When you create a pull request, you base it on a certain branch and request that it gets included.

Making a reviewer’s life easier: How to create a great pull request

As mentioned earlier, pull requests are not a core Git feature. Instead, every Git platform has its own design and its own idea about how a pull request should work. They look different on GitLab, GitHub, Bitbucket, etc. Every platform has a slightly different workflow for tracking, discussing, and reviewing changes.

Desktop GUIs like the Tower Git client, for example, can make this easier: they provide a consistent user interface, no matter what code hosting service you’re using.

Having said that, the general workflow is always the same and includes the following steps:

  1. If you don’t have write access to the repository in question, the first step is to create a fork, i.e. your personal version of the repository.
  2. Create a new local branch in your forked repository. (Reminder: pull requests are based on branches, not on commits!)
  3. Make some changes in your local branch and commit them.
  4. Push the changes to your own remote repository.
  5. Create a pull request with your changes and start the discussion with others.

Let’s look at the pull request itself and how to create one which makes another developer’s life easier. First of all, it should be short so it can be reviewed quickly. It’s harder to understand code when looking at 3,000 lines instead of 30 lines. 

Also, make sure to add a good and self-explanatory title and a meaningful description. Try to describe what you changed, why you opened the pull request, and how your changes affect the project. Most platforms allow you to add a screenshot which can help to demonstrate the changes.

Approve, merge, or decline?

Once your changes have been approved, you (or someone with write access) can merge the forked branch into the main branch. But what if the reviewer doesn’t want to merge the pull request in its current state? Well, you can always add new commits, and after pushing that branch, the existing pull request is updated.

Alternatively, the owner or someone else with write access can decline the pull request when they don’t want to merge the changes.

Safety net for developers

As you can see, pull requests are a great way to communicate and collaborate with your fellow developers. By asking others to review your work, you make sure that only high-quality code enters your codebase. 

If you want to dive deeper into advanced Git tools, feel free to check out my (free!) “Advanced Git Kit”: it’s a collection of short videos about topics like branching strategies, Interactive Rebase, Reflog, Submodules and much more.

Advanced Git series:

The post Better Collaboration With Pull Requests appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

©2003 - Present Akamai Design & Development.