Tag: Guide

How-to guide for creating edge-to-edge color bars that work with a grid

Hard-stop gradients are one of my favorite CSS tricks. Here, Marcel Moreau combines that idea with CSS grid to solve an issue that’s otherwise a pain in the butt. Say you have like a 300px right sidebar on a desktop layout with a unique background color. Easy enough. But then say you want that background color to stretch to the right edge of the browser window even though the grid itself is width-constrained. Tricker.

Direct Link to ArticlePermalink

The post How-to guide for creating edge-to-edge color bars that work with a grid appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , ,

A Complete Guide to Dark Mode on the Web

Dark mode has gained a lot of traction recently. Like Apple, for instance, has added dark mode to its iOS and MacOS operating systems. Windows and Google have done the same. 

DuckDuckGo’s light and dark themes

Let’s get into dark mode in the context of websites. We’ll delve into different options and approaches to implementing a dark mode design and the technical considerations they entail. We’ll also touch upon some design tips along the way.


Toggling Themes

The typical scenario is that you already have a light theme for your site, and you’re interested in making a darker counterpart. Or, even if you’re starting from scratch, you’ll have both themes: light and dark. One theme should be defined as the default that users get on first visit, which is the light theme in most cases (though we can let the user’s browser make that choice for us, as we’ll see). There also should be a way to switch to the other theme (which can be done automatically, as we’ll also see) — as in, the user clicks a button and the color theme changes.

There several approaches to go about doing this:

Using a Body Class

The trick here is to swap out a class that can be a hook for changing a style anywhere on the page.

<body class="dark-theme || light-theme">

Here’s a script for a button that will toggle that class, for example:

// Select the button const btn = document.querySelector('.btn-toggle');  // Listen for a click on the button btn.addEventListener('click', function() {   // Then toggle (add/remove) the .dark-theme class to the body   document.body.classList.toggle('dark-theme');   })

Here’s how we can use that idea:

<body>   <button class="btn-toggle">Toggle Dark Mode</button>   <h1>Hey there! This is just a title</h2>   <p>I am just a boring text, existing here solely for the purpose of this demo</p>   <p>And I am just another one like the one above me, because two is better than having only one</p>   <a href="#">I am a link, don't click me!</a> </body>

The general idea of this approach is to style things up as we normally would, call that our “default” mode, then create a complete set of color styles using a class set on the <body>  element we can use as a “dark” mode.

Let’s say our default is a light color scheme. All of those “light” styles are written exactly the same way you normally write CSS. Given our HTML, let’s apply some global styling to the body and to links.

body {   color: #222;   background: #fff; } a {   color: #0033cc; }

Good good. We have dark text (#222) and dark links (#0033cc) on a light background (#fff). Our “default” theme is off to a solid start.

Now let’s redefine those property values, this time set on a different body class:

body {   color: #222;   background: #fff; } a {   color: #0033cc; } 
 /* Dark Mode styles */ body.dark-theme {   color: #eee;   background: #121212; } body.dark-theme a {   color: #809fff; }

Dark theme styles will be descendants of the same parent class — which is .dark-theme in this example — which we’ve applied to the <body> tag.

How do we “switch” body classes to access the dark styles? We can use JavaScript! We’ll select the button class (.btn-toggle), add a listener for when it’s clicked, then add the dark theme class (.dark-theme) to the body element’s class list. That effectively overrides all of the “light” colors we set, thanks to the cascade and specificity. 

Here’s the complete code working in action. Click the toggle button to toggle in and out of dark mode.

Using Separate Stylesheets

Rather than keeping all the styles together in one stylesheet, we could instead toggle between stylesheets for each theme. This assumes you have full stylesheets ready to go.

For example, a default light theme like light-theme.css:

/* light-theme.css */ 
 body {   color: #222;   background: #fff; } a {   color: #0033cc; }

Then we create styles for the dark theme and save them in a separate stylesheet we’re calling dark-theme.css.

/* dark-theme.css */ 
 body {   color: #eee;   background: #121212; } body a {   color: #809fff; }

This gives us two separate stylesheets — one for each theme — we can link up in the HTML <head> section. Let’s link up the light styles first since we’re calling those the default.

<!DOCTYPE html> <html lang="en"> <head>   <!-- Light theme stylesheet -->   <link href="light-theme.css" rel="stylesheet" id="theme-link"> </head> 
 <!-- etc. --> 
 </html>

We are using a #theme-link ID that we can select with JavaScript to, again, toggle between light and dark mode. Only this time, we’re toggling files instead of classes.

// Select the button const btn = document.querySelector(".btn-toggle"); // Select the stylesheet <link> const theme = document.querySelector("#theme-link");  // Listen for a click on the button btn.addEventListener("click", function() {   // If the current URL contains "ligh-theme.css"   if (theme.getAttribute("href") == "light-theme.css") {     // ... then switch it to "dark-theme.css"     theme.href = "dark-theme.css";   // Otherwise...   } else {     // ... switch it to "light-theme.css"     theme.href = "light-theme.css";   } });

Using Custom Properties

We can also leverage the power of CSS custom properties to create a dark theme! It helps us avoid having to write separate style rulesets for each theme, making it a lot faster to write styles and a lot easier to make changes to a theme if we need to.

We still might choose to swap a body class, and use that class to re-set custom properties:

// Select the button const btn = document.querySelector(".btn-toggle"); 
 // Listen for a click on the button btn.addEventListener("click", function() {   // Then toggle (add/remove) the .dark-theme class to the body   document.body.classList.toggle("dark-theme"); });

First, let’s define the default light color values as custom properties on the body element:

body {   --text-color: #222;   --bkg-color: #fff;   --anchor-color: #0033cc; }

Now we can redefine those values on a .dark-theme body class just like we did in the first method:

body.dark-theme {   --text-color: #eee;   --bkg-color: #121212;   --anchor-color: #809fff; }

Here are our rulesets for the body and link elements using custom properties:

body {   color: var(--text-color);   background: var(--bkg-color); } a {   color: var(--anchor-color); }

We could just as well have defined our custom properties inside the document :root. That’s totally legit and even common practice. In that case, all the default theme styles definitions would go inside :root { } and all of the dark theme properties go inside :root.dark-mode { }.

Using Server-Side Scripts

If we’re already working with a server-side language, say PHP, then we can use it instead of JavaScript. This is a great approach if you prefer working directly in the markup.

<?php $ themeClass = ''; if (isset($ _GET['theme']) && $ _GET['theme'] == 'dark') {   $ themeClass = 'dark-theme'; } 
 $ themeToggle = ($ themeClass == 'dark-theme') ? 'light' : 'dark'; ?> <!DOCTYPE html> <html lang="en"> <!-- etc. --> <body class="<?php echo $ themeClass; ?>">   <a href="?theme=<?php echo $ themeToggle; ?>">Toggle Dark Mode</a>   <!-- etc. --> </body> </html>

We can have the user send a GET or POST request. Then, we let our code (PHP in this case) apply the appropriate body class when the page is reloaded. I am using a GET request (URL params) for the purpose of this demonstration.

And, yes, we can swap stylesheets just like we did in the second method.

<?php $ themeStyleSheet = 'light-theme.css'; if (isset($ _GET['theme']) && $ _GET['theme'] == 'dark') {   $ themeStyleSheet = 'dark-theme.css'; } 
 $ themeToggle = ($ themeStyleSheet == 'dark-theme.css') ? 'light' : 'dark'; ?> <!DOCTYPE html> <html lang="en"> <head>   <!-- etc. -->   <link href="<?php echo $ themeStyleSheet; ?>" rel="stylesheet"> </head> 
 <body>   <a href="?theme=<?php echo $ themeToggle; ?>">Toggle Dark Mode</a>   <!-- etc. --> </body> </html>

This method has an obvious downside: the page needs to be refreshed for the toggle to take place. But a server-side solution like this is useful in persisting the user’s theme choice across page reloads, as we will see later.


Which method should you choose?

The “right” method comes down to the requirements of your project. If you are doing a large project, for example, you might go with CSS properties to help wrangle a large codebase. On the other hand, if your project needs to support legacy browsers, then another approach will need to do instead.

Moreover, there’s nothing saying we can only use one method. Sometimes a combination of methods will be the most effective route. There may even be other possible methods than what we have discussed.


Dark Mode at the Operating System Level

So far, we’ve used a button to toggle between light and dark mode but we can simply let the user’s operating system do that lifting for us. For example, many operating systems let users choose between light and dark themes directly in the system settings.

The “General” settings in MacOS System Preferences

Pure CSS

Details

Fortunately, CSS has a prefers-color-scheme media query which can be used to detect user’s system color scheme preferences. It can have three possible values: no preference, light and dark. Read more about it on MDN.

@media (prefers-color-scheme: dark) {   /* Dark theme styles go here */ } 
 @media (prefers-color-scheme: light) {   /* Light theme styles go here */ }

To use it, we can put the dark theme styles inside the media query.

@media (prefers-color-scheme: dark) {   body {     color: #eee;     background: #121212;   } 
   a {     color: #809fff;   } }

Now, if a user has enabled dark mode from the system settings, they will get the dark mode styles by default. We don’t have to resort to JavaScript or server-side scripts to decide which mode to use. Heck, we don’t even need the button anymore!

JavaScript

Details

We can turn to JavaScript to detect the user’s preferred color scheme. This is a lot like the first method we worked with, only we’re using matchedMedia() to detect the user’s preference.

const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');nnif (prefersDarkScheme.matches) {n  document.body.classList.add('dark-theme');n} else {n  document.body.classList.remove('dark-theme');n}

There is a downside to using JavaScript: there will likely be a quick flash of the light theme as JavaScript is executed after the CSS. That could be misconstrued as a bug.

And, of course, we can swap stylesheets instead like we did in the second method. This time, we link up both stylesheets and use the media query to determine which one is applied.

Overriding OS Settings

We just looked at how to account for a user’s system-wide color scheme preferences. But what if users want to override their system preference for a site? Just because a user prefers dark mode for their OS doesn’t always mean they prefer it on a website. That’s why providing a way to manually override dark mode, despite the system settings, is a good idea.

View Code

Let’s use the CSS custom properties approach to demonstrate how to do this. The idea is to define the custom properties for both themes like we did before, wrap dark styles up in the prefers-color-scheme media query, then define a .light-theme class inside of that we can use to override the dark mode properties, should the user want to toggle between the two modes.

/* Default colors */ body {   --text-color: #222;   --bkg-color: #fff; } /* Dark theme colors */ body.dark-theme {   --text-color: #eee;   --bkg-color: #121212; }  /* Styles for users who prefer dark mode at the OS level */ @media (prefers-color-scheme: dark) {   /* defaults to dark theme */   body {      --text-color: #eee;     --bkg-color: #121212;   }   /* Override dark mode with light mode styles if the user decides to swap */   body.light-theme {     --text-color: #222;     --bkg-color: #fff;   } }

Now we can turn back to our trusty button to toggle between light and dark themes. This way, we’re respecting the OS color preference by default and allowing the user to manually switch themes.

// Listen for a click on the button  btn.addEventListener("click", function() {   // If the OS is set to dark mode...   if (prefersDarkScheme.matches) {     // ...then apply the .light-theme class to override those styles     document.body.classList.toggle("light-theme");     // Otherwise...   } else {     // ...apply the .dark-theme class to override the default light styles     document.body.classList.toggle("dark-theme");   } });

Browser Support

The prefers-color-scheme media query feature enjoys support by major browsers, including Chrome 76+, Firefox 67+, Chrome Android 76+ and Safari 12.5+ (13+ on iOS). It doesn’t support IE and Samsung Internet Browser.

That’s a promising amount of support!. Can I Use estimates 80.85% of user coverage.

Operating systems that currently support dark mode include MacOS (Mojave or later), iOS (13.0+), Windows (10+), and Android (10+).


Storing a User’s Preference

What we’ve looked at so far definitely does what it says in the tin: swap themes based on an OS preference or a button click. This is great, but doesn’t carry over when the user either visits another page on the site or reloads the current page.

We need to save the user’s choice so that it will be applied consistently throughout the site and on subsequent visits. To do that, we can save the user’s choice to the localStorage when the theme is toggled. Cookies are also well-suited for the job.

Let’s look at both approaches.

Using localStorage

We have a script that saves the selected theme to localStorage when the toggle takes place. In other words, when the page is reloaded, the script fetches the choice from localStorage and applies it. JavaScript is often executed after CSS, so this approach is prone to a “flash of incorrect theme” (FOIT).

View Code
// Select the button const btn = document.querySelector(".btn-toggle"); // Select the theme preference from localStorage const currentTheme = localStorage.getItem("theme"); 
 // If the current theme in localStorage is "dark"... if (currentTheme == "dark") {   // ...then use the .dark-theme class   document.body.classList.add("dark-theme"); } 
 // Listen for a click on the button  btn.addEventListener("click", function() {   // Toggle the .dark-theme class on each click   document.body.classList.toggle("dark-theme");      // Let's say the theme is equal to light   let theme = "light";   // If the body contains the .dark-theme class...   if (document.body.classList.contains("dark-theme")) {     // ...then let's make the theme dark     theme = "dark";   }   // Then save the choice in localStorage   localStorage.setItem("theme", theme); });

Using Cookies with PHP

To avoid FLIC, we can use a server-side script like PHP. Instead of saving the user’s theme preference in localStorage, we will create a cookie from JavaScript and save it there. But again, this may only be feasible if you’re already working with a server-side language.

View Code
// Select the button const btn = document.querySelector(".btn-toggle"); 
 // Listen for a click on the button  btn.addEventListener("click", function() {   // Toggle the .dark-theme class on the body   document.body.classList.toggle("dark-theme");      // Let's say the theme is equal to light   let theme = "light";   // If the body contains the .dark-theme class...   if (document.body.classList.contains("dark-theme")) {     // ...then let's make the theme dark     theme = "dark";   }   // Then save the choice in a cookie   document.cookie = "theme=" + theme; });

We can now check for the existence of that cookie and load the appropriate theme by applying the proper class to the <body> tag.

<?php $ themeClass = ''; if (!empty($ _COOKIE['theme']) && $ _COOKIE['theme'] == 'dark') {   $ themeClass = 'dark-theme'; } ?> 
 <!DOCTYPE html> <html lang="en"> <!-- etc. --> <body class="<?php echo $ themeClass; ?>"> <!-- etc. --> </body> </html>

Here is how to do that using the separate stylesheets method:

<?php $ themeStyleSheet = 'light-theme.css'; if (!empty($ _COOKIE['theme']) && $ _COOKIE['theme'] == 'dark') {   $ themeStyleSheet = 'dark-theme.css'; } ?> 
 <!DOCTYPE html> <html lang="en"> <head>   <!-- etc. -->   <link href="<?php echo $ themeStyleSheet; ?>" rel="stylesheet" id="theme-link"> </head> <!-- etc. -->

If your website has user accounts — like a place to log in and manage profile stuff — that’s also a great place to save theme preferences. Send those to the database where user account details are stored. Then, when the user logs in, fetch the theme from the database and apply it to the page using PHP (or whatever server-side script).

There are various ways to do this. In this example, I am fetching the user’s theme preference from the database and saving it in a session variable at the time of login.

<?php // Login action if (!empty($ _POST['login'])) {   // etc. 
   // If the uuser is authenticated...   if ($ loginSuccess) {     // ... save their theme preference to a session variable     $ _SESSION['user_theme'] = $ userData['theme'];   } } 
 // Pick the session variable first if it's set; otherwise pick the cookie $ themeChoice = $ _SESSION['user_theme'] ?? $ _COOKIE['theme'] ?? null; $ themeClass = ''; if ($ themeChoice == 'dark') {   $ themeClass = 'dark-theme'; } ?> 
 <!DOCTYPE html> <html lang="en"> <!-- etc. --> <body class="<?php echo $ themeClass; ?>"> <!-- etc. --> </body> </html>

I am using PHP’s null coalesce operator (??) to decide where to pick the theme preference: from the session or from the cookie. If the user is logged in, the value of the session variable is taken instead that of the cookie. And if the user is not logged in or has logged out, the value of cookie is taken.


Handling User Agent Styles

To inform the browser UA stylesheet about the system color scheme preferences and tell it which color schemes are supported in the page, we can use the color-scheme meta tag.

For example, let’s say the page should support both “dark” and “light” themes. We can put both of them as values in the meta tag, separated by spaces. If we only want to support a “light” theme, then we only need to use “light” as the value. This is discussed in a CSSWG GitHub issue, where it was originally proposed.

<meta name="color-scheme" content="dark light">

When this meta tag is added, the browser takes the user’s color scheme preferences into consideration when rendering UA-controlled elements of the page (like a <button>). It renders colors for the root background, form controls, and spell-check features (as well as any other UA-controlled styles) based on the user’s preference.

Source

Although themes are manually styled for the most part (which overrides the UA styles), informing the browser about the supported themes helps to avoid even the slightest chance of a potential FOIT situation. This is true for those occasions where HTML has rendered but CSS is still waiting to load.

We can also set this in CSS:

:root {   color-scheme: light dark; /* both supported */ }
via Jim Nielsen

At the time of writing, the color-scheme property lacks broad browser support, though Safari and Chrome both support it.


Combining all the things!

Let’s combine everything and create a working demo that:

  1. Automatically loads a dark or light theme based on system preferences
  2. Allows the user to manually override their system preference
  3. Maintains the user’s preferred theme on page reloads

Using JavaScript & Local Storage

// Select the button const btn = document.querySelector(".btn-toggle"); // Check for dark mode preference at the OS level const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)"); 
 // Get the user's theme preference from local storage, if it's available const currentTheme = localStorage.getItem("theme"); // If the user's preference in localStorage is dark... if (currentTheme == "dark") {   // ...let's toggle the .dark-theme class on the body   document.body.classList.toggle("dark-mode"); // Otherwise, if the user's preference in localStorage is light... } else if (currentTheme == "light") {   // ...let's toggle the .light-theme class on the body   document.body.classList.toggle("light-mode"); } 
 // Listen for a click on the button  btn.addEventListener("click", function() {   // If the user's OS setting is dark and matches our .dark-mode class...   if (prefersDarkScheme.matches) {     // ...then toggle the light mode class     document.body.classList.toggle("light-mode");     // ...but use .dark-mode if the .light-mode class is already on the body,     var theme = document.body.classList.contains("light-mode") ? "light" : "dark";   } else {     // Otherwise, let's do the same thing, but for .dark-mode     document.body.classList.toggle("dark-mode");     var theme = document.body.classList.contains("dark-mode") ? "dark" : "light";   }   // Finally, let's save the current preference to localStorage to keep using it   localStorage.setItem("theme", theme); });

Using PHP & Cookies

<?php $ themeClass = ''; if (!empty($ _COOKIE['theme'])) {   if ($ _COOKIE['theme'] == 'dark') {     $ themeClass = 'dark-theme';   } else if ($ _COOKIE['theme'] == 'light') {     $ themeClass = 'light-theme';   }   } ?> 
 <!DOCTYPE html> <html lang="en"> <!-- etc. --> <body class="<?php echo $ themeClass; ?>"> <!-- etc. --> <script>   const btn = document.querySelector(".btn-toggle");   const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");      btn.addEventListener("click", function() {     if (prefersDarkScheme.matches) {       document.body.classList.toggle("light-mode");       var theme = document.body.classList.contains("light-mode") ? "light" : "dark";     } else {       document.body.classList.toggle("dark-mode");       var theme = document.body.classList.contains("dark-mode") ? "dark" : "light";     }     document.cookie = "theme=" + theme;   }); </script> </body> </html>


Design Considerations

I often hear that implementing dark mode is easier than designing one. While I’ll refrain from judgement, let’s look at some considerations for designing a dark theme.

You already know the basic task: swap lighter color values for darker ones and vice versa. But there are some UI elements and enhancements that are more nuanced and require more attention. Let’s take a look at those.

Dark Mode Images

A good rule is to decrease the brightness and contrast of images a bit so that it looks comfortable to the eyes when it’s against a dark background. A super bright image on a super dark background can be jarring and dimming the image reduces some of that heavy contrast.

The CSS filter() function is more than capable of handling this for us:

/* Apply the filter directly on the body tag */ body.dark-theme img {   filter: brightness(.8) contrast(1.2); } 
 /* Or apply it via media query */ @media (prefers-color-scheme: dark) {   img {     filter: brightness(.8) contrast(1.2);   } }

We can do the same sort of thing directly in the markup using the <picture> element to load different versions of an image:

<picture>   <!-- Use this image if the user's OS setting is light or unset -->   <source srcset="photo-light.png" media="(prefers-color-scheme: light) or (prefers-color-scheme: no-preference)">   <!-- Use this image if the user's OS setting is dark -->   <source srcset="photo-dark.png" media="(prefers-color-scheme: dark)"> </picture>

The downside here is that it requires supplying two files where we only have to deal with one when using CSS. This also doesn’t fully account for the user toggling the color theme on the site.

Dark Mode Shadows

Dark mode shadows are tricky. If we simply invert a dark shadow using light colors, then we get this funky thing with a light shadow on a dark background… and it’s not a good look.

It’s possible to use a dark shadow in dark mode, but the background color has to be “light” enough (like a dark gray) to provide enough contrast to actually see the shadow against it.

Use opacity to convey depth, with high opacity regions having a lower depth. That’s to say, elements that have a higher elevation should have a lower opacity than elements that are “closer” in depth to the background.

Different shades of color create different perceptions of “depth”

Dark Mode Typography

The trick here is a lot like images: we’ve gotta balance the contrast. Use too heavy of a font and we get blaring text that’s makes us want to move away from the screen. Use too light of a font and we’ll strain our eyes while inching toward the screen to get a closer look.

The balance is somewhere in the middle. Robin has a nice write-up where he suggests a small bit of CSS that makes a big difference in legibility.

Dark Mode Icons

Icons fall into this “tricky” category because they’re sort of a cross between text and images. If we’re working with SVG icons, though, we can change the fill with CSS. On the other hand, if we’re using font icons, we can simply change the color property instead.

/* SVG icon */ body.dark-theme svg.icon path {   fill: #efefef; } /* Font icon (using Font Awesome as an example) */ body.dark-theme .fa {   color: #efefef; }

A lot of the same design considerations that are true for text, are also generally applicable to icons. For example, we ought to avoid using full white and heavy outlines.

Dark Mode Colors

Pure white text on a pure black background will look jarring. The trick here is to use an off-white for the text and off-black for the background. Material Design Guidelines for example recommends #121212 for the background.

Dark Mode Color Palettes

We’ve seen the difference using off-white and off-black colors makes for text and images. Let’s expand on that a bit with tips on how to develop a full color palette.

Most things boil down to one thing: contrast. That’s why the first tip before settling on any color is to run ideas through a contrast checker to ensure color ratios conform to WCAG’s guidelines for at least a AA rating, which is a contrast ratio of 4.5:1.

That means desaturated colors are our friends when working with a dark mode design. They help prevent overbearingly bright images and still give us plenty of room to create an effective contrast ratio.

Next, remember that accent colors are meant to be enhancements. They’re likely brighter than the dark theme background color, so using them like a primary color or the background color of a large container is just as jarring and hard on the eyes as a bright image or heavy white text.

If contrast is the balance we’re trying to strike, then remember that dark mode is more than blacks and grays. What about dark blue background with pale yellow text? Or dark brown with tan? There’s an entire (and growing) spectrum of color out there and we can leverage any part of it to fuel creativity.

A few examples of colors that are dark without resorting to full-on black:

#232B32

#152028

#202945

Material Design’s guidelines on dark mode is a handy resource on best practices for dark mode design. It’s definitely worth a read for more tips to keep in mind.

Dark Mode in the Wild

YouTube uses the CSS variables technique. They’ve defined all their colors in variables under the html selector while dark mode colors are defined under html:not(.style-scope)[dark]. When dark mode is enabled, YouTube adds a dark="true" attribute to the <html> tag. This is what they use to override the variables defined in the HTML.

YouTube adds dark=true attribute to the <html> when it switches to the dark mode.

In the wild, the CSS custom properties approach seems to be most popular. It’s being used by Dropbox Paper, Slack, and Facebook.

Simplenote uses the class-swapping method where all light style rules are descendants of a .theme-light parent class and all the dark styles fall under a .theme-dark class. When the theme is toggled, the appropriate class is applied to the <body> tag.

Simplenote uses two classes: .light-theme and .dark-theme to style the themes.

Twitter goes the extra mile and offers several themes to choose from: “Default,” “Dim,” and “Lights out.” The “Dim” option employs dark blue for a background color. Compare that to “Lights out” which uses a stark black.

Twitter offers three themes to choose from.

Dark mode or no dark mode? That is the question.

There are perfectly valid reasons on both sides. Some of those reasons even go beyond the scope of user experience and include things like timing, budget and resources.

While being considerate of why you might not want to implement a dark mode, here are reasons why you might want to have one:

  • It’s cool and trendy (although that’s not a reason alone to do it)
  • It enhances accessibility by supporting users who are sensitive to eye strain in starkly bright themes.
  • It allows users to decide the most comfortable way to consume content while providing us a way to maintain control over the look and feel of things. Remember, we want to beat the Reader Mode button!
  • It helps to preserve battery life for devices with OLED screen where brighter colors consume more energy.
  • It’s extremely popular and appears to be going nowhere. It’s possible that your users who prefer a dark mode (like me!) will expect your site to have one. Might as well be ready for it.

The post A Complete Guide to Dark Mode on the Web appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Adding a Custom Welcome Guide to the WordPress Block Editor

I am creating a WordPress plugin and there is a slight learning curve when it comes to using it. I’d like to give users a primer on how to use the plugin, but I want to avoid diverting users to documentation on the plugin’s website since that takes them out of the experience.

What would be great is for users to immediately start using the plugin once it’s installed but have access to helpful tips while they are actively using it. There’s no native feature for something like this in WordPress but we can make something because WordPress is super flexible like that.

So here’s the idea. We’re going to bake documentation directly into the plugin and make it easily accessible in the block editor. This way, users get to use the plugin right away while having answers to common questions directly where they’re working. 

My plugin operates through several Custom Post Types (CPT). What we’re going to build is essentially a popup modal that users get when they go to these CPTs. 

The WordPress block editor is built in React, which utilizes components that can be customized to and reused for different situations.  That is the case with what we’re making — let’s call it the <Guide> component — which behaves like a modal, but is composed of several pages that the user can paginate through.

WordPress itself has a <Guide> component that displays a welcome guide when opening the block editor for the first time:

Screenshot showing a modal on top of the WordPress block editor welcoming users to the editor for the first time.
WordPress displays a modal with instructions for using the block editor when a user loads the editor for the first time.

The guide is a container filled with content that’s broken up into individual pages. In other words, it’s pretty much what we want. That means we don’t have to re-invent the wheel with this project; we can reuse this same concept for our own plugin.

Let’s do exactly that. 

What we want to achieve

Before we get to the solution, let’s talk about the end goal.

The design satisfies the requirements of the plugin, which is a GraphQL server for WordPress. The plugin offers a variety of CPTs that are edited through custom blocks which, in turn, are defined through templates. There’s a grand total of two blocks: one called “GraphiQL client” to input the GraphQL query, and one called “Persisted query options” to customize the behavior of the execution.

Since creating a query for GraphQL is not a trivial task, I decided to add the guide component to the editor screen for that CPT. It’s available in the Document settings as a panel called “Welcome Guide.”

Screenshot showing the WordPress editor with the document settings panel open in the right column. a welcome guide tab is highlighted in the settings.

Crack that panel open and the user gets a link. That link is what will trigger the modal.

Close-up screenshot of the welcome guide tab opened, revealing a link that says "Open Guide: Creating Persisted Queries."

For the modal itself, I decided to display a tutorial video on using the CPT on the first page, and then describe in detail all the options available in the CPT on subsequent pages.

Screenshot showing the custom modal open in the block editor and containing an embedded video on how to use the plugin.

I believe this layout is an effective way to show documentation to the user. It is out of the way, but still conveniently close to the action. Sure, we can use a different design or even place the modal trigger somewhere else using a different component instead of repurposing <Guide>, but this is perfectly good.

Planning the implementation

The implementation comprises the following steps:

  1. Scaffolding a new script to register the custom sidebar panel
  2. Displaying the custom sidebar panel on the editor for our Custom Post Type only
  3. Creating the guide
  4. Adding content to the guide

Let’s start!

Step 1: Scaffolding the script

Starting in WordPress 5.4, we can use a component called <PluginDocumentSettingPanel> to add a panel on the editor’s Document settings like this:

const { registerPlugin } = wp.plugins; const { PluginDocumentSettingPanel } = wp.editPost;   const PluginDocumentSettingPanelDemo = () => (   <PluginDocumentSettingPanel     name="custom-panel"     title="Custom Panel"     className="custom-panel"   >     Custom Panel Contents   </PluginDocumentSettingPanel> ); registerPlugin( 'plugin-document-setting-panel-demo', {   render: PluginDocumentSettingPanelDemo,   icon: 'palmtree', } );

If you’re experienced with the block editor and already know how to execute this code, then you can skip ahead. I’ve been coding with the block editor for less than three months, and using React/npm/webpack is a new world for me — this plugin is my first project using them! I’ve found that the docs in the Gutenberg repo are not always adequate for beginners like me, and sometimes the documentation is missing altogether, so I’ve had to dig into the source code to find answers.

When the documentation for the component indicates to use that piece of code above, I don’t know what to do next, because <PluginDocumentSettingPanel> is not a block and I am unable to scaffold a new block or add the code there. Plus, we’re working with JSX, which means we need to have a JavaScript build step to compile the code.

I did, however, find the equivalent ES5 code:

var el = wp.element.createElement; var __ = wp.i18n.__; var registerPlugin = wp.plugins.registerPlugin; var PluginDocumentSettingPanel = wp.editPost.PluginDocumentSettingPanel; 
 function MyDocumentSettingPlugin() {   return el(     PluginDocumentSettingPanel,     {       className: 'my-document-setting-plugin',       title: 'My Panel',     },     __( 'My Document Setting Panel' )   ); } 
 registerPlugin( 'my-document-setting-plugin', {   render: MyDocumentSettingPlugin } );

ES5 code does not need be compiled, so we can load it like any other script in WordPress. But I don’t want to use that. I want the full, modern experience of ESNext and JSX.

So my thinking goes like this: I can’t use the block scaffolding tools since it’s not a block, and I don’t know how to compile the script (I’m certainly not going to set-up webpack all by myself). That means I’m stuck.

But wait! The only difference between a block and a regular script is just how they are registered in WordPress. A block is registered like this:

wp_register_script($ blockScriptName, $ blockScriptURL, $ dependencies, $ version); register_block_type('my-namespace/my-block', [   'editor_script' => $ blockScriptName, ]);

And a regular script is registered like this:

wp_register_script($ scriptName, $ scriptURL, $ dependencies, $ version); wp_enqueue_script($ scriptName);

We can use any of the block scaffolding tools to modify things then register a regular script instead of a block, which gains us access to the webpack configuration to compile the ESNext code. Those available tools are:

I chose to use the @wordpress/create-block package because it is maintained by the team developing Gutenberg.

To scaffold the block, we execute this in the command line:

npm init @wordpress/block

After completing all the prompts for information — including the block’s name, title and description — the tool will generate a single-block plugin, with an entry PHP file containing code similar to this:

/**  * Registers all block assets so that they can be enqueued through the block editor  * in the corresponding context.  *  * @see https://developer.wordpress.org/block-editor/tutorials/block-tutorial/applying-styles-with-stylesheets/  */ function my_namespace_my_block_block_init() {   $ dir = dirname( __FILE__ ); 
   $ script_asset_path = "$ dir/build/index.asset.php";   if ( ! file_exists( $ script_asset_path ) ) {     throw new Error(       'You need to run `npm start` or `npm run build` for the "my-namespace/my-block" block first.'     );   }   $ index_js     = 'build/index.js';   $ script_asset = require( $ script_asset_path );   wp_register_script(     'my-namespace-my-block-block-editor',     plugins_url( $ index_js, __FILE__ ),     $ script_asset['dependencies'],     $ script_asset['version']   ); 
   $ editor_css = 'editor.css';   wp_register_style(     'my-namespace-my-block-block-editor',     plugins_url( $ editor_css, __FILE__ ),     array(),     filemtime( "$ dir/$ editor_css" )   ); 
   $ style_css = 'style.css';   wp_register_style(     'my-namespace-my-block-block',     plugins_url( $ style_css, __FILE__ ),     array(),     filemtime( "$ dir/$ style_css" )   ); 
   register_block_type( 'my-namespace/my-block', array(     'editor_script' => 'my-namespace-my-block-block-editor',     'editor_style'  => 'my-namespace-my-block-block-editor',     'style'         => 'my-namespace-my-block-block',   ) ); } add_action( 'init', 'my_namespace_my_block_block_init' );

We can copy this code into the plugin, and modify it appropriately, converting the block into a regular script. (Note that I’m also removing the CSS files along the way, but could keep them, if needed.)

function my_script_init() {   $ dir = dirname( __FILE__ ); 
   $ script_asset_path = "$ dir/build/index.asset.php";   if ( ! file_exists( $ script_asset_path ) ) {     throw new Error(       'You need to run `npm start` or `npm run build` for the "my-script" script first.'     );   }   $ index_js     = 'build/index.js';   $ script_asset = require( $ script_asset_path );   wp_register_script(     'my-script',     plugins_url( $ index_js, __FILE__ ),     $ script_asset['dependencies'],     $ script_asset['version']   );   wp_enqueue_script(     'my-script'   ); } add_action( 'init', 'my_script_init' );

Let’s copy the package.json file over:

{   "name": "my-block",   "version": "0.1.0",   "description": "This is my block",   "author": "The WordPress Contributors",   "license": "GPL-2.0-or-later",   "main": "build/index.js",   "scripts": {     "build": "wp-scripts build",     "format:js": "wp-scripts format-js",     "lint:css": "wp-scripts lint-style",     "lint:js": "wp-scripts lint-js",     "start": "wp-scripts start",     "packages-update": "wp-scripts packages-update"   },   "devDependencies": {     "@wordpress/scripts": "^9.1.0"   } }

Now, we can replace the contents of file src/index.js with the ESNext code from above to register the <PluginDocumentSettingPanel> component. Upon running npm start (or npm run build for production) the code will be compiled into build/index.js.

There is a last problem to solve: the <PluginDocumentSettingPanel> component is not statically imported, but instead obtained from wp.editPost, and since wp is a global variable loaded by WordPress on runtime, this dependency is not present in index.asset.php (which is auto-generated during build). We must manually add a dependency to the wp-edit-post script when registering the script to make sure it loads before ours:

$ dependencies = array_merge(   $ script_asset['dependencies'],   [     'wp-edit-post',   ] ); wp_register_script(   'my-script',   plugins_url( $ index_js, __FILE__ ),   $ dependencies,   $ script_asset['version'] );

Now the script setup is ready!

The plugin can be updated with Gutenberg’s relentless development cycles. Run npm run packages-update to update the npm dependencies (and, consequently, the webpack configuration, which is defined on package "@wordpress/scripts") to their latest supported versions.

At this point, you might be wondering how I knew to add a dependency to the "wp-edit-post" script before our script. Well, I had to dig into Gutenberg’s source code. The documentation for <PluginDocumentSettingPanel> is somewhat incomplete, which is a perfect example of how Gutenberg’s documentation is lacking in certain places.

While digging in code and browsing documentation, I discovered a few enlightening things. For example, there are two ways to code our scripts: using either the ES5 or the ESNext syntax. ES5 doesn’t require a build process, and it references instances of code from the runtime environment, most likely through the global wp variable. For instance, the code to create an icon goes like this:

var moreIcon = wp.element.createElement( 'svg' );

ESNext relies on webpack to resolve all dependencies, which enables us to import static components. For instance, the code to create an icon would be:

import { more } from '@wordpress/icons';

This applies pretty much everywhere. However, that’s not the case for the <PluginDocumentSettingPanel> component, which references the runtime environment for ESNext:

const { PluginDocumentSettingPanel } = wp.editPost;

That’s why we have to add a dependency to the “wp-edit-post” script. That’s where the wp.editPost variable is defined.

If <PluginDocumentSettingPanel> could be directly imported, then the dependency to “wp-edit-post” would be automatically handled by the block editor through the Dependency Extraction Webpack Plugin. This plugin builds the bridge from static to runtime by creating a index.asset.php file containing all the dependencies for the runtime environment scripts, which are obtained by replacing "@wordpress/" from the package name with "wp-". Hence, the "@wordpress/edit-post" package  becomes the "wp-edit-post" runtime script. That’s how I figured out which script to add the dependency.

Step 2: Blacklisting the custom sidebar panel on all other CPTs 

The panel will display documentation for a specific CPT, so it must be registered only to that CPT. That means we need to blacklist it from appearing on any other post types.

Ryan Welcher (who created the <PluginDocumentSettingPanel> component) describes this process when registering the panel:

const { registerPlugin } = wp.plugins; const { PluginDocumentSettingPanel } = wp.editPost const { withSelect } = wp.data; 
 const MyCustomSideBarPanel = ( { postType } ) => { 
   if ( 'post-type-name' !== postType ) {     return null;   } 
   return(     <PluginDocumentSettingPanel       name="my-custom-panel"       title="My Custom Panel"     >       Hello, World!     </PluginDocumentSettingPanel>   ); } 
 const CustomSideBarPanelwithSelect = withSelect( select => {   return {     postType: select( 'core/editor' ).getCurrentPostType(),   }; } )( MyCustomSideBarPanel); 
 
 registerPlugin( 'my-custom-panel', { render: CustomSideBarPanelwithSelect } );

He also suggests an alternative solution, using useSelect instead of withSelect.

That said, I’m not totally convinced by this solution, because the JavaScript file must still be loaded, even if it isn’t needed, forcing the website to take a performance hit. Doesn’t it make more sense to not register the JavaScript file than it does to run JavaScript just to disable JavaScript?

I have created a PHP solution. I’ll admit that it feels a bit hacky, but it works well. First, we find out which post type is related to the object being created or edited:

function get_editing_post_type(): ?string {   if (!is_admin()) {     return null;   } 
   global $ pagenow;   $ typenow = '';   if ( 'post-new.php' === $ pagenow ) {     if ( isset( $ _REQUEST['post_type'] ) && post_type_exists( $ _REQUEST['post_type'] ) ) {       $ typenow = $ _REQUEST['post_type'];     };   } elseif ( 'post.php' === $ pagenow ) {     if ( isset( $ _GET['post'] ) && isset( $ _POST['post_ID'] ) && (int) $ _GET['post'] !== (int) $ _POST['post_ID'] ) {       // Do nothing     } elseif ( isset( $ _GET['post'] ) ) {       $ post_id = (int) $ _GET['post'];     } elseif ( isset( $ _POST['post_ID'] ) ) {       $ post_id = (int) $ _POST['post_ID'];     }     if ( $ post_id ) {       $ post = get_post( $ post_id );       $ typenow = $ post->post_type;     }   }   return $ typenow; }

Then, ,we register the script only if it matches our CPT:

add_action('init', 'maybe_register_script'); function maybe_register_script() {   // Check if this is the intended custom post type   if (get_editing_post_type() != 'my-custom-post-type') {     return;   } 
   // Only then register the block   wp_register_script(...);   wp_enqueue_script(...); }

Check out this post for a deeper dive on how this works.

Step 3: Creating the custom guide

I designed the functionality for my plugin’s guide based on the WordPress <Guide> component. I didn’t realize I’d be doing that at first, so here’s how I was able to figure that out.

  1. Search the source code to see how it was done there.
  2. Explore the catalogue of all available components in Gutenberg’s Storybook.

First, I copied content from the block editor modal and did a basic search. The results pointed me to this file. From there I discovered the component is called <Guide> and could simply copy and paste its code to my plugin as a base for my own guide.

Then I looked for the component’s documentation. I browsed the @wordpress/components package (which, as you may have guessed, is where components are implemented) and found the component’s README file. That gave me all the information I needed to implement my own custom guide component.

I also explored the catalogue of all the available components in Gutenberg’s Storybook (which actually shows that these components can be used outside the context of WordPress). Clicking on all of them, I finally discovered <Guide>. The storybook provides the source code for several examples (or stories). It’s a handy resource for understanding how to customize a component through props.

At this point, I knew <Guide> would make a solid base for my component. There is one missing element, though: how to trigger the guide on click. I had to rack my brain for this one!

This is a button with a listener that opens the modal on click:

import { useState } from '@wordpress/element'; import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import MyGuide from './guide'; 
 const MyGuideWithButton = ( props ) => {   const [ isOpen, setOpen ] = useState( false );   return (     <>       <Button onClick={ () => setOpen( true ) }>         { __('Open Guide: “Creating Persisted Queries”') }       </Button>       { isOpen && (         <MyGuide            { ...props }           onFinish={ () => setOpen( false ) }         />       ) }     </>   ); }; export default MyGuideWithButton;

Even though the block editor tries to hide it, we are operating within React. Until now, we’ve been dealing with JSX and components. But now we need the useState hook, which is specific to React.

I’d say that having a good grasp of React is required if you want to master the WordPress block editor. There is no way around it.

Step 4: Adding content to the guide

We’re almost there! Let’s create the <Guide> component, containing a <GuidePage> component for each page of content.

The content can use HTML, include other components, and whatnot. In this particular case, I have added three <GuidePage> instances for my CPT just using HTML. The first page includes a video tutorial and the next two pages contain detailed instructions.

import { Guide, GuidePage } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; 
 const MyGuide = ( props ) => {   return (     <Guide { ...props } >       <GuidePage>         <video width="640" height="400" controls>           <source src="https://d1c2lqfn9an7pb.cloudfront.net/presentations/graphql-api/videos/graphql-api-creating-persisted-query.mov" type="video/mp4" />           { __('Your browser does not support the video tag.') }         </video>         // etc.       </GuidePage>       <GuidePage>         // ...       </GuidePage>       <GuidePage>         // ...       </GuidePage>     </Guide>   ) } export default MyGuide;
imaged gif showing the mouse cursor clicking on the Open Guide link in the block editor's document settings, which opens the custom welcome guide containing a video with links to other pages in the modal.
Hey look, we have our own guide now!

Not bad! There are a few issues, though:

  • I couldn’t embed the video inside the <Guide> because clicking the play button closes the guide. I assume that’s because the <iframe> falls outside the boundaries of the guide. I wound up uploading the video file to S3 and serving with <video>.
  • The page transition in the guide is not very smooth. The block editor’s modal looks alright because all pages have a similar height, but the transition in this one is pretty abrupt.
  • The hover effect on buttons could be improved. Hopefully, the Gutenberg team needs to fix this for their own purposes, because my CSS aren’t there. It’s not that my skills are bad; they are nonexistent.

But I can live with these issues. Functionality-wise, I’ve achieved what I need the guide to do.

Bonus: Opening docs independently 

For our <Guide>, we created the content of each <GuidePage> component directly using HTML. However, if this HTML code is instead added through an autonomous component, then it can be reused for other user interactions.

For instance, the component <CacheControlDescription> displays a description concerning HTTP caching:

const CacheControlDescription = () => {   return (     <p>The Cache-Control header will contain the minimum max-age value from all fields/directives involved in the request, or "no-store" if the max-age is 0</p>   ) } export default CacheControlDescription;

This component can be added inside a <GuidePage> as we did before, but also within a <Modal> component:

import { useState } from '@wordpress/element'; import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import CacheControlDescription from './cache-control-desc'; 
 const CacheControlModalWithButton = ( props ) => {   const [ isOpen, setOpen ] = useState( false );   return (     <>       <Button          icon="editor-help"         onClick={ () => setOpen( true ) }       />       { isOpen && (         <Modal            { ...props }           onRequestClose={ () => setOpen( false ) }         >           <CacheControlDescription />         </Modal>       ) }     </>   ); }; export default CacheControlModalWithButton;

To provide a good user experience, we can offer to show the documentation only when the user is interacting with the block. For that, we show or hide the button depending on the value of isSelected:

import { __ } from '@wordpress/i18n'; import CacheControlModalWithButton from './modal-with-btn'; 
 const CacheControlHeader = ( props ) => {   const { isSelected } = props;   return (     <>       { __('Cache-Control max-age') }       { isSelected && (         <CacheControlModalWithButton />       ) }     </>   ); } export default CacheControlHeader;

Finally, the <CacheControlHeader> component is added to the appropriate control.

Animated gif showing the option to view a guide displaying when a block is selected in the editor.

Tadaaaaaaaa 🎉

The WordPress block editor is quite a piece of software! I was able to accomplish things with it that I would have been unable to without it. Providing documentation to the user may not be the shiniest of examples or use cases, but it’s a very practical one and something that’s relevant for many other plugins. Want to use it for your own plugin? Go for it!

The post Adding a Custom Welcome Guide to the WordPress Block Editor appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

A Guide to the Responsive Images Syntax in HTML

This guide is about the HTML syntax for responsive images (and a little bit of CSS for good measure). The responsive images syntax is about serving one image from multiple options based on rules and circumstances. There are two forms of responsive images, and they’re for two different things:

If your only goal is…

Increased Performance

Then what you need is…

<img   srcset=""   src=""   alt="" >

There is a lot of performance gain to be had by using responsive images. Image weight has a huge impact on pages’ overall performance, and responsive images are one of the best things that you can do to cut image weight. Imagine the browser being able to choose between a 300×300 image or a 600×600. If the browser only needs the 300×300, that’s potentially a 4× bytes-over-the-wire savings! Savings generally go up as the display resolution and viewport size go down; on the smallest screens, a couple of case studies have shown byte savings of 70–90%.

If you also need…

Design Control

Then what you need is…

<picture>   <source srcset="" media="">   <source srcset="" media="">   <img src="" alt=""> </picture>

Another perfectly legit goal with responsive images is not just to serve different sizes of the same image, but to serve different images. For example, cropping an image differently depending on the size of the screen and differences in the layout. This is referred to as “art direction.”

The <picture> element is also used for fallback image types and any other sort of media query switching (e.g. different images for dark mode). You get greater control of what browsers display.


There is a lot to talk about here, so let’s go through both syntaxes, all of the related attributes and values, and talk about a few related subjects along the way, like tooling and browsers.


Using srcset

The <img srcset="" src="" alt=""> syntax is for serving differently-sized versions of the same image. You could try to serve entirely different images using this syntax, but browsers assume that everything in a srcset is visually-identical and will choose whichever size they think is best, in impossible-for-you-to-predict ways. So I wouldn’t reccomend it.

Perhaps the easiest-possible responsive images syntax is adding a srcset attribute with x descriptors on the images to label them for use on displays with different pixel-densities.

<img    alt="A baby smiling with a yellow headband."   src="baby-lowres.jpg"   srcset="baby-highres.jpg 2x" >

Here, we’ve made the default (the src) the “low res” (1×) copy of the image. Defaulting to the smallest/fastest resources is usually the smart choice:. We also provide a 2× version. If the browser knows it is on a higher pixel-density display (the 2x part), it will use that image instead.

Demo
<img    alt="A baby smiling with a yellow headband."   src="baby-lowres.jpg"   srcset="     baby-high-1.jpg 1.5x,     baby-high-2.jpg 2x,     baby-high-3.jpg 3x,     baby-high-4.jpg 4x,     baby-high-5.jpg 100x   " >

You can do as many pixel-density variants as you like.

While this is cool and useful, x descriptors only account for a small percentage of responsive images usage. Why? They only let browsers adapt based on one thing: display pixel-density. A lot of times, though, our responsive images are on responsive layouts, and the image’s layout size is shrinking and stretching right along with the viewport. In those situations, the browser needs to make decisions based on two things: the pixel-density of the screen, and the layout size of the image. That’s where w descriptors and the sizes attribute come in, which we’ll look at in the next section.

Using srcset / w + sizes

This is the good stuff. This accounts for around 85% of responsive images usage on the web. We’re still serving the same image at multiple sizes, only we’re giving the browser more information so that it can adapt based on both pixel-density and layout size.

<img    alt="A baby smiling with a yellow headband."   srcset="     baby-s.jpg  300w,     baby-m.jpg  600w,     baby-l.jpg  1200w,     baby-xl.jpg 2000w   "   sizes="70vmin" >

We’re still providing multiple copies of the same image and letting the browser pick the most appropriate one. But instead of labeling them with a pixel density (x) we’re labelling them with their resource width, using w descriptors. So if baby-s.jpg is 300×450, we label it as 300w.

Using srcset with width (w) descriptors like this means that it will need to be paired with the sizes attribute so that the browser will know how large of a space the image will be displaying in. Without this information, browsers can’t make smart choices.

Demo

Creating accurate sizes

Creating sizes attributes can get tricky. The sizes attribute describes the width that the image will display within the layout of your specific site, meaning it is closely tied to your CSS. The width that images render at is layout-dependent rather than just viewport dependent!

Let’s take a look at a fairly simple layout with three breakpoints. Here’s a video demonstrating this:

Demo

The breakpoints are expressed with media queries in CSS:

body {   margin: 2rem;   font: 500 125% system-ui, sans-serif; } .page-wrap {   display: grid;   gap: 1rem;   grid-template-columns: 1fr 200px;   grid-template-areas:     "header header"     "main aside"     "footer footer"; }  @media (max-width: 700px) {   .page-wrap {     grid-template-columns: 100%;     grid-template-areas:       "header"       "main"       "aside"       "footer";   } } @media (max-width: 500px) {   body {     margin: 0;   } }

The image is sized differently at each breakpoint. Here’s a breakdown of all of the bits and pieces that affect the image’s layout width at the largest breakpoint (when the viewport is wider than 700px):

The image is as wide as 100vw minus all that explicitly sized margin, padding, column widths, and gap.
  • At the largest size: there is 9rem of explicit spacing, so the image is calc(100vw - 9rem - 200px) wide. If that column used a fr unit instead of 200px, we’d kinda be screwed here.
  • At the medium size: the sidebar is dropped below, so there is less spacing to consider. Still, we can do calc(100vw - 6rem) to account for the margins and padding.
  • At the smallest size: the body margin is removed, so just calc(100vw - 2rem) will do the trick.

Phew! To be honest, I found that a little challenging to think out, and made a bunch of mistakes as I was creating this. In the end, I had this:

<img    ...     sizes="     (max-width: 500px) calc(100vw - 2rem),      (max-width: 700px) calc(100vw - 6rem),     calc(100vw - 9rem - 200px)   " />

A sizes attribute that gives the browser the width of the image across all three breakpoints, factoring in the layout grid, and all of the surrounding gap, margin, and padding that end up impacting the image’s width.

Now wait! Drumroll! 🥁🥁🥁That’s still wrong. I don’t understand why exactly, because to me that looks like it 100% describes what is happening in the CSS layout. But it’s wrong because Martin Auswöger’s RespImageLint says so. Running that tool over the isolated demo reports no problems except the fact that the sizes attribute is wrong for some viewport sizes, and should be:

<img   ...   sizes="     (min-width: 2420px) 2000px,      (min-width: 720px) calc(94.76vw - 274px),      (min-width: 520px) calc(100vw - 96px),      calc(100vw - 32px)   " >

I don’t know how that’s calculated and it’s entirely unmaintainable by hand, but, it’s accurate. Martin’s tool programmatically resizes the page a bunch and writes out a sizes attribute that describes the actual, observed width of the image over a wide range of viewport sizes. It’s computers, doing math, so it’s right. So, if you want a super-accurate sizes attribute, I’d recommend just putting a wrong one on at first, running this tool, and copying out the correct one.

For an even deeper dive into all this, check out Eric Portis’ w descriptors and sizes: Under the hood.

Being more chill about sizes

Another option is use the Horseshoes & Hand Grenades Method™ of sizes (or, in other words, close counts).

For example, sizes="96vw" says, “This image is going to be pretty big on the page — almost the full width — but there will always be a little padding around the edges, so not quite. Or sizes="(min-width: 1000px) 33vw, 96vw" says, “This image is in a three-column layout on large screens and close to full-width otherwise.” Practicality-wise, this can be a sane solution.

You might find that some automated responsive image solutions, which have no way of knowing your layout, make a guess — something like sizes="(max-width: 1000px) 100vw, 1000px". This is just saying, “Hey we don’t really know much about this layout, but we’re gonna take a stab and say, worst case, the image is full-width, and let’s hope it never renders larger than 1000px”.

Abstracting sizes

I’m sure you can imagine how easy it is to not only get sizes wrong, but also have it become wrong over time as layouts change on your site. It may be smart for you to abstract it using a templating language or content filter so that you can change the value across all of your images more easily.

I’m essentially talking about setting a sizes value in a variable once, and using that variable in a bunch of different <img> elements across your site. Native HTML doesn’t offer that, but any back end language does; for instance, PHP constants, Rails config variables, the React context API used for a global state variable, or variables within a templating language like Liquid can all be used to abstract sizes.

<?php   // Somewhere global   $ my_sizes = ""; ?>  <img   srcset=""   src=""   alt=""   sizes="<?php echo $ my_sizes; ?>" />

“Browser’s choice”

Now that we have a sizes attribute in place, the browser knows what size (or close to it) the image will render at and can work its magic. That is, it can do some math that factors in the pixel density of the screen, and the size that the image will render at, then pick the most appropriately-sized image.

The math is fairly straightforward at first. Say you’re about to show an image that is 40vw wide on a viewport that is 1200px wide, on a 2x pixel-density screen. The perfect image would be 960 pixels wide, so the browser is going to look for the closest thing it’s got. The browser will always calculate a target size that it would prefer based on the viewport and pixel-density situations, and what it knows from sizes, and compare that target to what it’s got to pick from in srcset. How browsers do the picking, though, can get a little weird.

A browser might factor more things into this equation if it chooses to. For example, it could consider the user’s current network speeds, or whether or not the user has flipped on some sort of “data saver” preference. I’m not sure if any browsers actually do this sort of thing, but they are free to if they wish as that’s how the spec was written. What some browsers sometimes choose to do is pull from cache. If the math shows they should be using a 300px image, but they already have a 600px in local cache, they will just use that. Smart. Room for this sort of thing is a strength of the srcset/sizes syntax. It’s also why you always use different sizes of the same image, within srcset: you’ve got no way to know which image is going to be selected. It’s the browser’s choice.

This is weird. Doesn’t the browser already know this stuff?

You might be thinking, “Uhm why do I have to tell the browser how big the image will render, doesn’t it know that?” Well, it does, but only after it’s downloaded your HTML and CSS and laid everything out. The sizes attribute is about speed. It gives the browser enough information to make a smart choice as soon as it sees your <img>.

<img   data-sizes="auto"   data-srcset="     responsive-image1.jpg 300w,     responsive-image2.jpg 600w,     responsive-image3.jpg 900w"   class="lazyload"  />

Now you might be thinking, “But what about lazy-loaded images?” (as in, by the time a lazy-loaded image is requested, layout’s already been done and the browser already knows the image’s render size). Well, good thinking! Alexander Farkas’ lazysizes library writes out sizes attributes automatically on lazyload, and there’s an ongoing discussion about how to do auto-sizes for lazy-loaded images, natively.

sizes can be bigger than the viewport

Quick note on sizes. Say you have an effect on your site so that an image “zooms in” when it’s clicked. Maybe it expands to fill the whole viewport, or maybe it zooms even more, so that you can see more detail. In the past, we might have had to swap out the src on click in order to switch to a higher-res version. But now, assuming a higher-res source is already in the srcset, you can just change the sizes attribute to something huge, like 200vw or 300vw, and the browser should download the super-high-res source automatically for you. Here’s an article by Scott Jehl on this technique.

↩️ Back to top


Using <picture>

Hopefully, we’ve beaten it into the ground that <img srcset="" sizes="" alt=""> is for serving differently-sized versions of the same image. The <picture> syntax can do that too, but the difference here is that the browser must respect the rules that you set. That’s useful when you want to change more than just the resolution of the loaded image to fit the user’s situation. This intentional changing of the image is usually called “art direction.”

Art Direction

<picture>   <source      srcset="baby-zoomed-out.jpg"     media="(min-width: 1000px)"   />   <source      srcset="baby.jpg"     media="(min-width: 600px)"   />   <img      src="baby-zoomed-in.jpg"      alt="Baby Sleeping"   /> </picture>

This code block is an example of what it might look like to have three stages of an “art directed” image.

  • On large screens, show a zoomed-out photo.
  • On medium screens, show that same photo, zoomed in a bit.
  • On small screens, zoom in even more.

The browser must respect our media queries and will swap images at our exact breakpoints. That way, we can be absolutely sure that nobody on a small screen will see a tiny, zoomed-out image, which might not have the same impact as one of the zoomed-in versions.

Here’s a demo, written in Pug to abstract out some of the repetitive nature of <picture>.

Art direction can do a lot more than just cropping

Although cropping and zooming like this is the most common form of art direction by far, you can do a lot more with it. For instance, you can:

Sky’s the limit, really.

Combining source and srcset

Because <source> also uses the srcset syntax, they can be combined. This means that you can still reap the performance benefits of srcset even while swapping out visually-different images with <source>. It gets pretty verbose though!

<picture>   <source      srcset="       baby-zoomed-out-2x.jpg 2x,       baby-zoomed-out.jpg     "     media="(min-width: 1000px)"   />   <source      srcset="       baby-2x.jpg 2x,       baby.jpg     "     media="(min-width: 600px)"   />   <img      srcset="       baby-zoomed-out-2x.jpg 2x     "     src="baby-zoomed-out.jpg"     alt="Baby Sleeping"   /> </picture>

The more variations you create and the more resized versions you create per variation, the more verbose this code has to get.

Fallbacks for modern image formats

The <picture> element is uniquely suited to being able to handle “fallbacks.” That is, images in cutting-edge formats that not all browsers might be able to handle, with alternative formats for browsers that can’t load the preferred, fancy one. For example, let’s say you want to use an image in the WebP format. It’s a pretty great image format, often being the most performant choice, and it’s supported everywhere that the <picture> element is, except Safari. You can handle that situation yourself, like:

<picture>   <source srcset="party.webp">   <img src="party.jpg" alt="A huge party with cakes."> </picture>

This succeeds in serving a WebP image to browsers that support it, and falls back to a JPEG image, which is definitely supported by all browsers.

Here’s an example of a photograph (of me) at the exact same size where the WebP version is about 10% (!!!) of the size of the JPEG.

How do you create a WebP image? Well, it’s more of a pain in the butt than you’d like it to be, that’s for sure. There are online converters, command line tools, and some modern design software, like Sketch, helps you export in that format. My preference is to use an image hosting CDN service that automatically sends images in the perfect format for the requesting browser, which makes all this unnecessary (because you can just use img/srcset).

WebP isn’t the only player like this. Safari doesn’t support WebP, but does support a format called JPG 2000 which has some advantages over JPEG. Internet Explorer 11 happens to support an image format called JPEG-XR which has different advantages. So to hit all three, that could look like:

<picture>   <source srcset="/images/cereal-box.webp" type="image/webp" />   <source srcset="/images/cereal-box.jp2" type="image/jp2" />   <img src="/images/cereal-box.jxr" type="image/vnd.ms-photo" /> </picture>

This syntax (borrowed form a blog post by Josh Comeau) supports all three of the “next-gen” image formats in one go. IE 11 doesn’t support the <picture> syntax, but it doesn’t matter because it will get the <img> fallback which is in the JPEG-XR format it understands.

Estelle Weyl also covered this idea in a 2016 blog post on image optimization.

↩️ Back to top


Where do you get the differently-sized images?

You can make them yourself. Heck, even the free Preview app on my Mac can resize an image and “Save As.”

The Mac Preview app resizing an image, which is something that literally any image editing application (including Photoshop, Affinity Designer, Acorn, etc.) can also do. Plus, they often help by exporting the variations all at once.

But that’s work. It’s more likely that the creation of variations of these images is automated somehow (see the section below) or you use a service that allows you to create variations just by manipulating the URL to the image. That’s a super common feature of any image hosting/image CDN service. To name a few:

Not only do these services offer on-the-fly image resizing, they also often offer additional stuff, like cropping, filtering, adding text, and all kinds of useful features, not to mention serving assets efficiently from a CDN and automatically in next-gen formats. That makes them a really strong choice for just about any website, I’d say.

Here’s Glen Maddern in a really great screencast talking about how useful Image CDNs can be:

Design software is booming more aware that we often need multiple copies of images. The exporting interface from Figma is pretty nice, where any given selection can be exported. It allows multiple exports at once (in different sizes and formats) and remembers what you dod the last time you exported.

Exporting in Figma

Automated responsive images

The syntax of responsive images is complex to the point that doing it by hand is often out of the question. I’d highly recommend automating and abstracting as much of this away as possible. Fortunately, a lot of tooling that helps you build websites knows this and includes some sort of support for it. I think that’s great because that’s what software should be doing for us, particularly when it is something that is entirely programmatic and can be done better by code than by humans. Here are some examples…

  • Cloudinary has this responsive breakpoints tool including an API for generating the perfect breakpoints.
  • WordPress generates multiple versions of images and outputs in the responsive images syntax by default.
  • Gatsby has a grab-bag of plugins for transforming and implementing images on your site. You ultimately implement them with gatsby-image, which is a whole fancy thing for implementing responsive images and other image loading optimizations. Speaking of React, it has component abstractions like “An Almost Ideal React Image Component” that also does cool stuff.
  • Nicolas Hoizey’s Images Responsiver Node module (and it’s Eleventy plugin) makes a ton of smart markup choices for you, and pairs nicely with a CDN that can handle the on-the-fly resizing bits.
  • These are just a few examples! Literally anything you can do to make this process easier or automatic is worth doing.
Here’s me inspecting an image in a WordPress blog post and seeing a beefy srcset with a healthy amount of pre-generated size options and a sizes attribute tailored to this theme.
A landing page for gatsby-image explaining all of the additional image loading stuff it can do.

I’m sure there are many more CMSs and other software products that help automate away the complexities of creating the responsive images syntax. While I love that all this syntax exists, I find it all entirely too cumbersome to author by hand. Still, I think it’s worth knowing all this syntax so that we can build our own abstractions, or check in on the abstractions we’re using to make sure they are doing things correctly.

Related concepts

  • The object-fit property in CSS controls how an image will behave in its own box. For example, an image will normally “squish” if you change the dimensions to something different than its natural aspect ratio, but object-fit can be used to crop it or contain it instead.
  • The object-position property in CSS allows you to nudge an image around within its box.

What about responsive images in CSS with background images?

We’ve covered exactly this before. The trick is to use @media queries to change the background-image source. For example:

.img {   background-image: url(small.jpg); } @media    (min-width: 468px),   (-webkit-min-device-pixel-ratio: 2),    (min-resolution: 192dpi) {   .img {     background-image: url(large.jpg);   } }

With this CSS syntax, depending on the browser conditions, the browser will only download one of the two images, which achieves the same performance goal that the responsive images syntax in HTML does. If it helps, think of the above as the CSS equivalent of the <picture> syntax: the browser must follow your rules and display what matches.

If you’re looking to let the browser choose the best option, like srcset/sizes, but in CSS, the solution is ultimately going to be the image-set() function. There’s two problems with image-set(), today, though:

  • Support for it isn’t there yet. Safari’s implementation leads the pack, but image-set() has been prefixed in Chrome for eight years, and it’s not there at all in Firefox.
  • Even the spec itself seems behind the times. For example, it only supports x descriptors (no w, yet).

Best to just use media queries for now.

Do you need to polyfill?

I’m pretty meh on pollyfilling any of this right this moment. There is a great polyfill though, called Picturefill, which will buy you full IE 9-11 support if you need that. Remember, though, that none of this stuff breaks to the point of not displaying any image at all in non-supporting browsers, assuming you have an <img src="" alt=""> in there somewhere. If you make the (fairly safe) assumption that IE 11 is running on a low-pixel-density desktop display, you can make your image sources reflect that by default and build out from there.

Other important image considerations

  • Optimizing quality: The point of responsive images is loading the smallest, most impactful resource that you can. You can’t achieve that without effectively compressing your image. You’re aiming for a “sweet spot” for every image, between looking good and being light. I like to let image hosting services solve this problem for me, but Etsy has a really great writeup of what they’ve been able to accomplish with infrastructure that they built themselves.
  • Serving from CDNs: Speaking of image hosting services, speed comes in many forms. Fast servers that are geographically close to the user are an important speed factor as well.
  • Caching: What’s better than loading less data over the network? Loading no data at all! That’s what HTTP caching is for. Using the Cache-Control header, you can tell the browser to hang on to images so that if the same image is needed again, the browser doesn’t have to go over the network to get it, which is a massive performance boost for repeat viewings.
  • Lazy loading: This is another way to avoid loading images entirely. Lazy loading means waiting to download an image until it is in or near the viewport. So, for example, an image way far down the page won’t load if the user never scrolls there.

Other good resources

(That I haven’t linked up in the post already!)

Browser Support

This is for srcset/sizes, but it’s the same for <picture>.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
38 38 No 16 9

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
81 68 81 9.0-9.2

The post A Guide to the Responsive Images Syntax in HTML appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

A Complete Guide to CSS Functions

Introduction

In programming, functions are a named portion of code that performs a specific task. An example of this could be a function written in JavaScript called sayWoof():

function sayWoof() {   console.log("Woof!"); }

We can use this function later in our code, after we have defined our desired behavior. For this example, any time you type sayWoof() in your website or web app’s JavaScript it will print “Woof!” into the browser’s console.

Functions can also use arguments, which are slots for things like numbers or bits of text that you can feed into the function’s logic to have it modify them. It works like this in JavaScript:

function countDogs(amount) {   console.log("There are " + amount + " dogs!"); }

Here, we have a function called countDogs() that has an argument called amount. When a number is provided for amount, it will take that number and add it to a pre-specified sentence. This lets us create sentences that tell us how many dogs we’ve counted.

countDogs(3); // There are 3 dogs! countDogs(276); // There are 276 dogs! countDogs("many"); // There are many dogs!

Some programming languages come with baked-in functions to help prevent you from having to reinvent the wheel for every new project. Typically, these functions are built to help make working with the main strengths and features of the language easier. 

Take libraries, for example. Libraries are collections of opinionated code made to help make development faster and easier, effectively just curated function collections — think FitVids.js for creating flexible video elements.

Like any other programming language, CSS has functions. They can be inserted where you’d place a value, or in some cases, accompanying another value declaration. Some CSS functions even let you nest other functions within them!

Basics of CSS Functions

Anatomy of a CSS declaration. Inside of a selector class called .selector there is a declaration of background-image: url(‘parchment.jpg’); Arrows label the property (background-image), the function (url), and the argument (parchment.jpg).

Unlike other programming languages, we cannot create our own functions in CSS, per se. That kind of logic is reserved for CSS selectors, which allow you to create powerful conditional styling rules

As opposed to other programming languages — where the output of a function typically invisibly affects other logic down the line — the output of CSS functions are visual in nature. This output is used to control both layout and presentation of content. For example: 

.has-orange-glow {   filter: drop-shadow(0.25rem 0 0.75rem #ef9035); }

The CSS filter function drop-shadow() uses the arguments we provide it to create an orange outer glow effect on whatever it is applied to.

In the following demo, I have a JavaScript function named toggleOrangeGlow that toggles the application of the class .has-orange-glow on the CSS-Tricks logo. Combining this with a CSS transition, we’re able to create a cool glowing effect:

You may be familiar with some CSS functions, but the language has a surprisingly expansive list! 

Much like any other technology on the web, different CSS functions have different levels of browser support. Make sure you research and test to ensure your experience works for everyone, and use things like @supports to provide quality alternate experiences.

Common CSS Functions

url()

url() allows you to link to other resources to load them. This can include images, fonts, and even other stylesheets. For performance reasons, it’s good practice to limit the things you load via url(), as each declaration is an additional HTTP request.

attr()

This function allows us to reach into HTML, snag an attribute’s content, and feed it to the CSS content property. You’ll commonly see attr() used in print stylesheets, where it is used to show the URL of a link after its text. Another great application of this function is using it to show the alt description of an image if it fails to load.

calc()

If there’s one function you should spend some time experimenting with, it’s calc(). This function takes two arguments and calculates a result from the operator (+, -, *, /) you supply it, provided those arguments are numbers with or without an accompanying unit.

Unlike CSS preprocessors such as Sass, calc() can mix units, meaning you can do things like subtract 6rem from 100%. calc() is also updated on the fly, so if that 100% represents a width, it’ll still work if that width changes. calc() can also accept CSS Custom Properties as arguments, allowing you an incredible degree of flexibility

lang()

Including a lang attribute in your HTML is a really important thing to do. When present in your HTML, you’re able to use the lang() function to target the presence of the attribute’s value and conditionally apply styling based on it. 

One common use for this selector is to set language-specific quotes, which is great for things like internationalization. 

Clever designers and developers might also use it as a hook for styling translated versions of their sites, where cultural and/or language considerations mean there’s different perceptions about things like negative space.

:not()

This pseudo class selector will select anything that isn’t what you specify. For example, you could target anything that isn’t an image with body:not(img). While this example is dangerously powerful, scoping :not() to more focused selectors such as BEM’s block class can give you a great deal of versatility. 

Currently, :not() supports only one selector for its argument, but support for multiple comma-separated arguments (e.g. div:not(.this, .that)) is being worked on!

CSS Custom Properties

var() is used to reference a custom property declared earlier in the document. It is incredibly powerful when combined with calc().

An example of this is declaring a custom property called --ratio: 1.618; in the root of the document, then invoking it later in our CSS to control line height: line-height: var(--ratio);.

Here, var() is a set of instructions that tells the browser, “Go find the argument called --ratio declared earlier in the document, take its value, and apply it here.” 

Remember! calc() lets us dynamically adjust things on the fly, including the argument you supply via var().

This allows us to create things like modular scale systems directly in CSS with just a few lines of code. If you change the value of --ratio, the whole modular scale system will update to match.

In the following CodePen demo, I’ve done exactly just that. Change the value of --scale in the Pen’s CSS to a different number to see what I mean:

It’s also worth mentioning that JavaScript’s setProperty method can update custom properties in real time. This allows us to quickly and efficiently make dynamic changes to things that previously might have required a lot of complicated code to achieve. 

Color Functions

Another common place you see CSS functions is when working with color.

rgb() and rgba()

These functions allow you to use numbers to describe the red (r), green (g), blue (b), and alpha (a) levels of a color. For example, a red color with a hex value of #fb1010 could also be described as rgba(251, 16, 16, 1). The red value, 251, is far higher than the green and blue values (16 and 16), as the color is mostly comprised of red information. 

The alpha value of 1 means that it is fully opaque, and won’t show anything behind what the color is applied to. If we change the alpha value to be 0.5, the color will be 50% transparent. If you use an rgb() function instead of rgba(), you don’t have to supply an alpha value. This creates terser code, but prevents you from using transparency.

hsl() and hsla()

Similar to rgb() and rgba(), hsl() and hsla() are functions that allow you to describe color. Instead of using red, green, and blue, they use hue (h), saturation (s), and lightness (l). 

I prefer using hsla() over rgba() because its model of describing color works really well with systematized color systems. Each of the color level values for these functions can be CSS Custom Properties, allowing you to create powerful, dynamic code.

Syntax updates

In the upcoming CSS Color Module Level 4 spec, we can ignore the a portion of rgba() and hsla(), as well as the commas. Now, spaces are used to separate the rgb and hsl arguments, with an optional / to indicate an alpha level.

https://twitter.com/argyleink/status/1218305696862588928

Pseudo Class Selectors

These selectors use specialized argument notation that specifies patterns of what to select. This allows you to do things like select every other element, every fifth element, every third element after the seventh element, etc.

Pseudo class selectors are incredibly versatile, yet often overlooked and under-appreciated. Many times, a thoughtful application of a few of these selectors can do the work of one or more node packages. 

:nth-child()

nth-child() allows you to target one or more of the elements present in a group of elements that are on the same level in the Document Object Model (DOM) tree.

In the right hands, :nth-child() is incredibly powerful. You can even solve fizzbuzz with it! If you’re looking for a good way to get started, Chris has a collection useful pseudo selector recipes.

:nth-last-child()

This pseudo class selector targets elements in a group of one or more elements that are on the same level in the DOM. It starts counting from the last element in the group and works backwards through the list of available DOM nodes.

:nth-last-of-type()

This pseudo class selector can target an element in a group of elements of a similar type. Much like :nth-last-child(), it starts counting from the last element in the group. Unlike :nth-last-child, it will skip elements that don’t apply as it works backwards. 

:nth-of-type()

:nth-of-type() matches a specified collection of elements of a given type. For example, a declaration of img:nth-of-type(5) would target the fifth image on a page.

Animation Functions

Animation is an important part of adding that certain je ne sais quoi to your website or web app. Just remember to put your users’ needs first and honor their animation preferences.

Creating animations also requires controlling the state of things over time, so functions are a natural fit for making that happen.

cubic-bezier()

Instead of keyword values like ease, ease-in-out, or linear, you can use cubic-bezier() to create a custom timing function for your animation. While you can read about the math that powers cubic beziers, I think it’s much more fun to play around with making one instead.

A custom cubic bezier curve created on cubic-bezier.com. There are also options to preview and compare your curve with CSS’s ease, linear, ease-in, ease-out, and ease-in-out transitions.
Lea Verou’s cubic-bezier.com.

path()

This function is paired with the offset-path property. It allows you to “draw” a SVG path that other elements can be animated to follow.

Both Michelle Barker and Dan Wilson have published excellent articles that go into more detail about this approach to animation.

steps()

This relatively new function allows you to set the easing timing across an animation, which allows for a greater degree of control over what part of the animation occurs when. Dan Wilson has another excellent writeup of how it fits into the existing animation easing landscape. 

Sizing and Scaling Functions

One common thing we do with animation is stretch and squash stuff. The following functions allow you to do exactly that. There is a catch, however: These CSS functions are a special subset, in that they can only work with the transform property.

scaleX(), scaleY(), scaleZ(), scale3d(), and scale()

Scaling functions let you increase or decrease the size of something along one or more axes. If you use scale3d() you can even do this in three dimensions!

translateX(), translateY(), translateZ(), translate3d(), and translate()

Translate functions let you reposition an element along one or more axes. Much like scale functions, you can also extend this manipulation into three dimensions.

perspective()

This function lets you adjust the appearance of an object to make it look like it is projecting up and out from its background.

rotateX(), rotateY(), rotateZ(), rotate3d(), and rotate()

Rotate functions let you swivel an element along one or more axes, much like grasping a ball and turning it around in your hand.

skewX(), skewY(), and skew()

Skew functions are a little different from scaling and rotation functions in that they apply a distortion effect relative to a single point. The amount of distortion is proportionate to the angle and distance declared, meaning that the further the effect continues in a direction the more pronounced it will be. 

Jorge Moreno also did us all a favor and made a great tool called CSS Transform Functions Visualizer. It allows you to adjust sizing and scaling in real time to better understand how all these functions work together:

As responsible web professionals, we should be mindful of our users and the fact that they may not be using new or powerful hardware to view our content. Large and complicated animations may slow down the experience, or even cause the browser to crash in extreme scenarios.

To prevent this, we can use techniques like will-change to prepare the browser for what’s in store, and the update media feature to remove animation on devices that do not support a fast refresh rate. 

Filter Functions

CSS filter functions are another special subset of CSS functions, in that they can only work with the filter property. Filters are special effects applied to an element, mimicking functionality of graphics editing programs such as Photoshop.

You can do some really wild things with CSS filter functions, stuff like recreating the effects you can apply to your posts on Instagram!

brightness()

This function adjusts how, um, bright something appears. Setting it to a low level will make it appear as if it has had a shadow cast over it. Setting it to a high level will blow it out, like an over-exposed photo.

blur()

If you’re familiar with Photoshop’s Gaussian Blur filter, you know how blur() works. The more of this you apply, the more indistinct the thing you apply it to will look.

contrast()

contrast() will adjust the degree of difference between the lightest and darkest parts of what is applied to.

grayscale()

grayscale() removes the color information from what it is applied to. Remember that this isn’t an all-or-nothing affair! You can apply a partial grayscale effect to make something look weathered or washed out.

An interesting application of grayscale() could be lightly applying it to images when dark mode is enabled, to slightly diminish the overall vibrancy of color in a situation where the user may want less eye strain.

invert()

While invert() can be used to make something look like a photo negative, my favorite technique is to use it in a inverted colors media query to invert inverted images and video:

@media (inverted-colors: inverted) {   img,   video {     filter: invert(100%);   } }

This ensures that image and video content looks the way it should, regardless of a user’s expressed browsing mode preferences. 

opacity()

This function controls how much of the background is visible through the element (and child elements) the function is applied to. 

An element that has 0% opacity will be completely transparent, although it will still be present in the DOM. If you need to remove an object completely, use other techniques such as the hidden attribute.

saturate()

Applying this filter can enhance, or decrease the intensity of the color of what it is applied to. Enhancing an image’s saturation is a common technique photographers use to fix underexposed photos.

sepia()

There are fancier ways to describe this, but realistically it’s a function that makes something look like it’s an old-timey photograph.

drop-shadow()

A drop shadow is a visual effect applied to an object that makes it appear like it is hovering off of the page. There’s a bit of a trick here, in that CSS also allows you to apply drop shadow effects to text and elements. It’s also distinct from the box-shadow property is that it applies drop shadows to the shape of an element rather than the actual box of an element.

Skilled designers and developers can take advantage of this to create complicated visual effects.

hue-rotate()

When a class with a declaration containing hue-rotate() is applied to an element, each pixel used to draw that element will have it’s hue valued shifted by the amount you specify. hue-rotate()‘s effect is applied to each and every pixel it is applied to, so all colors will update relative to their hue value’s starting point.

This can create a really psychedelic effect when applied to things that contain a lot of color information, such as photos.

SVG filters 

filter() also lets us import SVGs filters to use to create specialized visual effects. The topic is too complicated to really do it justice in this article — if you’re looking for a good starting point, I recommend “The Art Of SVG Filters And Why It Is Awesome” by Dirk Weber.

The word “West!” rendered in a Wild West-style font, with layered teal drop shadows giving it a 3D effect. Behind it is a purple starburst pattern. Screenshot.
This effect was created by skillful application of SVG filter effects.

Gradient Functions

Gradients are created when you transition one color to one or more other colors. They are workhorses of modern user interfaces — skilled designers and developers use them to lend an air of polish and sophistication to their work.

Gradient functions allow you to specify a whole range of properties, including:

  • Color values,
  • The position on the gradient area where that color comes in,
  • What angle the gradient is positioned at.

And yes, you guessed it: the colors we use in a gradient can be described using CSS color functions!

linear-gradient() and repeating-linear-gradient()

Linear gradients apply the color transformation in a straight line, from one point to another — this line can be set at an angle as well. In cases where there’s more area than gradient, using repeating-linear-gradient() will, er, repeat the gradient you described until all the available area has been filled.

radial-gradient() and repeating-radial-gradient()

Radial gradients are a lot like linear gradients, only instead of a straight line, color transformations radiate outward from a center point. They’re oftentimes used to create a semitransparent screen to help separate a modal from the background it is placed over.

conic-gradient() and repeating-conical-gradient

Conic gradients are different from radial gradients in that the color rotates around a circle. Because of this, we can do neat things like create donut charts. Unfortunately, support for conic gradients continues to be poor, so use them with caution.

A teal and red donut chart set to 36%. To the right of the chart is a range slider, also set to 36%. Screenshot.
An adjustable conic gradient donut chart made by Ana Tudor

Grid Functions

CSS Grid is a relatively new feature of the language. It allows us to efficiently create adaptive, robust layouts for multiple screen sizes. 

It’s worth acknowledging our roots. Before Grid, layout in CSS was largely a series of codified hacks to work with a language originally designed to format academic documents. Grid’s introduction is further acknowledgement that the language’s intent has changed. 

Modern CSS is an efficient, fault-tolerant language for controlling presentation and layout across a wide range of device form factors. Equipped with Grid and other properties like flexbox, we’re able to create layouts that would have been impossible to create in earlier iterations of CSS. 

Grid introduces the following CSS functions to help you use it.

fit-content()

This function “clamps” the size of grid rows or columns, letting you specify a maximum size a grid track can expand to. fit-content() accepts a range of values, but most notable among them are min-content and max-content. These values allow you to tie your layout to the content it contains. Impressive stuff!

minmax()

minmax() allows you to set the minimum and maximum desired heights and widths of your grid rows and columns. This function can also use min-content and max-content, giving us a great deal of power and flexibility.

repeat()

You can loop through patterns of grid column and rows using repeat(). This is great for two scenarios: 

  1. When you do know how many rows or columns you need, but typing them out would be laborious. A good example of this would be constructing the grid for a calendar.
  2. When you don’t know how many rows or columns you need. Here, you can specify a template that the browser will honor as it propagates content into your layout.

Shape Functions

Like filter() and transform(), shape CSS functions only work with one property: clip-path. This property is used to mask portions of something, allowing you to create all sorts of cool effects.

circle()

This function creates a circular shape for your mask, allowing you to specify its radius and position.

ellipse()

Like circle(), ellipse() will draw a rounded shape, only instead of a perfect circle, ellipse() lets you construct an oblong mask.

inset()

This function will mask out a rectangle inside of the element you apply it to.

polygon()

With polygon(), you are able to specify an arbitrary number of points, allowing you to draw complicated shapes. polygon() also takes an optional fill-rule argument, which specifies which part of the shape is the inside part.

Miscellaneous Functions

These are the un-categorizable CSS functions, things that don’t fit neatly elsewhere.

element()

Ever pointed a camera at its own video feed? That’s sort of what element() does. It allows you to specify the ID of another element to create an “image” of what that element looks like. You can then apply other CSS to that image, including stuff like CSS filters!

It might take a bit to wrap your head around the concept — and it has some support concerns — but element() is a potentially very powerful in the right hands.

Preethi Sam‘s “Using the Little-Known CSS element() Function to Create a Minimap Navigator” demonstrates how to use it to create a code minimap and is an excellent read.

Here, she’s created a minimap for reading through a longform article:

image-set()

This function allows you to specify a list of images for the browser to select for a background image, based on what it knows about the capabilities of its display and its connection speed. It is analogous to what you would do with the srcset property.

::slotted()

This is a pseudo-element selector used to target elements that have been placed into a slot inside a HTML template. ::slotted() is intended to be used when working with Web Components, which are custom, developer-defined HTML elements.

Not Ready for Prime Time

Like any other living programming language, CSS includes features and functionality that are actively being worked on. 

These functions can sometimes be previewed using browsers that have access to the bleeding edge. Firefox Nightly and Chrome Canary are two such browsers. Other features and functionality are so new that they only exist in what is being actively discussed by the W3C.

annotation()

This function enables Alternate Annotation Forms, characters reserved for marking up things like notation and annotation. These characters typically will be outlined with a circle, square, or diamond shape.

Not many typefaces contain Alternate Annotation Forms, so it’s good to check to see if the typeface you’re using includes them before trying to get annotation() to work. Tools such as Wakamai Fondue can help with that.

he numbers 1 and 2 enclosed in hollow and solid-filled circles. Following them are the letters B and R enclosed in hollow and solid-filled squares. Screenshot.Stylistic Alternates.
Examples of annotation glyphs from Jonathan Harrell’s post, “Better Typography with Font Variants”

counter() and counters()

When you create an ordered list in HTML, the browser will automatically generate numbers for you and place them before your list item content. These pieces of browser-generated list content are called counters. 

By using a combination of the ::marker pseudo-element selector, the content property, and the counter() function, we can control the content and presentation of the counters on an ordered list. For browsers that don’t support counter() or counters() yet, you still get a decent experience due to the browser automatically falling back to its generated content:

For situations where you have nested ordered lists, the counters() function allows a child ordered list to access its parent. This allows us to control their content and presentation. If you want to learn more about the power of ::marker, counter(), and counters(), you can read “CSS Lists, Markers, And Counters” by Rachel Andrew.

cross-fade()

This function will allow you to blend one background image into one or more other background images. Its proposed syntax is similar to gradient functions, where you can specify the stops where images start and end.

dir()

This function allows you to flip the orientation of a language’s reading order. For English, that means a left-to-right (ltr) reading order gets turned into right-to-left (rtl). Only Firefox currently has support for dir(), but you can achieve the same effect in Chromium-based browsers by using an attribute selector such as [dir="rtl"].

env()

env(), short for environment, allows you to create conditional logic that is triggered if the device’s User Agent matches up. It was popularized by the iPhone X as a method to work with its notch

That being said, device sniffing is a fallacious affair — you shouldn’t consider env() a way to cheat it. Instead, use it as intended: to make sure your design works for devices that impose unique hardware constraints on the viewport.

has()

has() is a relational pseudo-class that will target an element that contains another element, provided there is at least one match in the HTML source. An example of this is be a:has(> img), which tells the browser to target any link that contains an image. 

A diagram showing how the CSS selector a:has(> img) targets only links that contain images in a collection of links that contain either images or paragraphs.

Interestingly, has() is currently being proposed as CSS you can only write in JavaScript. If I were to wager a guess as to why this is, it is to scope the selector for performance reasons. With this approach has() is triggered only after the browser has been told to process conditional logic, and therefore query the state of things.

image()

This function will let you insert either a static image (referenced with url(), or draw one dynamically via gradients and element()

Trigonometry functions

These functions will allow us to perform more advanced mathematical operations

  • Sine: sin()
  • Cosine: cos()
  • Tangent: tan()
  • Arccosine: acos()
  • Arcsine: asin()
  • Arctangent: atan()
  • Arctangent: atan2()
  • Square root: sqrt()
  • The square root of the sum of squares of its arguments: hypot()
  • Power: pow()

I’m especially excited to see what people who are more clever than I am will do with these functions, especially for things like animation!

clamp()

When providing minimum, maximum, and preferred values as arguments, clamp() will honor the preferred value so long as it does not exceed the minimum and maximum boundaries. 

clamp() will allow us to author things like components whose size will scale along with the size of the viewport, but won’t shrink or grow past a specific size. This will be especially useful for creating CSS locks, where you can ensure a responsive type size will not get so small that it can’t be read.

:host() and :host-context()

To be honest, I’m a little hazy on the specifics of the jargon and mechanics that power the Shadow DOM. Here’s how the MDN describes host():

The :host() CSS pseudo-class function selects the shadow host of the shadow DOM containing the CSS it is used inside (so you can select a custom element from inside its shadow DOM) — but only if the selector given as the function’s parameter matches the shadow host.

And here’s what they have to say about :host-context():

The :host-context() CSS pseudo-class function selects the shadow host of the shadow DOM containing the CSS it is used inside (so you can select a custom element from inside its shadow DOM) — but only if the selector given as the function’s parameter matches the shadow host’s ancestor(s) in the place it sits inside the DOM hierarchy.

:is() and :where()

:is() has had a bit of an identity crisis. Previously referred to as both matches() and vendor prefixed as :-webkit-any/:-moz-any, it now enjoys a standardized, agreed-upon name. It is a pseudo class selector that accepts a range of selectors as its argument. 

This allows an author to group and target a wide range of selectors in an efficient way. :where() is much like :is(), only it has a specificity of zero, while the specificity of :is() is set to the highest specificity in the provided selector list. 

:is() and :where() will allow us a good deal of flexibility about how we select things to style, especially for situations where you may not have as much control over the web site or web app’s stylesheet (e.g. third-party integrations, ads, etc.).

max() and min()

These functions allow you to select either the maximum or minimum value from a range of values you provide. Much like clamp(), these functions allow us to make things responsive up until a certain point. 

:nth-col() and :nth-last-col()

These pseudo-classes will allow you to select one or a specified series columns in a CSS grid to apply styling to them. A good mental model for how these functions will work is how CSS pseudo class selectors operate. Unlike pseudo class selectors, :nth-col() and :nth-last-col() should be able to target implicit grid columns.

symbols()

This function allows you to specify a list of different kinds of characters to use for list bullets. Much like annotation(), you’ll want to make sure the typeface you use contains a glyph you want to use as a symbol before trying to get symbols() to work.

Deprecated Functions

Sometimes things just don’t work out the way you think they will. While deprecated CSS functions may still render in the browser for legacy support reasons, it isn’t recommended you use them going forward.

matrix() and matrix3d()

These functions were turned into more discrete sizing and scaling functions.

rect()

This function was part of the deprecated clip property. Use the clip-path property and its values instead.

target-counter(), target-counters(), and target-text()

These functions were intended to help work with fragment URLs for paged (printed) media. You can read more about them on the W3C’s CSS Generated Content for Paged Media Module documentation

Typography

The web is typography, so it makes sense to give your type the care and attention it deserves. While CSS provides some functions specifically designed to unlock the potential of your website or webapp’s chosen typefaces, it is advised to not use the following functions to access these advanced features. 

Instead, use lower-level syntax via font-feature-settings. You can figure out if the font you’re using supports these features by using a  tool such as Wakamai Fondue.

character-variant(), styleset(), and stylistic()

Many typefaces made by professional foundries include alternate treatments for certain letters, or combinations of letters. One example use case is providing different variations of commonly-used letters for typefaces designed to look like handwriting, to help make it appear more natural-looking.

Two examples of the sentence, “Easy Sunday morning & my fox. The first sentence does not have Stylistic Alternates enabled. The second sentence does, with the alternate characters (a, “un”, “m, “rn” g, &, m, f, and x) highlighted in green. Screenshot.
Stylistic Alternates example by Tunghsiao Liu’s “OpenType Features in CSS”

Utilizing these functions activates these special alternate characters, provided they are present in the font’s glyph set

Unfortunately, it is not a standardized offering. Different typefaces will have different ranges of support, based on what the typographer chose to include. It would be wise to check to see if the font you’re using supports these special features before writing any code.

format()

When you are importing a font via the url() function, the format() function is an optional hint that lets you manually specify the font’s file format. If this hint is provided, the browser won’t download the font if it does not recognize the specified file format.

@font-face {   font-family: 'MyWebFont';   src: url('mywebfont.woff2') format('woff2'), /* Cutting edge browsers */        url('mywebfont.woff') format('woff'), /* Most modern Browsers */        url('mywebfont.ttf') format('truetype'); /* Older Safari, Android, iOS */ }
leader()

You know when you’re reading a menu at a restaurant and there’s a series of periods that help you figure out what price is attached to what menu item? Those are leaders. 

The W3C had plans for them with its CSS Generated Content for Paged Media Module, but it unfortunately seems like leader() never quite managed to take off. Fortunately, the W3C also provides an example of how to accomplish this effect using a clever application of the content property.

local()

local() allows you to specify a font installed locally, meaning it is present on the device. Local fonts either ship with the device, or can be manually installed. 

Betting on someone installing a font so things look the way you want them to is very risky! Because of this, it is recommended you don’t specify a local font that needs to be manually installed. Your site won’t look the way it is intended to, even moreso if you don’t specify a fallback font.

@font-face {   font-family: 'FeltTipPen';   src: local('Felt Tip Pen Web'), /* Full font name */        local('FeltTipPen-Regular'); /* Postscript name */ }
ornaments()

Special dingbat characters can be enabled using this function. Be careful, as not all dingbat characters are properly coded in a way that will work well if a user does something like change the font, or use a specialized browsing mode.

swash()

Swashes are alternate visual treatments for letters that give them an extra-fancy flourish. They’re commonly found in italic and cursive-style typefaces.

An example of a swash being applied to a script-style typeface. There’s two versions of the phrase, “Fred And Ginger”. The first version doesn’t have swashes activated. The second example does. In the second example, the letter F, and, and the letter G are highlighted to demonstrate them being activated. Screenshot.
Swash example by Tunghsiao Liu’s “OpenType Features in CSS”

Why so many?

CSS is maligned as frequently as it is misunderstood. The guiding thought to understanding why all these functions are made available to us is knowing that CSS isn’t prescriptive — not every website has to look like a Microsoft Word document. 

The technologies that power the web are designed in such a way that someone with enough interest can build whatever they want. It’s a powerful, revolutionary concept, a large part of why the web became so ubiquitous.

The post A Complete Guide to CSS Functions appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Google’s Technical Writing Guide

It’s good!

I’ve written up my advice (sprinkled with great advice from others), but this is way more straightforward nuts-and-bolts training on technical writing. It’s structured like an actual course, with exercises along the way.

I’m far from an expert here. But between Geoff and I, we end up doing a lot of technical article editing for the sake of clarity.

Comedy writers seek the funniest results, horror writers strive for the scariest, and technical writers aim for the clearest. In technical writing, clarity takes precedence over all other rules. 

It can be tricky to get right. Read the section on Active voice. That’s easy for anyone to get wrong.

Essentially every single rule is just an extension of “make it more clear.”

Direct Link to ArticlePermalink

The post Google’s Technical Writing Guide appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

A Complete Guide to calc() in CSS

CSS has a special calc() function for doing basic math. Here’s an example:

.main-content {   /* Subtract 80px from 100vh */   height: calc(100vh - 80px); }

In this guide, let’s cover just about everything there is to know about this very useful function.

calc() is for values

The only place you can use the calc() function is in values. See these examples where we’re setting the value for a number of different properties.

.el {   font-size: calc(3vw + 2px);   width:     calc(100% - 20px);   height:    calc(100vh - 20px);   padding:   calc(1vw + 5px); }

It could be used for only part of a property too, for example:

.el {   margin: 10px calc(2vw + 5px);   border-radius: 15px calc(15px / 3) 4px 2px;   transition: transform calc(1s - 120ms); }

It can even be a part of another function that forms a part of a property! For example, here’s calc() used within the color stops of a gradient

.el {   background: #1E88E5 linear-gradient(     to bottom,     #1E88E5,     #1E88E5 calc(50% - 10px),     #3949AB calc(50% + 10px),     #3949AB   ); }

calc() is for lengths

Notice all the examples above are essentially numbers-based. We’ll get to some of the caveats of how the numbers can be used (because sometimes you don’t need a unit), but this is for number math, not strings or anything like that.

.el {   /* Nope! */   counter-reset: calc("My " + "counter"); } .el::before {   /* Nope! */   content: calc("Candyman " * 3); }

There are many lengths of CSS though, and they can all be used with calc():

  • px
  • %
  • em
  • rem
  • in
  • mm
  • cm
  • pt
  • pc
  • ex
  • ch
  • vh
  • vw
  • vmin
  • vmax

You can also not perform any calculation and it is still valid:

.el {   /* Little weird but OK */   width: calc(20px); }

Nope on media queries

When calc() is used correctly (length units used as a value to a property), it sadly calc() won’t work when applied to media queries.

@media (max-width: 40rem) {   /* Narrower or exactly 40rem */ }  /* Nope! */ @media (min-width: calc(40rem + 1px)) {   /* Wider than 40rem */ }

It would be cool someday because you could do mutually exclusive media queries in a fairly logical way (like above).

Mixing units 🎉

This is perhaps the most valuable feature of calc()! Almost every example above has already done this, but just to put a point on it, here it is mixing different units:

/* Percentage units being mixed with pixel units */ width: calc(100% - 20px);

That’s saying: As wide as the element is, minus 20 pixels.

There is literally no way to pre-calculate that value in pixels alone in a fluid width situation. In other words, you can’t preprocess calc() with something like Sass as an attempted complete a polyfill. Not that you need to, as the browser support is fine. But the point is that it has to be done in the browser (at “runtime”) when you mix units in this way, which is most of the value of calc().

Here’s some other examples of mixing units:

transform: rotate(calc(1turn + 45deg));  animation-delay: calc(1s + 15ms);

Those probably could be preprocessed as they mix units that aren’t relative to anything that is determined at runtime.

Comparison to preprocessor math

We just covered that you can’t preprocess the most useful things that calc() can do. But there is a smidge of overlap. For example, Sass has math built into it, so you can do things like:

$ padding: 1rem;  .el[data-padding="extra"] {   padding: $ padding + 2rem; // processes to 3rem;   margin-bottom: $ padding * 2; // processes to 2rem;  }

Even math with units is working there, adding same-unit values together or multiplying by unitless numbers. But you can’t mix units and it has similar limitations to calc() (e.g. like multiplying and dividing must be with unit-less numbers).

Show the math

Even you aren’t using a feature that is uniquely possible only with calc(), it can be used to “show your work” inside CSS. For example, say you need to calculate exactly 17th the width of an element…

.el {   /* This is easier to understand */   width: calc(100% / 7);    /* Than this is */   width: 14.2857142857%; }

That might pan out in some kind of self-created CSS API like:

[data-columns="7"] .col { width: calc(100% / 7); } [data-columns="6"] .col { width: calc(100% / 6); } [data-columns="5"] .col { width: calc(100% / 5); } [data-columns="4"] .col { width: calc(100% / 4); } [data-columns="3"] .col { width: calc(100% / 3); } [data-columns="2"] .col { width: calc(100% / 2); }

The Math operators of calc()

You’ve got +, -, *, and /. But they differ in how you are required to use them.

Addition (+) and subtraction (-) require both numbers to be lengths

.el {   /* Valid 👍 */   margin: calc(10px + 10px);    /* Invalid 👎 */   margin: calc(10px + 5); }

Invalid values invalidate the whole individual declaration.

Division (/) requires the second number to be unitless

.el {   /* Valid 👍 */   margin: calc(30px / 3);    /* Invalid 👎 */   margin: calc(30px / 10px);    /* Invalid 👎 (can't divide by 0) */   margin: calc(30px / 0); }

Multiplication (*) requires one of the numbers to be unitless

.el {   /* Valid 👍 */   margin: calc(10px * 3);    /* Valid 👍 */   margin: calc(3 * 10px);    /* Invalid 👎 */   margin: calc(30px * 3px); }

Whitespace matters

Well, it does for addition and subtraction.

.el {   /* Valid 👍 */   font-size: calc(3vw + 2px);    /* Invalid 👎 */   font-size: calc(3vw+2px);    /* Valid 👍 */   font-size: calc(3vw - 2px);    /* Invalid 👎 */   font-size: calc(3vw-2px); }

I imagine it has to do with negative numbers somehow, as using negative numbers (e.g. calc(5vw - -5px)) is OK. I suppose that makes something like calc(5vw--5px) weird, especially in how custom properties use double-dashes (e.g. var(--padding)).

Multiplication and division do not need the whitespace around the operators. But I’d think good general advice is to include the space for readability and muscle memory for the other operators.

Whitespace around the outsides doesn’t matter. You can even do line breaks if you’d like:

.el {   width: calc(     100%     /   3   ); }

Careful about this, though: no spaces between calc() and the opening paren.

.el {   /* Invalid 👎 */   width: calc (100% / 3); }

Nesting calc(calc());

You can but it’s never necessary. It’s the same as using an extra set of parentheses without the calc() part. For example:

.el {   width: calc(     calc(100% / 3)     -     calc(1rem * 2)   ); }

You don’t need those inside calc() because the parens work alone:

.el {   width: calc(    (100% / 3)     -    (1rem * 2)   ); }

And in this case, the “order of operations” helps us even without the parentheses. That is, division and multiplication happen first (before addition and subtraction), so the parentheses aren’t needed at all. It could be written like this:

.el {   width: calc(100% / 3 - 1rem * 2); }

But feel free to use the parens if you feel like it adds clarity. If the order of operations doesn’t work in your favor (e.g. you really need to do the addition or subtraction first), you’ll need parens.

.el {   /* This */   width: calc(100% + 2rem / 2);    /* Is very different from this */   width: calc((100% + 2rem) / 2); }

CSS custom properties and calc() 🎉

Other than the amazing ability of calc() to mix units, the next most awesome thing about calc() is using it with custom properties. Custom properties can have values that you then use in a calculation:

html {   --spacing: 10px; }  .module {   padding: calc(var(--spacing) * 2); }

I’m sure you can imagine a CSS setup where a ton of configuration happens at the top by setting a bunch of CSS custom properties and then letting the rest of the CSS use them as needed.

Custom properties can also reference each other. Here’s an example where some math is used (note the lack of a calc() function at first) and then later applied. (It ultimately has to be inside of a calc().)

html {   --spacing: 10px;   --spacing-L: var(--spacing) * 2;   --spacing-XL: var(--spacing) * 3; }  .module[data-spacing="XL"] {   padding: calc(var(--spacing-XL)); }

Custom properties can come from the HTML, which is a pretty darn cool and useful thing sometimes. (See how Splitting.js adds indexes to words/characters as an example.)

<div style="--index: 1;"> ... </div> <div style="--index: 2;"> ... </div> <div style="--index: 3;"> ... </div>
div {   /* Index value comes from the HTML (with a fallback) */   animation-delay: calc(var(--index, 1) * 0.2s); }

Adding units later

In case you’re in a situation where it’s easier to store numbers without units, or do math with unit-less numbers ahead of time, you can always wait until you apply the number to add the unit by multiplying by 1 and the unit.

html {   --importantNumber: 2; }  .el {   /* Number stays 2, but it has a unit now */   padding: calc(var(--importantNumber) * 1rem); }

Messing with colors

Color format like RGB and HSL have numbers you can mess with using calc(). For example, setting some base HSL values and then altering them forming a system of your own creation (example):

html {   --H: 100;   --S: 100%;   --L: 50%; }  .el {   background: hsl(     calc(var(--H) + 20),     calc(var(--S) - 10%),     calc(var(--L) + 30%)   ) }

You can’t combine calc() and attr()

The attr() function in CSS looks appealing, like you can yank attribute values out of HTML and use them. But…

<div data-color="red">...</div>
div {   /* Nope */   color: attr(data-color); }

Unfortunately, there are no “types” in play here, so the only thing attr() is for are strings in conjunction with the content property. That means this works:

div::before {   content: attr(data-color); }

I mention this, because it might be tempting to try to pull a number in that way to use in a calculation, like:

<div class="grid" data-columns="7" data-gap="2">...</div>
.grid {   display: grid;    /* Neither of these work */   grid-template-columns: repeat(attr(data-columns), 1fr);   grid-gap: calc(1rem * attr(data-gap)); }

Fortunately, it doesn’t matter much because custom properties in the HTML are just as useful or more!

<div class="grid" style="--columns: 7; --gap: 2rem;">...</div>
.grid {   display: grid;    /* Yep! */   grid-template-columns: repeat(var(--columns), 1fr);   grid-gap: calc(var(--gap)); }

Browser tooling

Browser DevTools will tend you show you the calc() as you authored it in the stylesheet.

Firefox DevTools – Rules

If you need to figure out the computed value, there is a Computed tab (in all browser DevTools, at least that I know about) that will show it to you.

Chrome DevTools – Computed

Browser support

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
19* 4* 11 12 6*

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
80 68 80 6.0-6.1*

If you really needed to support super far back (e.g. IE 8 or Firefox 3.6), the usual trick is to add another property or value before the one that uses calc():

.el {   width: 92%; /* Fallback */   width: calc(100% - 2rem); }

There are quite a few known issues for calc() as well, but they are all for old browsers. Can I use… lists 13 of them, here’s a handful:

  • Firefox <59 does not support calc() on color functions. Example: color: hsl(calc(60 * 2), 100%, 50%).
  • IE 9 – 11 will not render the box-shadow property when calc() is used for any of the values.
  • Neither IE 9 – 11 nor Edge support width: calc() on table cells.

Use-case party

I asked some CSS developers when they last used calc() so we could have a nice taste here for for how others use it in their day-to-day work.

I used it to create a full-bleed utility class: .full-bleed { width: 100vw; margin-left: calc(50% - 50vw); } I’d say calc() is in my top 3 CSS things.

I used it to make space for a sticky footer.

I used it to set some fluid type / dynamic typography… a calculated font-size based on minimums, maxiums, and a rate of change from viewport units. Not just the font-size, but line-height too.

If you’re using calc() as part of a fluid type situation that involves viewport units and such, make sure that you include a unit that uses rem or em so that the user still has some control over bumping that font up or down by zooming in or out as they need to.

One I really like is having a “content width” custom property and then using that to create the spacing that I need, like margins: .margin { width: calc( (100vw - var(--content-width)) / 2); }

I used it to create a cross-browser drop-cap component. Here’s a part of it:

.drop-cap { --drop-cap-lines: 3; font-size: calc(1em * var(--drop-cap-lines) * var(--body-line-height)); }

I used it to make some images overflow their container on an article page.

I used it to place a visualization correctly on the page by combining it with padding and vw/vh units.

I use it to overcome limitations in background-position, but expecially limitations in positioning color stops in gradients. Like “stop 0.75em short of the bottom”.

Other Trickery

The post A Complete Guide to calc() in CSS appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

A Guide to Handling Browser Events

In this post, Sarah Chima walks us through how we can work with browser events, such as clicking, using JavaScript. There’s a ton of great info in here! If JavaScript isn’t your strong suit, I think this is the best explanation of event handling that I’ve read in quite some time.

When an event occurs, the browser creates an event object and passes it as an argument to the event handler. This event object contains details of the event. For instance, you might want to know which button was clicked or which key was pressed or the mouse coordinates when the event occurred. You can get all of these from the event object.

Sarah also links to this really good primer on bubbling and capturing that’s worth checking out, too.

Direct Link to ArticlePermalink

The post A Guide to Handling Browser Events appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

A Guide to Console Commands

The developer’s debugging console has been available in one form or another in web browsers for many years. Starting out as a means for errors to be reported to the developer, its capabilities have increased in many ways; such as automatically logging information like network requests, network responses, security errors or warnings.

There is also a way for a website’s JavaScript to trigger various commands that output to the console for debugging purposes. These commands are contained in a console object available in almost every browser. Even though these features are mostly consistent between browsers, there are a few differences. Some of these differences are simply visual in nature while others do have slight functional differences to keep in mind.

For the curious, here’s the spec by WHATWG linked from the MDN console docs.

This guide covers what’s available in the console object of Firefox and Chrome as they are often the most popular browsers for development and they do have a few differences in various aspects of the console. The new Chromium-based Edge is essentially the same as Chrome in many ways so, in most cases, the console commands will operate much the same.

Quick Links

The console.log command

The first thing we can do is log the console object itself to see what your browser of choice actually offers.

console.log(console);

This command will output the various properties of the console object as the browser knows them. Most of them are functions and will be rather consistent regardless of browser. If there are differences in the properties of the console object from one browser to another, this way you can see the differences. One such difference I can point out between Firefox and Chrome is that Chrome provides a “memory” property that outputs some basic memory usage stats. Firefox doesn’t provide this property and yet has a “name” property that Chrome does not have.

Thankfully, most of the differences between the browsers tend to be just as trivial. That way, you can be fairly confident that your code will output much the same regardless of the browser in use.

First things first: clear()

With heavy usage of the console comes a very crowded output of text. Sometimes you just want to clear things out and start with a fresh console. Browsers typically provide a button in DevTools that performs this task. However, the console object itself also provides a command to handle this:

console.clear();

This will clear the console and will helpfully inform you of that by outputting a message like “Console was cleared.”

Common usage: debug(), error(), info(), log(), and warn()

There are five commands that at first glance seem to do the exact same thing. And, technically, they do. But browsers provide additional features tied to the five commands to give each their own distinct benefit.

These five commands are:

console.debug(); console.error(); console.info(); console.log(); console.warn();

I’m sure many of you have seen console.log() before (I mean, we just talked about it up top) and have probably used it before. Before we get into what you can log in these five commands, let’s see our first minor difference between Chrome and Firefox.

Chrome console showing debug, error, info, log, and warn

This is an example in Chrome of each command outputting a string, such as console.debug('console.debug()');.  Notice that some of them have a color treatment to give a visual indication of the type of output it is. The error and warn outputs have an additional icon for even easier identification.

Firefox console showing debug, error, info, log, and warn

Here is the same list in Firefox and, while it looks similar, there are three minor differences. For example, console.debug() is not color-coded and console.info() has an additional icon next to it. In Chrome, both console.error() and console.warn() can be expanded to show additional information about the output while Firefox only does this with console.error(). This additional information provides a trace of the lines of code involved to get to where the particular command was called.

One thing that is useful about these five commands is that the browsers provide filtering options to show or hide each type as you wish. Firefox has them right there at the top of the console above the output while Chrome hides them in a dropdown, labeled “All levels” which you can see in the earlier Chrome console screenshot. “All levels” is there because I have all five set to be shown. If you were to choose the “Default” option then the debug output (listed as “Verbose”) is hidden while the others are shown. Unchecking “Info”, “Warnings”, or “Errors” causes the dropdown to display a different title such as “Custom levels” or “Errors only” depending on what is selected.

The intentions for usage of error and warn are easy to determine; how to use the other choices is up to you. If you do make extensive use of the different options then you might consider documenting the expectations of each as to not confuse things late in the project — especially if it is a team project.

Now, let’s discuss what we can actually log inside these commands. Since they all behave the same, I’ll just focus on logging as the example.

The simplest examples involve just passing a string, number, object, or array into the log command. Technically, any of JavaScript’s data types can be used, but for most of them, the output is much the same.

console.log('string'); console.log(42); console.log({object: 'object'}); console.log(['array', 'array']);
Chrome string, number, object, and array log examples

I’m showing these examples in Chrome with the object and array already expanded. They are normally collapsed but the output next to the arrow is consistent between both states. Firefox displays a little differently but, for the most part, the output is the same. Firefox does tell you whether it is displaying an object or array before expanding, but shows the same as Chrome while expanded.

One interesting thing to add is that you can pass more than one item to the log as parameters and it’ll display them inline.

console.log('string', 'string'); console.log(42, 1138); console.log({object: 'object'}, {object: 'object'}); console.log(['array', 'array'], ['array', 'array']);
Chrome strings, numbers, objects, and arrays examples

Often when I’m working with x and y coordinates, such as what can be outputted by mouse events, it’s useful to log the two together in one statement.

String substitution

The different console logging commands provide string substitution that allows inserting different values into the string for output. This is useful for describing a variable in the log to make it clear as to what’s being reported.

console.log('This is a string: %s', 'string'); console.log('This is a number: %i', 42); console.log('This is an object: %o', {object: 'object'});
Chrome string substitution examples

Here is a list of the data types that can substituted into the output string:

Data type Substitution symbol
Objects and arrays %o or %O
Integers %d or %i
Strings %s
Floats %f

The first parameter would be the string to output with the symbols placed in the appropriate locations. Then each parameter after that is the value to substitute inside the first parameter’s string. Keep in mind that you’ll have to keep the substitution types and the parameters in the specific order or you’ll get unexpected results.

If your console supports template literals, it’s a bit easier to get similar results as string substitutions.

console.log(`This is a string: $ {'string'}`); console.log(`This is a number: $ {42}`); console.log(`This is an object: $ {{object: 'object'}}`);
Chrome template literal examples

Notice that the object is handled a bit better with the string substitution, so pick the appropriate choice for your requirements. Since it’s possible to insert more than one value in the output, let’s compare the two.

console.log('This is a string: %s. This is a number: %i', 'string', 42); console.log(`This is a string: $ {'string'}. This is a number: $ {42}`);
Chrome string substitution and template literals

With the string substitution each value is added as a parameter to be inserted into the output. With template literals, on the other hand, you add them wherever they need to be in the output. Also, you can combine them.

console.log(`This is a number: $ {42}. This is an object: %o`, {object: 'object'});
Chrome string substitution with template literals

So, there are lots of options to pick and choose from so you can go with the best options for your needs. 

Styling the output

Another potentially useful and fun thing is that you can apply CSS styles to the console’s output. It works just like the string substitution method where you insert a %c variable for styles to be applied from the parameters.

Here’s a simple example:

console.log('%cThis is large red text', 'color: red; font-size: 30px;');
Chrome styling in the console

This time there is a slight difference in the Firefox output:

Firefox styling in the console

Not really that much of a difference, but something to keep in mind.

What essentially happens is that %c reads the strings in the parameters to determine what styling to apply. So, say there’s a second styling being passed, %c moves on to the next parameter, much like with string substitution. An empty string in the parameter list resets the styling back to default.

console.log('This is %cred text %cand this is %cgreen text.', 'color: red;', '', 'color: green;');
Using multiple styles in the Chrome console.

The styling properties available are rather limited when compared to typical CSS styling on a webpage. You can look at it as a sort of inline block of text that allow you to manipulate a limited set of styling properties.

With some work and experimenting, you could create interesting messaging within the console. One idea is to draw extra attention to a particular log, especially an error of some sort.

console.log('%cHello there!', `   background: white;   border: 3px solid red;   color: red;   font-size: 50px;   margin: 40px;   padding: 20px; `);
Chrome custom styling

In this example, we can see that the CSS is a bit verbose, but there is something we can do to mimic the class system that we leverage in CSS. The values of each parameter for styling can be stored in variables to allow for repeated use without having to duplicate the string of styles in each parameter.

const clearStyles = ''; const largeText = 'font-size: 20px;'; const yellowText = 'color: yellow;'; const largeRedText = 'font-size: 20px; color: red;'; const largeGreenText = 'font-size: 20px; color: green;'; 
 console.log(`This is %clarge red text. %cThis is %clarge green text. %cThis is %clarge yellow text.`,   largeRedText,   clearStyles,   largeGreenText,   clearStyles,   largeText + yellowText );
Chrome custom template styling

There are several things going on here, so let’s break it down a bit. First, we have a collection of variables that holds our styling strings. Think of each as a sort of class to be reused in the parameters of the console log.

We are also using a template literal in the log, which means we can have line breaks in our output. Then, for each %c in the text, there’s a corresponding variable used in a parameter to define the styles for that particular part of the output text. In addition to each variable that holds styling, there is also a clearStyles argument that can be used to reset styles to prepare for the next set of styling. You could just use an empty string as in previous examples, but I like the clear intention that comes from using the variable name. The last parameter shows that the variables can be combined, which opens up more possible ways of handling the styles.

Now, that’s a great deal of text covering essentially five console commands that only output text to the console. So, let’s move on to other commands of the console object. Although, some of these can still use many of the features described so far, we won’t focus on that aspect as much with the following commands.

Being assertive: assert()

The console.assert() command is similar to the error command mentioned previously. The difference is that asserting allows for the usage of a boolean condition to determine whether it should output the text to the console.

For example, let’s say you wanted to test the value of a variable and make sure it wasn’t larger than a certain number value. If the variable is below that number and the condition resolves to true, the assert command does nothing. If the condition resolves to false, then the output text is displayed. This way you don’t have to wrap a console.error() command with an if statement to determine if the error message is needed in the first place.

let value = 10; console.assert(value <= 7, 'The value is greater than 7.');
Chrome assert example

We can see that assert has the same appearance as the error command, except that it also prepends “Assertion failed:” to the output text. Chrome can also expand this output to show a trace of where the assertion came from.

The trace can be quite helpful with common patterns of functions within functions calling other functions and so on. Although, you can see in the example above that the line the assert came from doesn’t tell you how the code got to that line.

let value = 10; 
 function function_one () {   function_two(); } 
 function function_two () {   function_three(); } 
 function function_three() {   console.assert(value < 7, 'This was false.'); } 
 function_one();
Chrome assert with trace

This sequence is actually in reverse order in terms of the code. The last line shows an anonymous entry (which is an HTML script tag in this case) on line 78. That’s where function_one was called. Inside that function, we have a call for function_two, which, in turn, calls function_three. Inside that last function is where the assert is located. So, in this development world of functions sharing other functions; a description of the path to that point of the assert is quite handy.

Unfortunately, this trace is not provided in Firefox with the assert command, as it is with the error command.

Firefox assert example

Keeping count: count() and countReset()

Ever wonder how many times a certain thing happens in your code? For instance, how many times does a particular function get called during a sequence of events? That’s where the console.count() command can help out.

By itself, the count command is rather simple and has limited use. If you use the command in its default state you only get a simple count. For example, if we call it three times in a row, we get a sequential count.

console.count(); console.count(); console.count();
Chrome default count example

As you can see, we get a simple count from one to three. The default behavior means that count is merely incrementing the output by one each time it runs, no matter where it shows up in the code. You do get the line number in the code where it happened, but the count is a simple total no matter the situation.

To make this command a bit more useful, we can provide a label to keep a separate count for that label.

console.count('label A'); console.count('label B'); console.count('label A'); console.count('label B'); console.count('label A'); console.count('label B');
Chrome label count example

Even though using the count command with labels causes the output to alternate between labels, each one keeps its own count. One scenario where this comes in handy is placing a count inside a function so that every time that function is called, the count is incremented. The label option makes it so that a count can be kept for individual functions to provide for a good idea of how many times each function is being called. That’s great for troubleshooting performance bottlenecks or simply seeing how much work a page is doing.

There’s a way to reset the count. Let’s say we have a loop that gets called multiple times, but the number of iterations of the loop can be dynamic. This is done with the console.countReset() command with the same label from the count command.

console.count(); console.count(); console.countReset(); console.count(); 
 console.count('this is a label'); console.count('this is a label'); console.countReset('this is a label'); console.count('this is a label');
Chrome count reset example

Each count — with and without a label — is called twice and console.countReset() is applied right before another count instance. You can see that Chrome counts up to two, then restarts when it encounters countReset. There’s nothing in DevTools to indicate the reset happened, so an assumption is made that it did happen because the count started over.

And yet, the same code is a bit different in Firefox.

Firefox count reset example

Here, the reset is indicated by the count being set all the way back to zero. That is the indicator that the reset was called, whereas we have no such indication in Chrome.

As for label options, just about anything can be used. I suppose a simple way to describe it is that if you give it anything that can be resolved to a string, it’ll probably work as a label. You could even use a variable that has values that change over time, where count will use the current value of the variable as a label each time it is encountered. So, you could keep count of the values as they change over time.

Describe that thing: dir() and dirxml()

The main idea behind these two commands is to display either properties of a Javascript object with console.dir() or descendant elements of an XML/HTML element with console.dirxml(). It appears Chrome has these implemented as expected, while Firefox just uses both as aliases for console.log().

Let’s give console.log(), console.dir(), and console.dirxml() the same simple object to see what we get. Keep in mind that you normally would not log an object with console.dirxml().

const count = {   one: 'one',   two: 'two',   three: 'three' }; 
 console.log(count); console.dir(count); console.dirxml(count);
Chrome simple dir() and dirxml() example

Firefox gives us much the same, except the console.dir() is automatically expanded.

Firefox simple dir() and dirxml() example

Another simple comparison to console.log() is to repeat the object in the same command.

Chrome dir() and dirxml() double example
Firefox dir() and dirxml() double example

Not really that much different other than that Chrome doesn’t show the second object in console.dir() like Firefox does. Which makes sense because Chrome is trying to display properties of an object (ignoring the second) while Firefox is just aliasing everything to a console.log(). So, for situations like this with objects there is little difference between console.log(), console.dir(), and console.dirxml() in the browsers.

A useful benefit of console.dir() in Chrome that I can point out is how DOM elements are handled. For example, here’s how console.log() displays in Chrome and Firefox when given a DOM element.

Chrome console.log() DOM example.
Firefox console.log() DOM example

Now, I’ve always liked how Firefox outputs a DOM element inside a console.log(), as it gives you all the properties of that DOM element. So, when I wanted to look up a specific property of a DOM element to manipulate with JavaScript, it’s only a console.log() away to find it. Chrome, on the other hand, gives us the HTML code of the DOM element in the console.log() just like it would in console.dirxml().

To get the properties in Chrome, use console.dir() with the DOM element. I was quite happy to find that console.dir() in Chrome provides the properties of a DOM element just as I came to rely on that information in Firefox.

As for console.dirxml() in Chrome, it can be useful for displaying an HTML element and its children outside of the clutter of the DOM Inspector. You can even edit some of the existing HTML live in the console, but you won’t have the same level of abilities as in the DOM Inspector.

Let’s get together: group(), groupCollapsed(), and groupEnd()

Here’s a simple one: Group different console outputs together to show a form of relationship among them. It is somewhat limited in features so its usefulness will depend a great deal on how you plan to use it. This is the console.group() command.

console.group(); console.log('one'); console.log('two'); console.log('three'); console.groupEnd(); 
 console.group('this is a label'); console.log('one'); console.log('two'); console.log('three'); console.groupEnd();
Chrome group() example

In the first block of code we call console.group() in its default state, have three logs, and then finally call console.groupEnd(). The console.groupEnd() simply defines the end of the grouping. The second block has a string as a parameter that essentially becomes the label for that group. Notice that in the first block without a label it just identifies itself as a console.group in Chrome while in Firefox it shows as <no group label>. In most cases, you’ll want a proper label to distinguish between groups.

Also notice the arrow next to the labels. Clicking on that collapses the group. In the code examples above, if we change console.group() to console.groupCollapsed(), they start collapsed and must be opened to see the output.

You can also nest the groups. The console.groupEnd() command simply refers to the last opened group.

console.group('outer group'); console.log('outer one'); console.log('outer two'); console.group('inner group'); console.log('inner one'); console.log('inner two'); console.log('inner three'); console.groupEnd(); console.log('outer three'); console.groupEnd();
Chrome nested group() example

Just as a quick note, if you want the group label to stand out a bit more in a list of output in the console, you can style it just as we did with strings earlier.

console.group('%cstyled group', 'font-size: 20px; color: red;'); console.log('one'); console.log('two'); console.log('three'); console.groupEnd();
Chrome styled group() example

Have a seat at the: table()

In previous examples, we’ve seen what happens when we put an array or object inside a console.log() or console.dir(). There’s another option for these data types for a more structured display, which is console.table().

Here’s a simple example with an array:

let basicArray = [   'one',   'two',   'three' ]; console.table(basicArray);
Chrome basic array table() example

Here’s the same example in Firefox for comparison.

Firefox basic array table() example

A slight visual difference, but pretty much the same. That said, Chrome does still give you the expandable output under the table, much like you’d see in console.log(). Chrome will also provide basic column sorting if you click on the heading.

The output is similar when passing in an object:

let basicObject = {   one: 'one',   two: 'two',   three: 'three' }; console.table(basicObject);
Chrome basic object table() example

So, that was a pretty simple example with basic outputs. How about something a little more complex and is often used in coding projects? Let’s look at an array of objects.

let arrayOfObjects = [   {     one: 'one',     two: 'two',     three: 'three'   },   {     one: 'one',     two: 'two',     three: 'three'   },   {     one: 'one',     two: 'two',     three: 'three'   } ]; console.table(arrayOfObjects);
Chrome array of objects table() example

As you can see, this gets us a nice layout of objects with repeating keys as column labels. Imagine data along the lines of user information, dates, or whatever might be data often used in loops. Keep in mind that all the keys in each of the objects will be represented as a column, whether there is corresponding keys with data in the other objects. If an object doesn’t have data for a key’s column, it appears as empty.

An array of arrays is similar to the array of objects. Instead of keys being labels for the columns, it uses the index of the inner arrays as column labels. So if an array has more items than the other arrays, then there will be blank items in the table for those columns. Just like with the array of objects.

So far, simple arrays and objects have simple output displayed. Even a slightly more complex array of objects still has a solid, useful structure. Things can get a bit different with mixing the data types though.

For example, an array of arrays where one of the inner array items is an object.

let arrayOfArraysWithObject = [   ['one', 'two', {three: 'three', four: 'four'}],   ['one', 'two', {three: 'three', four: 'four'}],   ['one', 'two', {three: 'three', four: 'four'}] ];  console.table(arrayOfArraysWithObject);
Chrome array of arrays with object table() example

Now, to see what is contained in those objects in the third column, we’ll have to expand that array output below the table. Not that bad, really. Here’s how Firefox handles the same output.

Firefox array of array with object table() example

Firefox just lets us expand the object within the table.

How about mixing the data types the other way, where we have an object with arrays as values for each key? It works much the same as the array of arrays. The difference is that each row is labeled with a key instead of the index. Of course, for each level of data type you add to the mix will result in a more complex looking table. 

This is all about: time(), timeLog(), and timeEnd()

Here we have a simple way to log how long something takes to complete. We call console.time() with a label, call console.timeLog() with the same label for an update, and call console.timeEnd() again with the same label to stop the timer.

console.time('this is a timer'); console.timeLog('this is a timer'); console.timeEnd('this is a timer');

The output for Chrome and Firefox is much the same. Here’s an example output with code that logs the time every second for five seconds and then stops.

Chrome time() example
Firefox time() example

Notice that the reported times are not quite the same, but probably close enough for most requirements. Also, Firefox is nice enough to note that the timer has ended while Chrome requires an assumption once the label stops appearing. The first four lines of output come from the call console.timeLog('this is a timer'); and the last line is from the call to console.timeEnd('this is a timer');.

Dropping breadcrumbs with: trace()

The console.trace() command is actually similar to console.error() and console.warn(). Calling this command will output a stack trace to the console showing the path through the code to that call. We can even pass it a string as a form of label, but other data types such as arrays or objects can be passed. The behavior of passing data like that is the same as what we would get from a console.log() call. It’s a simple way to pass along some information to the console without triggering a more dire looking console.error() or console.warn() call.

debugger

This is a simple command to trigger a pause in the console’s debugger, if it exists. It is similar to placing a breakpoint in the debugger, or the browser’s equivalent, to cause the same type of pause while executing code. Here’s a simple example:

function whatsInHere() {   debugger;   // rest of the code }

In this particular example, the open console’s debugger will pause code execution and the browser will open up the source file to show the line of code as soon as the function is called. It could be useful for easy breakpoints with some complicated projects.

Technically, the debugger command isn’t a part of the console object in the browser. It’s a useful feature that the console will respond to from JavaScript code.

Some additional console utilities

That’s a good look at most of the standard commands available to us in the console object. Each of these will work more-or-less the same across modern browsers. There may be some differences between browsers, as we saw in some of the examples. But there are a few more things I’d like to take a moment to point out, as they might prove useful in various ways.

The following examples can be considered more like console “utilities.” They are not a part of the console object like most of the previous examples. Therefore they are not called with a leading console object reference. These utilities are supported directly by the browsers themselves. They cannot be called from JavaScript code but must be typed directly in the console to be used. In some cases the utility might be unique to a particular browser, in others the utility is supported much the same way in several browsers. Your mileage may vary based on your browser of choice.

$ 0, $ 1, $ 2, $ 3, $ 4

These five commands are extremely handy. The first one, $ 0, represents the currently selected element in the DOM inspector. This essentially provides a shortcut instead of having to use more traditional DOM methods, such as getElementById or a querySelector. You can use it in various ways, within various console commands, or by itself to get information about the currently selected element. For example:

console.log($ 0);

The other commands in this set represent elements that were previously selected. Think of them as a form of selection history. $ 1 is the previous element, $ 2 is the previous before that, and so on. Although the first command is available in Firefox, the commands for previously selected elements are not.

$ (‘element’), $ $ (‘elements’)

If you find yourself typing out document.querySelector('element') in the console repeatedly, there’s a shortcut. You can just type $ ('element') and it performs the same function. The shortcut might remind many of jQuery, but to select multiple elements reminds me of MooTools. To select multiple elements, you’d use $ $ ('elements') instead of document.querySelectorAll('elements').

$ x(‘//element’)

This is a shortcut for XPath that will return an array of elements that match the expression. An easy example is $ x('//div'), which will present an array of every div element on the page. This isn’t that much different than using $ $ ('div') like we did with $ ('element'), but there are many options for writing XPath expressions.

One example of a simple step up in a XPath expression is $ x('//div[span]'), which would return the div elements on the page that happen to contain a span element. This is the equivalent of :has in CSS Selectors Level 4 draft, which isn’t supported in browsers yet.

These are just basic examples that only scratch the surface of XPath.

clear()

This is another version of console.clear(), but without the “Console was cleared” message.

getEventListeners(object)

This command, when given a DOM element, will report the event listeners registered to that element. For example, using the $ 0 example from above we can use getEventListeners($ 0) to get something like this:

Chrome getEventListeners() example

Expanding each item in the array provides various information about that event listener. This function isn’t supported in Firefox, but it does offer something similar that can be found in the DOM inspector.

Firefox DOM Inspector events information.

Clicking on the “event” badge next to the element provides a list of events registered to the element. Then each event can be expanded to show the code involved with the event.

That’s it for now!

 I’ll end it here, with a large amount of information detailing various commands that can be used in the browser’s console output or with JavaScript. This isn’t everything that is possible — there’s simply too much to cover. In some cases, each browser has its own capabilities or utilities that can be leveraged. We looked at the bulk of what we might find in Chrome and Firefox, but there’s likely more out there. Plus, there will always be new features introduced in the future. I invite you to dig deeper to discover more ways to leverage browser DevTools for your coding projects.

The post A Guide to Console Commands appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

A Complete Guide to Data Attributes

Introduction

HTML elements can have attributes on them that are used for anything from accessibility information to stylistic control.

<!-- We can use the `class` for styling in CSS, and we've also make this into a landmark region --> <div class="names" role="region" aria-label="Names"></div>

What is discouraged is making up your own attributes, or repurposing existing attributes for unrelated functionality.

<!-- `highlight` is not an HTML attribute --> <div highlight="true"></div>  <!-- `large` is not a valid value of `width` --> <div width="large">

There are a variety of reasons this is bad. Your HTML becomes invalid, which may not have any actual negative consequences, but robs you of that warm fuzzy valid HTML feeling. The most compelling reason is that HTML is a living language and just because attributes and values that don’t do anything today doesn’t mean they never will.

Good news though: you can make up your own attributes. You just need to prefix them with data-* and then you’re free to do what you please!

Syntax

It can be awfully handy to be able to make up your own HTML attributes and put your own information inside them. Fortunately, you can! That’s exactly what data attributes are. They are like this:

<!-- They don't need a value --> <div data-foo></div>  <!-- ...but they can have a value --> <div data-size="large"></div>  <!-- You're in HTML here, so careful to escape code if you need to do something like put more HTML inside --> <li data-prefix="Careful with HTML in here."><li>  <!-- You can keep dashing if you like --> <aside data-some-long-attribute-name><aside>

Data attributes are often referred to as data-* attributes, as they are always formatted like that. The word data, then a dash -, then other text you can make up.

Can you use the data attribute alone?

<div data=""></div>

It’s probably not going to hurt anything, but you won’t get the JavaScript API we’ll cover later in this guide. You’re essentially making up an attribute for yourself, which as I mentioned in the intro, is discouraged.

What not to do with data attributes

Store content that should be accessible. If the content should be seen or read on a page, don’t only put them in data attributes, but make sure that content is in the HTML content somewhere.

<!-- This isn't accessible content --> <div data-name="Chris Coyier"></div>  <!-- If you need programmatic access to it but shouldn't be seen, there are other ways... --> <div>   <span class="visually-hidden">Chris Coyier</span> </div>

Here’s more about hiding things.

Styling with data attributes

CSS can select HTML elements based on attributes and their values.

/* Select any element with this data attribute and value */ [data-size="large"] {   padding: 2rem;   font-size: 125%; }  /* You can scope it to an element or class or anything else */ button[data-type="download"] { } .card[data-pad="extra"] { }

This can be compelling. The predominant styling hooks in HTML/CSS are classes, and while classes are great (they have medium specificity and nice JavaScript methods via classList) an element either has it or it doesn’t (essentially on or off). With data-* attributes, you get that on/off ability plus the ability to select based on the value it has at the same specificity level.

/* Selects if the attribute is present at all */ [data-size] { }  /* Selects if the attribute has a particular value */ [data-state="open"], [aria-expanded="true"] { }  /* "Starts with" selector, meaning this would match "3" or anything starting with 3, like "3.14" */ [data-version^="3"] { }  /* "Contains" meaning if the value has the string anywhere inside it */ [data-company*="google"] { }

The specificity of attribute selectors

It’s the exact same as a class. We often think of specificity as a four-part value:

inline style, IDs, classes/attributes, tags

So a single attribute selector alone is 0, 0, 1, 0. A selector like this:

div.card[data-foo="bar"] { }

…would be 0, 0, 2, 1. The 2 is because there is one class (.card) and one attribute ([data-foo="bar"]), and the 1 is because there is one tag (div).

Attribute selectors have less specificity than an ID, more than an element/tag, and the same as a class.

Case-insensitive attribute values

In case you’re needing to correct for possible capitalization inconsistencies in your data attributes, the attribute selector has a case-insensitive variant for that.

/* Will match <div data-state="open"></div> <div data-state="Open"></div> <div data-state="OPEN"></div> <div data-state="oPeN"></div> */ [data-state="open" i] { }

It’s the little i within the bracketed selector.

Using data attributes visually

CSS allows you to yank out the data attribute value and display it if you need to.

/* <div data-emoji="✅"> */  [data-emoji]::before {   content: attr(data-emoji); /* Returns '✅' */   margin-right: 5px; }

Example styling use-case

You could use data attributes to specify how many columns you want a grid container to have.

<div data-columns="2"></div> <div data-columns="3"></div> <div data-columns="4"></div>

Accessing data attributes in JavaScript

Like any other attribute, you can access the value with the generic method getAttribute.

let value = el.getAttribute("data-state");  // You can set the value as well. // Returns data-state="collapsed" el.setAttribute("data-state", "collapsed");

But data attributes have their own special API as well. Say you have an element with multiple data attributes (which is totally fine):

<span    data-info="123"    data-index="2"    data-prefix="Dr. "   data-emoji-icon="🏌️‍♀️" ></span>

If you have a reference to that element, you can set and get the attributes like:

// Get span.dataset.info; // 123 span.dataset.index; // 2  // Set span.dataset.prefix = "Mr. "; span.dataset.emojiIcon = "🎪";

Note the camelCase usage on the last line there. It automatically converts kebab-style attributes in HTML, like data-this-little-piggy, to camelCase style in JavaScript, like dataThisLittlePiggy.

This API is arguably not quite as nice as classList with the clear add, remove, toggle, and replace methods, but it’s better than nothing.

You have access to inline datasets as well:

<img src="spaceship.png"   data-ship-id="324" data-shields="72%"   onclick="pewpew(this.dataset.shipId)"> </img>

JSON data inside data attributes

<ul>   <li data-person='     {       "name": "Chris Coyier",       "job": "Web Person"     }   '></li> </ul>

Hey, why not? It’s just a string and it’s possible to format it as valid JSON (mind the quotes and such). You can yank that data and parse it as needed.

const el = document.querySelector("li");  let json = el.dataset.person; let data = JSON.parse(json);  console.log(data.name); // Chris Coyier console.log(data.job); // Web Person

JavaScript use-cases

The concept is that you can use data attributes to put information in HTML that JavaScript may need access to do certain things.

A common one would have to do with database functionality. Say you have a “Like” button:

<button data-id="435432343">♡</button>

That button could have a click handler on it which performs an Ajax request to the server to increment the number of likes in a database on click. It knows which record to update because it gets it from the data attribute.

Specifications

Browser support

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
7 6 11 12 5.1

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
79 68 3 5.0-5.1

The post A Complete Guide to Data Attributes appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]