The “Other” C in CSS

Category Image 052

I think it’s worth listening to anything Sara Soueidan has to say. That’s especially true if she’s speaking at an event for the first time in four years, which was the case when she took the stage at CSS Day 2024 in Amsterdam. What I enjoy most about Sara is how she not only explains the why behind everything she presents but offers it in a way that makes me go “a-ha!” instead of “oh crap, I’m doing everything wrong.”

(Oh, and you should take her course on Practical Accessibility.)

Sara’s presentation, “The Other ‘C’ in CSS”, was published on YouTube just last week. It’s roughly 55 minutes of must-see points on the various ways CSS can, and does, impact accessibility. I began watching the presentation casually but quickly fired up a place where I could take thorough notes once I found myself ooo-ing and ahhh-ing along.

So, these are the things I took away from Sara’s presentation. Let me know if you’ve also taken notes so we can compare! Here we go, there’s a lot to take in.

Here’s the video

Yes, CSS affects accessibility

CSS changes more than the visual appearance of elements, whether we like it or not. More than that, its effects cascade down to HTML and the accessibility tree (accTree). And when we’re talking about the accTree, we’re referring to a list of objects that describes and defines accessible information about elements.

There are typically four main bits of info about an accTree object:

  • Role: what kind of thing is this? Most HTML elements map to ARIA roles, but not all of them.
  • Name: identifies the element in the user interface.
  • Description: how do we further describe the thing?
  • State: what is its current state? Announce it!

The browser provides interactive features — like checking a checkbox that updates and exposes the element’s information — so the user knows what happens following an interaction.

Accessibility tree objects may also contain properties and relationships, such as whether it is part of a group or labeled by another element.

Example: List semantics

CSS can affect an object’s accessible role, name, description, or even whether it is exposed in the accTree at all. As such, it can directly impact the screen reader announcement. We shared a while back how removing list-style affects list semantics, particularly in the case of Safari, and Sara explains its nuances.

/* Removes list role semantics in Safari */
/* Need to add aria-role=list */
ul {
  list-style: none;
}

/* Does not remove role semantics in Safari */
nav ul {
  list-style: none:
}

/* Removed unless specifically re-added in the markup */
ul:where([role="list"]) {
  list-style: none;
}

/* Preserves list semantics */
ul {
  list-style: "";
}

display: contents

CSS can completely remove the presence of an element from the accessibility tree. I took a screenshot from one of Sara’s slides but it’s just so darn helpful that I figured putting the info in a table would be more useful:

Exposed to a11y APIs?Keyboard accessible?Visually accessible (rendered)?Children exposed to a11y APIs?
display: none
visibility: hidden
opactity: 0 and filter: opacity(0)
clip-path: inset(100%)
position(off-canvas)
.visually-hidden
display: contents

The display: contents method does more than it’s supposed to. In short, we know that display controls the type of box an element generates. A value of none, for example, generates no box.

The contents value is sort of like none in that not box is generated. The difference is that it has no impact on the element’s children. In other words, declaring contents does not remove the element or its child elements from the accTree. More than that, there’s a current bug report saying that declaring contents in Firefox breaks the anchoring effect of an ID attribute attached to an element.

Eric Bailey says that using display: contents is considered harmful. If using it, the recommendation is to set it on a generic <div> instead of a semantically meaningful element. If we were to use it on a meaningful interactive element, it would be removed from the accTree, and its children would be bumped up to the next level in the DOM.

Visually hiding stuff

Many, many of us use some sort of .visibility-hidden class as a utility for hiding elements while allowing screenreaders to pick them up and announce the contents. TPGi has a great breakdown of the technique.

.visually-hidden:not(:focus):not(:active) {
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0); /* for IE only */
  clip-path: inset(50%);
  position: absolute;
  white-space: nowrap;
}

This is super close to what I personally use in my work, but the two :not() statements were new to me and threw me for a loop. What they do is make sure that the selector only applies when the element is neither focused nor activated.

It’s easy to slap this class on things we want to hide and call it a day. But we have to be careful and use it intentionally when the situation allows for us to hide but still announce an element. For example, we would not want to use this on interactive elements because those should be displayed at all times. If you’re interacting with something, we have to be able to see it. But for generic text stuff, all good. Skip to content links, too.

There’s an exception! We may want an animated checkbox and have to hide the native control’s appearance so that it remains hidden, even though CSS is styling it in a way that it is visible. We still have to account for the form control’s different states and how it is announced to assistive tech. For example, if we hide the native checkbox for a custom one by positioning it way off the screen, the assistive tech will not announce it on focus or activation. Better to absolutely position the checkbox over the custom one to get the interactive accessibility benefits.

Bottom line: Ask yourself whether an interactive element will become visible when it receives focus when deciding whether or not to use a .visually-hidden utility.

CSS and accessible names

The browser follows a specific process when it determines an element’s accessible name (accName):

  • First, it checks for aria-labelledby. If present, and if the ID in the attribute is a valid reference to an element on the page, it uses the reference’s element’s computed text as the element’s accessible name.
  • Otherwise, it checks for aria-label.
  • Otherwise, unless the element is marked with role="presentation" or role="none" (i.e., the element does not accept an accName anymore), the browser checks if the element can get its own name, which could happen in a few ways, including:
    • from an HTML elemnenty, such as alt or title (which is best on an <iframe>; otherwise, avoid),
    • from another element, like <label> or <legend>, or
    • from its contents.

At this point, Sara went into a brief (but wonderful) tangent on <button> semantics. Buttons are labelable elements and can get their accName by using an aria-label attribute, an aria-labelledby attribute, its contents, or even a <label> element.

ARIA takes precedence over HTML which is why we want to avoid it only where we have to. We can see the priorities and overrides for accessible names in DevTools under the Accessibility tab when inspecting elements.

DevTools exposing the accessibility tree of the document and aria attributes for a selected anchor element.

But note: the order of priority defined in the accName computation algorithm does not define the order of priority that you should follow when providing an accName to elements. The steps should like be reversed if anything. Prioritize native HTML!

CSS generated content

Avoid using CSS to create meaningful content. Here’s why:

<a href="#" class="info">CSS generated content</a>
.info::before {
  content: "ⓘ" / "Info: ";
  /* or */
  content: url('path-to-icon.svg') / "Info: ";
}

/* Contents: : Info: CSS generated content. */

But it’s more nuanced than that. For one, we’re unable to translate content generated by CSS into different languages, at least via automated tools. Another one: that content is gone if CSS is unavailable for whatever reason. I didn’t think this would ever be too big a concern until Sara reminded me that some contexts completely strip out CSS, like Safari’s Reader Mode (something I rely on practically every day, but wish I didn’t have to).

There are also edge cases where CSS generated content might be inaccessible, including in Forced Colors environments (read: color conflicts), or if a broken image is passed to the url() function (read: alt text of the image is not shown in place of the broken image, at least in most browsers, yet it still contributes to the accName, violating SC 2.5.3 Label in Name). Adrian Roselli’s article on the topic includes comprehensive test results of the new feature, showing different results.

Inline SVG is probably better! But we can also do this to help with icons that are meant to be decorative to not repeat redundant information. But it is inconsistent as far as browser implementation (but Sara says Safari gets it right).

/* like: <img src="icon.svg" alt=""> */
.icon {
  content: url('path/to/icon.svg') / "";
}

So, what can we do to help prevent awkward and inaccessible situations that use CSS generated content?

  • Avoid using CSS pseudo-elements for meaningful content — use HTML!
  • Hide decorative and redundant CSS content by giving it an empty alt text (when support is there and behavior is consistent).

CSS can completely strip an element of its accName…

…if the source of the name is hidden in a way that removes it from the accessibility tree.

For example, an <input> can get its accName from a <label>, but that label is hidden by CSS in a way that doesn’t expose it to a11y APIs. In other words, the <label> is no longer rendered and neither are its contents, so the input winds up with no accName.

Showing the HTML for a label-input pair and CSS that uses display: none to hide the label.

BUT! Per spec:

By default assistive technologies do not relay hidden information, but an author can explicitly override that and include hidden text as part of the accessible name or accessible description by using aria-labelledby or aria-describedby.

So, in this case, we can reuse the label even if it is hidden by tacking on aria-labelledby. We could use the .visually-hidden utility, but the label is still accessible and will continue to be announced.

Using aria-labelled by on an text form input with DevTools showing the input's accessible name which is pulled from the label element.

CSS does not affect the state of an element in the accTree

If we use a <button> to show/hide another element, for example, the <button> element state needs to expose that state. Content on hover or focus violates SC 1.4.13 which requires a way to dismiss the content. And users must be able to move their cursor away from the text and have it persist.

CSS-only modals using the checkbox hack are terrible because they don’t trap focus, don’t make the page content inert, and don’t manage keyboard focus (without JavaScript).

Popovers created with the Popover API are always non-modal. If you want to create a modal popover, a <dialog> is the right way to go. I’m enamored with Jhey Tompkins’s demo using the popover for a flyout navigation component, so much so that I used it in another article. But, using popover for modal-type stuff — including for something like a flyout nav — we still need to update the accessible states.

There’s much more to consider, from focus traps to inert content. But we can also consider removing the popover’s ::backdrop for fewer restrictions, like making background content inert or trapping focus. Then again, something like a popover-based flyout navigation violates SC 2.4.12 Focus Not Obscured if it covers or obscures another element with focus. So, yes, visibility is important for usability but we should shoot for better usability that goes beyond WCAG conformance. (Sara elaborates on this in a comment down below.)

So… close the popover when focus leaves it. Sara mentioned an article that Amit Sheen wrote for Smashing Magazine where it’d be wise to pay close attention to how a change is communicated to the user when a <select> menu <option> is selected to update colors on the page. That poses issues about SC 3.2.2 where something changes on input. When the user interacts with it, the user should know what’s going to happen.

Final thoughts

Yeah, let all that sink in. It feels good, right? Again, what I love most about Sara’s presentation (or any of them, for that matter) is that she isn’t pointing any condemning fingers at anyone. I care about oodles accessible experiences but know just how much I don’t know, and it’s practical stuff like this where I see clear connections to my work that can make me better.

I took one more note from Sara’s talk and didn’t quite know where to put it, but I think the conclusion makes sense because it’s a solid reminder that HTML, CSS, and, yes JavaScript, all have seats at the table and can each contribute positively to accessible experience:

  • Hacking around JavaScript with CSS can introduce accessible barriers. JavasScript is still useful and required for these things. Use the right tool for the job.

The “Other” C in CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Creating a Cohesive User Experience Using HSL Colors in CSS

Category Image 052

We all know what importance colors hold in anything, whether it’s a website layout, image, video, or any other graphical element. In essence, color is a subjective experience that results from the interaction between light, the eye, and the brain. Adding colors to the website gives a new life to the whole layout and graphical elements. Nobody likes to visit web pages with white, black, and gray colors on them. Colors make the elements look more realistic and catchy to the human eye.

Not just theoretically, psychology also comes into play when we use colors on websites. It has been scientifically proven that a specific set of colors triggers particular emotions in the human brain, such as autumn colors like orange and yellow representing joy or happiness, red color to festive seasons, and blue viewed as calm and trustworthy. Besides, you must have noticed that many food companies often use red and yellow on their websites, pharmaceutical companies tend to use green on their sites, fitness companies sometimes use orange, and so on.

Solving Media Object Float Issues With CSS Block Formatting Contexts

Category Image 052

Let’s imagine we’re making a small component. It can be anything, really, but let’s use a media object as an example. Nicole Sullivan had a solid definition of media objects from way back in 2010, and you probably already know the pattern well: some form of media (often an image) on the left and text beside it on the right. The media could be an image or a video, for example.

This is the basic HTML for the layout, minimized for brevity:

<section class="container">
  <article class="float-left">
    <img src="https://picsum.photos/100">
      <p>I've never had to cook or clean since I discovered Xyz. They perform all my tasks for me. I recommend them.</p>
      <h3>Dan Somore</h3>
  </article>

  <!-- more articles -->

</section>

This HTML gives us a <section> element that is the container for four <article> elements, where each one is a testimonial container that holds an <img> and a <div> with a block of text — our media objects.

Let’s apply some light styling in CSS:

/* Give the parent container breathing room */
.container {
  padding: 20px;
}

/* 
  Styles for each testimonial container 
  Each container is floated left
*/
.float-left {
  border: 2px solid blue;
  background-color: transparent;
  float: left;
  width: 45%;
  min-height: 150px;
  margin-bottom: 20px;
  margin-right: 20px;
}

/* Testimonial images are floated left */
img {
  float: left;
  margin-right: 10px;
}

This code is by no means perfect. In fact, it introduces the wrapping and overflow issues we’re about to discuss. We will look at these issues together before getting into solutions.

Issue 1: Height Collapsing

When an element is floated in its container, it exits its normal document flow and into a floated position, making no contributions to the container’s height. In a container of many floated media objects, the container element’s height is collapsed to contain only non-floated elements. The collapsed height might be inconspicuous in containers without a border or non-floated elements and could disrupt the layout of other elements after a media object container. However, this issue can be easily discovered if there is a non-floated element in the container, among other floated elements.

Let’s add a border to the parent container to see the height-collapsing effect.

The height of the content is what influences the height of the testimonial container. If the image were in the container’s flow, it would be taller than the text, and the container would adjust to it. But, alas, that’s not the case since we introduced a block formatting context when floating the image.

The popular solution with a single line of CSS on the testimonial’s parent container:


.container {
  overflow: auto;
}

The BFC this generates establishes a new document flow within the page’s root element, containing all the container's child elements, including floated media objects. It effectively prevents the testimonial elements from being displaced beyond the parent container’s borders — no extra divs or pseudo-elements are needed like the clearfix approach.

See the Pen Float Solutions: overflow: auto [forked] by Geoff Graham.

That certainly gets the job done! But I want to show you one more way to do this because I believe it’s the best of the bunch.

The Best Solution: display: flow-root

display: flow-root was introduced to address inconsistencies associated with using overflow for generating BFCs. In fact, display: flow-root was explicitly designed to produce BFC, while the overflow property is designed to manage content that surpasses its container. Consequently, overflow can induce unintended side effects, from unwanted scrollbars to data loss.

That’s why I recommend using display: flow-root. It is meant to create a BFC when you need it, whereas the other solutions are more like workarounds.

Conclusion

CSS block formatting contexts are great because they allow you to leave the main document flow, allowing elements to interact differently in a layout. But, of course, those different interactions can feel like buggy behavior if you’re unaware that you’re actually working in a different formatting context.

This is exactly why we have modern layout techniques like Flexbox and Grid. Before we had them, floats were a nice trick for faking columns. But the BFC they created wasn’t so nice. Hence clever workarounds like the clearfix to create a BFC to wrangle the other BFC.

Perhaps the bigger takeaway from all this, though, is to evaluate your layout strategy. If you’re reaching for a float, is it really the best option for what you’re trying to do? Because if so, you may as well embrace the natural text-wrapping behavior rather than trying to fight it. And if you don’t want to fight it, that’s a sure sign you ought to reach for a more modern layout technique, like Flexbox or Grid.

Resources

Further Reading On SmashingMag

Ripple Button Effect Using Pure CSS

Featured Imgs 03

Google’s Material Design guidelines introduced the ripple effect, a subtle animation that indicates user action. The ripple effect rapidly gained popularity in web design as a sophisticated visual feedback form that refines user interaction, particularly on buttons. Today, we’ll show you how to create a ripple button effect using nothing but pure CSS.

Your Web Designer Toolbox
Unlimited Downloads: 500,000+ Web Templates, Icon Sets, Themes & Design Assets


Building the Button

The basic structure of our button is quite simple. It’s a single line of HTML:

<button class="btn-ripple">CLICK ME</button>

This is a standard button element with a class btn-ripple attached to it, which will be our reference when we define the ripple effect in CSS.

Casting Ripples With CSS

/* Styling for the ripple button */
.btn-ripple {
  border: none; /* Removing the default button border */
  border-radius: 6px; /* Giving our button rounded corners */
  padding: 12px 16px; /* Providing some padding around the button text */
  font-size: 1.2em; /* Increasing the font size of the button text */
  cursor: pointer; /* Changing the cursor to a hand icon when hovering over the button */
  color: white; /* Making the button text color white */
  background-color: #fa6e83; /* Setting the initial button background color */
  outline: none; /* Removing the outline from the button */
  background-position: center; /* Setting the position of the background image to center */
  transition: background 1s; /* Adding a transition to the background color */
}

/* Defining the hover state */
.btn-ripple:hover {
  background: #f94b71 radial-gradient(circle, transparent 1%, #f94b71 1%)
    center/15000%; /* Creating a radial gradient background on hover */
}

/* Defining the active (clicked) state */
.btn-ripple:active {
  background-color: #f97c85; /* Changing the button color when active */
  background-size: 100%; /* Increasing the size of the background image */
  transition: background 0s; /* Removing the transition from the background color */
}

Let’s break down the CSS setup:

  • The .btn-ripple class sets up the basic appearance of the button. The background-color is initially set to #FA6E83, a light color, and the background-position is centered to ensure our ripple effect starts from the middle of the button.
  • When you hover over the button, the :hover pseudo-class is activated. It changes the background to a radial gradient that’s centered where the pointer is located, simulating the ripple effect. The gradient starts as transparent (transparent 1%) and transitions to the button color (#F94B71 1%), creating a soft ripple effect.
  • Upon clicking the button, the :active pseudo-class takes effect. It changes the background-color to a darker shade (#F97C85) and expands the background-size to 100%, reinforcing the ripple effect. The transition for the background is also set to 0s, making the effect appear instantaneously when the button is clicked.

The Result

See the Pen Pure CSS Ripple Button Effect by 1stWebDesigner (@firstwebdesigner) on CodePen.

Final Thoughts

We demonstrated a classic example of how simple CSS can be used to create appealing interactivity in a user interface. But as you strive to refine your UI, it’s critical to remember that each interface element might require different tweaks.

Consider the context in which your buttons are used. A button for submitting form data might benefit from a more subdued ripple effect, while a call-to-action button could be more prominent with a stronger one.

For more intricate animations or synchronizing with other UI events, JavaScript could be leveraged for more granular control. CSS provides a solid base for styling and basic animations, but JavaScript opens up more advanced possibilities.

And of course, customization is key. While we used specific colors for our ripple button here, feel free to experiment with colors, shapes, and transitions that align with your brand and design aesthetic.

How to Animate Gradient Text Using CSS

Featured Imgs 23

Web design takes a captivating turn when CSS comes into play. It enables a world of transformations, such as taking static text elements and infusing them with life. Our focus today is one such engaging transformation – animate gradient text using CSS.

So, let’s demonstrate how a seemingly complex effect can be achieved with a few lines of code.

UNLIMITED DOWNLOADS: 400,000+ Fonts & Design Assets

Starting at only $16.50 per month!

Setting Up the Text in the HTML

We begin by defining our text element in HTML, which in this case is a simple heading:

<h1 class="animated-gradient">1stWebDesigner</h1>

Here, we create an <h1> element with a class called “animated-gradient”. This class becomes our anchor for creating the gradient animation in CSS.

Unfolding the Gradient Animation

The core part lies within our CSS. Let’s define the gradient and set it in motion with the following code:

/* Google Fonts for Open Sans */
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@700&amp;display=swap');

/* Define animation */
@keyframes gradient-shift {
  0% {background-position: 0%}
  100% {background-position: 100%}
}

/* Styling our animated gradient text */
.animated-gradient {
  font-family: 'Open Sans', sans-serif;
  font-size: 2em;
  background: linear-gradient(270deg, #ff4b59, #ff9057, #ffc764, #50e3c2, #4a90e2, #b8e986, #ff4b59);
  background-size: 200%;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: gradient-shift 3s ease-in-out infinite;
}

Our CSS setup does the following:

  • @import url: This directive fetches the Open Sans font from Google Fonts, noted for its modern and clean aesthetics.
  • @keyframes: Here, we define an animation named gradient-shift. This animation creates the illusion of motion in the gradient by shifting the background’s position from 0% to 100%.
  • font-family and font-size: These properties set our text’s font to Open Sans and its size to 2em.
  • background: This property generates a linear gradient using a striking array of colors. The gradient direction is set to 270 degrees, providing a left-to-right color flow.
  • background-size: This property, set to 200%, enlarges the background, contributing to the illusion of movement.
  • -webkit-background-clip and -webkit-text-fill-color: These properties render the text transparent, allowing the animated gradient to shine through.
  • animation: Lastly, we deploy our gradient-shift animation. It uses an ease-in-out timing function for smooth transitions and loops indefinitely, creating an ever-changing cascade of colors.

The Result

And there we have it! Check out the vibrant, animated gradient text:

See the Pen Animated Gradient Text by 1stWebDesigner (@firstwebdesigner) on CodePen.

Final Thoughts

The process of creating the animated gradient text effect is surprisingly straightforward, but the creative opportunities it unveils are far-reaching. With this foundational knowledge, you can experiment with different color schemes and gradient directions, apply the animation to various elements like buttons or headers, and even incorporate subtle animated accents into your design.

Remember, the real beauty of CSS is in its flexibility and power – it provides a vast canvas for creativity. You could also explore further with CSS keyframes to manipulate other properties and add more dynamic animations to your design. Feel free to dive deeper into the world of CSS animations with our guide on CSS keyframes.

Implementing Adaptive Dark Mode Based on User’s OS Settings: A Step-by-Step Guide

Category Image 052

More and more users prefer browsing websites in dark mode to reduce eye strain and save battery life. To provide the best user experience, it’s helpful to implement an automatic dark mode on your website that adjusts according to a user’s operating system settings. In this tutorial, we’ll walk you through the steps to achieve this.

Your Web Designer Toolbox
Unlimited Downloads: 500,000+ Web Templates, Icon Sets, Themes & Design Assets


 

1. Create a CSS file for dark mode

First, create a separate CSS file called “darkmode.css” that contains the styles for your website’s dark mode. This file should include the color palette, font styles, and other design elements that cater to dark mode. For example:

body {
  background-color: #121212;
  color: #ffffff;
}

h1, h2, h3, h4, h5, h6 {
  color: #f0f0f0;
}

a {
  color: #ffffff;
  text-decoration: underline;
}

2. Link the dark mode CSS file

In your website’s main HTML file, add the following link tag inside the head section to reference the dark mode CSS file:

<link id="darkmode-stylesheet" rel="stylesheet" href="darkmode.css" media="(prefers-color-scheme: dark)" />

The “media” attribute ensures that the dark mode CSS file will only be applied if the user’s operating system is set to dark mode.

3. Detect operating system’s color scheme

Now it’s time to detect the user’s operating system color scheme with JavaScript. Create a new JavaScript file called “darkmode.js” and add the following code:

function getSystemColorScheme() {
  return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

This function checks if the user’s operating system is set to dark mode or light mode by evaluating the ‘prefers-color-scheme’ media query.

4. Save the user’s preference

To improve the user experience, we’ll save their preference in the browser’s local storage. Add the following code to the “darkmode.js” file:

function saveUserPreference(preference) {
  localStorage.setItem('color-scheme', preference);
}

function loadUserPreference() {
  return localStorage.getItem('color-scheme');
}

These two functions save and load the user’s color scheme preference, respectively.

5. Toggle dark mode

Create a function to toggle dark mode on and off by adding or removing the “dark” class from the HTML body element:

function toggleDarkMode(enableDarkMode) {
  if (enableDarkMode) {
    document.body.classList.add('dark');
  } else {
    document.body.classList.remove('dark');
  }
}

6. Initialize dark mode based on user preference or system settings

Now, add a function to initialize dark mode on page load. This function checks if the user has a saved preference in local storage; if not, it defaults to their operating system’s settings:

function initializeDarkMode() {
  const userPreference = loadUserPreference();

  if (userPreference) {
    toggleDarkMode(userPreference === 'dark');
  } else {
    const systemColorScheme = getSystemColorScheme();
    toggleDarkMode(systemColorScheme === 'dark');
  }
}

document.addEventListener('DOMContentLoaded', initializeDarkMode);

7. Allow users to toggle dark mode manually

As a fallback and also providing a better experience for your users, it is a good practice to give them the ability to switch between dark and light modes manually. To do this, add a button or a switch element in your website’s main HTML file:

<button id="toggle-darkmode">Toggle Dark Mode</button>

Next, add an event listener for this button in your “darkmode.js” file:

function handleDarkModeToggle() {
  const currentPreference = loadUserPreference() || getSystemColorScheme();
  const newPreference = currentPreference === 'dark' ? 'light' : 'dark';

  toggleDarkMode(newPreference === 'dark');
  saveUserPreference(newPreference);
}

document.getElementById('toggle-darkmode').addEventListener('click', handleDarkModeToggle);

This function toggles dark mode, saves the new preference in local storage, and updates the user interface.

8. Listen for operating system changes

To ensure that your website’s dark mode adapts to changes in the user’s operating system settings, add an event listener to the “darkmode.js” file that listens for changes to the ‘prefers-color-scheme’ media query:

function handleSystemColorSchemeChange(event) {
  if (!loadUserPreference()) {
    toggleDarkMode(event.matches);
  }
}

window.matchMedia('(prefers-color-scheme: dark)').addListener(handleSystemColorSchemeChange);

This function checks if the user has a saved preference in local storage. If not, it toggles dark mode based on the updated operating system settings.

9. Link the JavaScript file

Finally, include the “darkmode.js” file in your main HTML file by adding the following script tag inside the head section:

<script src="darkmode.js" defer></script>

The “defer” attribute ensures that the script is executed only after the HTML document has been fully parsed.

All done! Now you have everything you need to implement dark mode on your future projects!

Mastering CSS Variables and Unlocking Their Full Potential

Category Image 052

CSS Variables, also known as Custom Properties, have revolutionized the way we manage styles and build maintainable, flexible stylesheets. They enable developers to store and reuse values throughout a stylesheet, making it easier to change themes, colors, fonts, and more with just a few updates. In this article, we’ll explore the best practices for using CSS variables, along with some helpful code examples.

Your Designer Toolbox Unlimited Downloads: 500,000+ Web Templates, Icon Sets, Themes & Design Assets

1. Defining and Using CSS Variables

To define a CSS variable, you must use the double hyphen (–) syntax. Typically, you’ll want to create your variables within the :root pseudo-class, which refers to the highest-level parent element in the DOM tree. This ensures that your variables are globally accessible.

:root {
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --font-size: 16px;
}

body {
  font-size: var(--font-size);
}

a {
  color: var(--primary-color);
}

button {
  background-color: var(--secondary-color);
}

In the example above, we’ve defined three CSS variables: –primary-color, –secondary-color, and –font-size. To use these variables, we use the var() function, which accepts the variable name as its argument.

2. Fallback Values

One of the great features of CSS variables is their ability to provide a fallback value. This is helpful when a variable may not be defined or supported by a particular browser. To provide a fallback value, include a second argument within the var() function.

body {
  font-size: var(--font-size, 16px);
}

In this example, if the –font-size variable isn’t defined, the browser will use the fallback value of 16px.

3. Leveraging Variables in Media Queries and Theming

CSS variables are incredibly useful when combined with media queries and theming. You can quickly and easily update your styles for different devices or themes by redefining the variable values.

:root {
  --font-size: 16px;
}

@media screen and (min-width: 768px) {
  :root {
    --font-size: 18px;
  }
}

body {
  font-size: var(--font-size);
}

In this example, we’ve updated the –font-size variable within a media query. When the screen width is at least 768px, the font size will automatically adjust to 18px.

4. Working with Calculated Values

CSS variables can be combined with the calc() function to create dynamic and flexible values.

:root {
  --base-padding: 10px;
}

.container {
  padding: calc(var(--base-padding) * 2);
}

In the example above, we’ve defined a base padding value and used it in combination with the calc() function to double the padding for the .container element.

5. Handling Colors with HSL and CSS Variables

When working with colors, using the HSL color format in combination with CSS variables makes it easier to create color schemes and adjust hues, saturation, and lightness.

:root {
  --primary-hue: 210;
  --primary-color: hsl(var(--primary-hue), 50%, 50%);
  --primary-color-light: hsl(var(--primary-hue), 50%, 75%);
  --primary-color-dark: hsl(var(--primary-hue), 50%, 25%);
}

a {
  color: var(--primary-color);
}

a:hover {
  color: var(--primary-color-light);
}

a:active {
  color: var(--primary-color-dark);
}

In this example, we’ve used the HSL color format and CSS variables to create a primary color and its lighter and darker variations. By adjusting the –primary-hue value, you can change the color scheme throughout your stylesheet with ease.

6. JavaScript Interoperability

CSS variables can be accessed and manipulated using JavaScript, providing an additional layer of flexibility and dynamism. For instance, you can create user-customizable themes by modifying variables through JavaScript.

<button onclick="changeTheme()">Change Theme</button>

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0A%20%20function%20changeTheme()%20%7B%0A%20%20%20%20let%20root%20%3D%20document.documentElement%3B%0A%20%20%20%20let%20currentHue%20%3D%20parseInt(root.style.getPropertyValue('--primary-hue'))%3B%0A%20%20%20%20let%20newHue%20%3D%20(currentHue%20%2B%2030)%20%25%20360%3B%0A%20%20%20%20root.style.setProperty('--primary-hue'%2C%20newHue)%3B%0A%20%20%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

In the example above, clicking the “Change Theme” button will change the –primary-hue variable, effectively updating the primary color and its variations.

Wrapping Up

Variables have transformed the way we approach theming, responsiveness, and maintainability in our stylesheets. By following best practices such as global variable declaration, using fallback values, leveraging variables in media queries, working with calculated values, handling colors with HSL, and harnessing JavaScript interoperability, you’ll unlock their full potential.

With this newfound understanding, you can create more efficient, flexible, and dynamic stylesheets that adapt to different devices, user preferences, and themes with minimal effort.

How to Use Icon Fonts in WordPress Post Editor (No Code)

Category Image 091

Do you want to use icon fonts in the WordPress post editor?

Icon fonts allow you to easily use images and symbols in text. They are lightweight and won’t slow down your site, and they can be easily scaled to any size and styled like any other text font.

In this article, we’ll show you how to easily use icon fonts in the WordPress post editor without writing any HTML code.

Using icon fonts in the WordPress editor

We’ll show you multiple methods, each one using a slightly different approach than the other. You can choose one that works best for you.

Method 1. Adding Icon Fonts in WordPress Post Editor using JVM Rich Text Icons

This method is recommended to use on any kind of WordPress website. It is easy to use and works seamlessly with the block editor.

First, you need to install and activate the JVM Rich Text Icons plugin. For more details, see our step-by-step guide on how to install a WordPress plugin.

Upon activation, you can simply edit a WordPress post or page or create a new one. Inside the post editor, add a new paragraph block, and you’ll see a new Flag icon in the block toolbar.

Icon font button in the block toolbar

Clicking on it will show a popup of icons to choose from. It uses the popular Font Awesome icon fonts by default.

You can use the search to look for an icon or simply scroll down to find the icon you want, and then click to add it.

Choose icons to insert

One advantage of using icon fonts is that you can use CSS to style them.

However, since you are already using the block editor, you can simply use the built-in color tools to style the icons.

Style icon fonts using block editor tools

The plugin allows you to use icon fonts in most text blocks such as Paragraph, List, Button, Columns, Cover, and more.

Here is an example of using icon fonts and block options to style three columns.

Icon fonts in columns

Another useful example of using icon fonts is with buttons.

This time we are using inline icon fonts alongside some text for the two buttons.

Using icon fonts in buttons and lists

Feel free to use the block editor tools like text alignment, colors, spacing, and more to get the most out of the icon fonts.

Method 2. Add Icon Fonts in WordPress Post Editor with Font Awesome

This method requires you to add shortcodes in the post editor to display icon fonts. You can use this method if you don’t need to regularly use icon fonts in your WordPress posts and pages.

First, you need to install and activate the Font Awesome plugin. For more details, see our step-by-step guide on how to install a WordPress plugin.

Upon activation, you can edit a post or page in WordPress and use the following shortcode to add a font icon.

[icon name="home"]
Adding icon fonts using shortcode

The name parameter here is the name of the font used by Font Awesome. You can find the entire list on the Font Awesome cheatsheet page.

Once added, you can preview your post or page to see how the icon will look on the live site since it will not display as an icon in the block editor.

This is how it looked on our test site.

Font icon preview

You can use the shortcode inside a paragraph and inline with other text. You can also add it on its own using the ‘Shortcode’ block.

However, using the ‘Shortcode’ block will not give you the styling options you’ll get with other text blocks.

You can also add the shortcode inside columns to create a features row.

Shortcode in columns

It would be a bit trickier as you will not be able to see the actual images, and the column heights will keep changing within the editor.

Here is how it looked on our test website. The columns are the same height, even though they are not in the editor.

Icon fonts preview using Font Awesome

You’ll probably have to preview your work in a new browser tab many times to see how it will look to users.

Method 3. Using Icon Fonts with WordPress Page Builders

This method is great if you are creating a landing page or designing your website using a WordPress page builder like SeedProd.

SeedProd is the best WordPress page builder on the market. It allows you to easily create beautiful landing pages or design your complete website.

SeedProd WordPress Website Builder

First, you need to install and activate the SeedProd plugin. For more details, see our step-by-step guide on how to install a WordPress plugin.

Upon activation, you’ll be asked to enter your plugin license key. You can find this information under your account on the SeedProd website.

SeedProd license key

After entering your license key and clicking ‘Verify Key,’ you can start working on your landing page.

Simply go to the SeedProd » Landing Pages page and click on the ‘Add New Landing Page’ button.

Add new landing page

After that, you will be asked to choose a template for your landing page.

SeedProd comes with a bunch of beautiful designs that you can use as a starting point, or you can start with a blank template and design the whole thing yourself.

Choose landing page template

For this tutorial, we will be using a pre-designed template. Simply click on a template to select it and continue.

Next, you will be asked to provide a title for your landing page and choose a URL.

Provide page title and URL

After entering them, click on the ‘Save and Start Editing the Page’ button to continue.

SeedProd will now launch the page builder interface. It is a drag-and-drop design tool where you can simply point and click on any item to edit it.

SeedProd page builder interface

You can also drag and drop blocks from the left column to add new elements to your design.

For the sake of this tutorial, we are going to add the Icon block.

Add icon block

After you add the block, you can simply click to edit its properties.

The left column will change to show the options for the Icon block. You can click into the ‘Icon’ section to the left and choose a different icon image or change the color and style.

Icon block settings

Another way to use icons in SeedProd is by adding the ‘Icon Box’ block.

The difference between this and the ‘Icon’ block we used previously is that ‘Icon Box’ allows you to add text along with your chosen icon.

This is one of the most common ways to use icons when displaying product features, services, and other items.

Icon box block

You can place your icon box inside columns, choose colors, and adjust the icon size to your liking.

Additionally, you can also format the accompanying text using SeedProd’s formatting toolbar.

Icon box inside columns

Once you are finished editing your page, don’t forget to click on the ‘Save’ button at the top right corner of the screen.

If you’re ready, you can click ‘Publish’ for the page to go live, or you can click on ‘Preview’ to make sure it looks like you want it to.

Save and publish your landing page

You can also click on ‘Save as Template’ so you can reuse this design with SeedProd on other parts of your website.

Here is how the icon fonts looked on our test website.

Icon fonts preview

We hope this article helped you learn how to use icon fonts in WordPress post editor without writing HTML code. You may also want to see our WordPress performance guide to optimize your website speed or the best landing page plugins for WordPress.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post How to Use Icon Fonts in WordPress Post Editor (No Code) first appeared on WPBeginner.

CSS Basics: Visibility: Hidden vs. Display: None

Category Image 052

visibility: hidden and display: none are two CSS properties that can be used to hide elements. While they may seem similar at first glance, there are some significant differences between them. In this article, we’ll explore these differences and provide some examples to illustrate their use.

The UX Designer Toolbox

Unlimited Downloads: 500,000+ Wireframe & UX Templates, UI Kits & Design Assets Starting at only $16.50 per month!

 

visibility: hidden

The visibility property in CSS determines whether or not an element is visible on the web page. If set to hidden, the element will be hidden from view, but it will still occupy space on the page. This means that any other elements that would normally be positioned after it will still be positioned as if the hidden element were still visible.

Here’s an example of how visibility: hidden works:

<!DOCTYPE html>
<html>
  <head>
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0A%20%20%20%20%20%20%23hidden-element%20%7B%0A%20%20%20%20%20%20%20%20visibility%3A%20hidden%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />
  </head>
  <body>
    <p>This is some text before the hidden element.</p>
    <div id="hidden-element">
      <p>This element is hidden using visibility.</p>
    </div>
    <p>This is some text after the hidden element.</p>
  </body>
</html>

In this example, the #hidden-element is hidden using visibility: hidden. Notice that the element still occupies space on the page, and the text after it is still positioned as if it were visible.

display: none

The display property in CSS determines how an element is displayed on the web page. If set to none, the element will be completely removed from the page and will not occupy any space. This means that any other elements that would normally be positioned after it will be repositioned as if the hidden element were not present.

Here’s an example of how display: none works:

<!DOCTYPE html>
<html>
  <head>
    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0A%20%20%20%20%20%20%23hidden-element%20%7B%0A%20%20%20%20%20%20%20%20display%3A%20none%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />
  </head>
  <body>
    <p>This is some text before the hidden element.</p>
    <div id="hidden-element">
      <p>This element is hidden using display.</p>
    </div>
    <p>This is some text after the hidden element.</p>
  </body>
</html>

In this example, the #hidden-element is hidden using display: none. Notice that the element does not occupy any space on the page, and the text after it is repositioned as if the element were not present.

When to use visibility: hidden vs. display: none

Now that we’ve seen how visibility: hidden and display: none work, it’s important to consider when to use one over the other.

Use visibility: hidden when you want to hide an element from view but still want it to occupy space on the page. This can be useful when you want to reveal the element later or when you want to maintain the layout of the page.

Use display: none when you want to completely remove an element from the page and don’t want it to occupy any space. This can be useful when you want to completely hide an element and don’t plan to reveal it later.

How To Use CSS To Maintain Aspect Ratio For Responsive Design

Category Image 052

Maintaining the Aspect ratio of a div is a common requirement when creating responsive web designs. In this article, we’ll explore how to use CSS to maintain the Aspect ratio of a div as the window’s width changes.

To achieve this, we’ll use the padding hack. The padding hack is a technique that uses a percentage value for padding-top or padding-bottom to maintain the Aspect ratio of an element. The percentage value is calculated based on the width of the parent element. As the width of the parent element changes, the padding value will adjust to maintain the Aspect ratio of the child element.

Your Web Designer Toolbox
Unlimited Downloads: 500,000+ Web Templates, Icon Sets, Themes & Design Assets


 

Let’s start by creating a div element with a background color.

<div class="aspect-ratio"></div>

<style>
  .aspect-ratio {
    background-color: #ccc;
  }
</style>

To maintain the Aspect ratio of this div, we’ll set its padding-top to a percentage value. The percentage value will be calculated based on the desired Aspect ratio of the div. For example, if we want the Aspect ratio to be 16:9, we’ll set the padding-top to 56.25% (9/16 * 100).

.aspect-ratio {
  background-color: #ccc;
  padding-top: 56.25%;
}

Now, as the width of the parent element changes, the padding value will adjust as desired.

Here’s an example that shows how to use the padding hack on a div with a background image.

<div class="aspect-ratio" style="background-image: url('image.jpg')"></div>

<style>
  .aspect-ratio {
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    position: relative;
  }
  
  .aspect-ratio:before {
    content: "";
    display: block;
    padding-top: 56.25%;
  }
  
  .aspect-ratio > * {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
</style>

In this example, we are again using the padding hack, and we’re also using a pseudo-element (:before) to create the padding. We’re setting the position of the parent element to relative and the position of the child element to absolute to position the child element inside the parent element. We’re also setting the width and height of the child element to 100% to fill the parent element.

In conclusion, the padding hack is a simple and effective technique for maintaining the Aspect ratio of a div as the window’s width changes. By using a percentage value for padding-top or padding-bottom, we can calculate the padding value based on the desired Aspect ratio of the div. This technique is widely used in responsive web design and can be used to create a variety of layouts and designs. Be sure to check out our library of CSS articles and tutorials while you’re here!

How To Align Checkboxes and Their Labels Consistently Across Browsers

Category Image 052

While checkboxes are relatively straightforward to implement, aligning them with their labels can be a challenge, as each browser renders them differently. In this article, we will explain how to align checkboxes and their labels consistently across all browsers using CSS.

UNLIMITED DOWNLOADS: 500,000+ WordPress & Design Assets

Sign up for Envato Elements and get unlimited downloads starting at only $16.50 per month!

 

Wrap the Checkbox and Label in a Container

The first step is to wrap the elements in a container so we can use it to apply styling to both the checkbox and the label.

<div class="checkbox-container">
  <input type="checkbox" id="checkbox1">
  <label for="checkbox1">Checkbox Label</label>
</div>

Style the Checkbox and Label

Once we have our container, we can use CSS to position the checkbox and label, adjust their size, and style them.

.checkbox-container {
  display: flex;
  align-items: center;
}

.checkbox-container input[type="checkbox"] {
  margin-right: 10px;
}

.checkbox-container label {
  margin: 0;
}

The display: flex; property allows us to align the checkbox and label vertically. The align-items: center; property centers the checkbox and label vertically within the container.

The margin-right: 10px; property adds a small amount of space between the checkbox and the label. The margin: 0; property removes any margin that may be added by default by the browser.

Styling the Checkbox to make it visually appealing

In addition to aligning the checkbox and label, we can also style the checkbox to make it more visually appealing.

.checkbox-container input[type="checkbox"] {
  appearance: none;
  width: 20px;
  height: 20px;
  border: 2px solid #ccc;
  border-radius: 4px;
  background-color: #fff;
  cursor: pointer;
}

.checkbox-container input[type="checkbox"]:checked {
  background-color: #007bff;
  border-color: #007bff;
}

.checkbox-container input[type="checkbox"]:checked::before {
  content: "\2713";
  font-size: 16px;
  color: #fff;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

The appearance: none; property removes the default styling of the checkbox, allowing us to create our own custom style. The width and height properties set the size of the checkbox. The border property creates a border around the checkbox, and the border-radius property rounds the corners of the checkbox.

The background-color property sets the background color of the checkbox, and the cursor: pointer; property changes the cursor to a pointer when the user hovers over the checkbox.

The input[type="checkbox"]:checked selector styles the checkbox when it is checked. The background-color property changes the background color of the checkbox, and the border-color property changes the color of the border.

The input[type="checkbox"]:checked::before pseudo-element adds a checkmark to the checkbox when it is checked. The content property adds the checkmark character, and the font-size property sets the size of the checkmark. The color property sets the color of the checkmark, and the position: absolute; property positions the checkmark in the center of the checkbox.

Conclusion

By wrapping checkboxes and their labels in a container and applying CSS styling, we can align checkboxes and their labels consistently across all browsers, as well as create a more visually appealing and user-friendly form element. Be sure to check out our other CSS articles while you’re here!

The Difference Between the :where() and :is() CSS Selectors

Category Image 052

The CSS selectors :where() and :is() are two pseudo-classes that allow you to select elements based on their relationship with other elements. Although they sound similar, they are different in terms of functionality and syntax.

Your Web Designer Toolbox
Unlimited Downloads: 500,000+ Web Templates, Icon Sets, Themes & Design Assets


The :where() pseudo-class was introduced as part of the CSS Selectors Level 4 specification and allows you to select elements based on the presence of other elements that match a specific condition. In other words, you can select elements based on the relationship between elements in a DOM tree. For example, you can use the :where() selector to select a list item li only if it is the first child of an unordered list ul:

li:where(:first-child of ul) {
  background-color: yellow;
}

On the other hand, the :is() pseudo-class is part of the CSS Selectors Level 3 specification and allows you to select an element if it is one of several different selectors. It is similar to the logical OR operator, in CSS. For example, you can use the :is() selector to select a p element if it is either the first child of its parent or has a class of highlight:

p:is(:first-child, .highlight) {
  background-color: yellow;
}

It’s important to note that the :where() selector has better browser support than the :is() selector, and that the :is() selector should not be used in conjunction with the :not() pseudo-class.

Quick Tip: How To Disable Resizing of a Textarea in HTML or CSS

Category Image 052

In this quick tip, we’re going to show you 2 different ways to disable the resizing of a textarea, for those times when you don’t want the user to be able to control it in this manner. It’s a relatively quick process, with just some simple CSS using the resize CSS property.

Your Designer Toolbox Unlimited Downloads: 500,000+ Web Templates, Icon Sets, Themes & Design Assets

 

HTML

The HTML textarea element provides a resizable property by default. To disable it, you can use the CSS property resize: none in the textarea element. Here is an example:

 <textarea style="resize: none;"></textarea> 

CSS

You can also disable the resizable property of a textarea by using CSS that is not inline. You can add a class to the textarea element and then add the CSS property resize: none to the class. Here is an example:

<textarea class="no-resize"></textarea>

<style>
  .no-resize {
    resize: none;
  }
</style>

Quick Tip: How to Disable Text Selection Highlighting in CSS

Category Image 052

There are two main CSS properties that control text selection highlighting: user-select and -webkit-user-select. These properties are used to specify whether or not users are able to select text on the web page.

UNLIMITED DOWNLOADS: Email, admin, landing page & website templates

Starting at only $16.50 per month!

To disable text selection highlighting in CSS, you can set the value of the user-select property to none:

body {
   -webkit-user-select: none;  /* for Safari */
   -moz-user-select: none;     /* for Firefox */
   -ms-user-select: none;      /* for Internet Explorer */
   user-select: none;          /* for modern browsers */
}

In this example, the user-select property is applied to the body element, which means that text selection highlighting will be disabled for the entire page. If you want to disable text selection highlighting for a specific element, simply apply the property to that element instead of the body element.

It’s important to note that the -webkit-user-select and -moz-user-select properties are vendor-specific extensions, and are used to ensure compatibility with Safari and Firefox, respectively. The -ms-user-select property is used for compatibility with Internet Explorer. The standard user-select property should work in all modern browsers.

Easy Fluid Typography With clamp() Using Sass Functions

Typography Definitions Cover

Fluid typography is getting a lot more popular, especially since the clamp() math function is available in every evergreen browser. But if we’re honest, it’s still a lot of mathematics to achieve this. You can use tools such as utopia.fyi, which are fantastic. But in large projects, it can get messy pretty fast. I’m a big fan of readable and maintainable code and always want to see what my code is doing at a glance. I’m sure there are many more of you like that, so instead of adding a full clamp() function inside of our code, maybe we can make this a bit more readable with Sass.

Why Should We Use Fluid Typography?

Usually, when designing for different screen sizes, we use media queries to determine the font size of our typographic elements. Although this usually gives enough control for the more conventional devices, it doesn’t cover all of the screen sizes.

By using fluid typography, we can make the typography scale more logically between all sorts of different devices.

This is now possible in all evergreen browsers because of the clamp() function in CSS. It is perfect for the job and reduces our media query writing, thus saving us a bit of file size along the way.

How Exactly Does This clamp() Function Work For Typography?

In short, the clamp function looks like this:

clamp([min-bound], [value-preferred], [max-bound]);

This takes into account three numbers: a minimum bound, preferred value, and a maximum bound. By using rem values, we can increase the accessibility a bit, but it’s still not 100% foolproof, especially for external browser tools.

If you want a more in-depth explanation of the math, I suggest you read this post from Adrian Bece “Modern Fluid Typography Using CSS Clamp ”.

However, there is a bit of a problem. When you read those clamp functions inside your CSS, it’s still hard to see exactly what is happening. Just imagine a file full of font sizes that look like this:

clamp(1.44rem, 3.44vw + 0.75rem, 2.81rem)

But with a little help from the sass function, we can make these font sizes much more readable.

What Do We Want To Achieve With This Simple Sass Function?

In short, we want to do something like this: We have a minimum font size, from the moment our breakpoint is larger than 400px, we want it to scale it to our biggest font size until the maximum breakpoint is reached.

The minimum and maximum font sizes are covered quite easily. If we want a minimum font size of 16px (or 1rem) and a maximum font size of 32px (or 2rem), we already have the two parts of our clamp function:

clamp(1rem, [?], 2rem)
Creating A Basic Automated Fluid Function

This is where things get tricky, and I suggest you follow the article by Adrian Bece, who gives a great in-depth explanation of the math behind this.

In short, the equation is the following:

(maximum font-size - minimum font-size) / (maximum breakpoint - minimum breakpoint)

Let’s get ready to do some mathematics for this to happen in Sass, so let’s create our fluid-typography.scss function file and start by adding sass:math and the function with the values we’ll need:

@use "sass:math";

@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {

}

Now, let’s add the calculation for the slope inside of our function with some sass:math:

@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
 $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
}

To get a value we can work with, we’ll need to multiply our slope by 100:

$slope-to-unit: $slope * 100;

All that is left is for us to find our intercept to build the equation. We can do this with the following function:

$intercept: $min-size - $slope * $min-breakpoint;

And finally, return our function:

@return clamp(#{$min-size}, #{$slope-to-unit}#{$unit} + #{$intercept}, #{$max-size});

If we call the created sass function in our scss, we should now get fluid typography:

h1 {
   font-size: #{fluid(1rem, 2rem, 25rem, 62.5rem)}
}

A Note About Units

In most cases, we will be using a viewport width when it comes to fluid typography, so this makes a good default. However, there are some cases, especially when using the clamp() function for vertical spacing, where you want to use a viewport height instead of width. When this is desired, we can change the outputted unit and use a minimum and maximum breakpoint for the height:

h1 {
   font-size: #{fluid(1rem, 2rem, 25rem, 62.5rem, vh)}
}
Updating The Function To Make The Calculations Feel More Natural

We got what we need, but let’s be honest, most of the time, we are implementing a design, and it doesn’t feel natural to pass our viewports as rems. So, let’s update this function to use pixels as a viewport measurement. While we’re at it, let’s update the font sizes so we can use pixel values for everything. We will still convert them to rem units since those are better for accessibility.

First, we’ll need an extra function to calculate our rem values based on a pixel input.

Note: This won’t work if you change your base rem value.

@function px-to-rem($px) {
    $rems: math.div($px, 16px) * 1rem;
    @return $rems;
}

Now we can update our fluid function to output rem values even though it gets pixels as input. This is the updated version:

@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
    $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
    $slope-to-unit: $slope * 100;
    $intercept-rem: px-to-rem($min-size - $slope * $min-breakpoint);
    $min-size-rem: px-to-rem($min-size);
    $max-size-rem: px-to-rem($max-size);
    @return clamp(#{$min-size-rem}, #{$slope-to-unit}#{$unit} + #{$intercept-rem}, #{$max-size-rem});
}

Now we can use the following input:

font-size: #{fluid(16px, 32px, 320px, 960px)}

This will result in the following:

font-size: clamp(1rem, 2.5vw + 0.5rem, 2rem);

At first glance, this seems perfect, but mostly that’s because I’ve been using very simple values. For example, when clamping to a maximum value of 31px instead of 32px, our rem values won’t be so rounded, and our output will get a bit messy.

Input:

font-size: #{fluid(16px, 31px, 320px, 960px)}

Output:

font-size: clamp(1rem, 2.34375vw + 0.53125rem, 1.9375rem);

If you’re like me and find this a bit messy as well, we could round our values a little bit to increase readability and save some bytes in our final CSS file. Also, it might get a bit tedious if we always have to add the viewport, so why not add some defaults in our function?

Rounding Our Values And Adding Some Defaults

Let’s start by adding a rounding function to our Sass file. This will take any input and round it to a specific amount of decimals:

@function round($number, $decimals: 0) {
    $n: 1;
    @if $decimals > 0 {
        @for $i from 1 through $decimals {
            $n: $n * 10;
        }
    }
    @return math.div(math.round($number * $n), $n);
}

Now we can update our output values with rounded numbers. Update the function accordingly. I would suggest setting at least two decimals for the output values for the most consistent results:

@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
    $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
    $slope-to-unit: round($slope * 100, 2);
    $intercept-rem: round(px-to-rem($min-size - $slope * $min-breakpoint), 2);
    $min-size-rem: round(px-to-rem($min-size), 2);
    $max-size-rem: round(px-to-rem($max-size), 2);
    @return clamp(#{$min-size-rem}, #{$slope-to-unit}#{$unit} + #{$intercept-rem}, #{$max-size-rem});
}

Now the same example as before will give us a much cleaner result.

Input:

font-size: #{fluid(16px, 31px, 320px, 960px)};

Output:

font-size: clamp(1rem, 2.34vw + 0.53rem, 1.94rem);

Adding A Default Breakpoint

If you don’t feel like repeating yourself, you can always set a default breakpoint to your function. Try updating the function like this:

$default-min-bp: 320px;
$default-max-bp: 960px;

@function fluid($min-size, $max-size, $min-breakpoint: $default-min-bp, $max-breakpoint: $default-max-bp, $unit: vw) {
    // ...
}

Now, we don’t need to repeat these viewports all the time. We can still add a custom breakpoint but a simple input such as:

font-size: #{fluid(16px, 31px)};

Will still result in:

font-size: clamp(1rem, 2.34vw + 0.53rem, 1.94rem);

Here is the full function:

@use 'sass:math';

$default-min-bp: 320px;
$default-max-bp: 960px;

@function round($number, $decimals: 0) {
    $n: 1;
    @if $decimals > 0 {
        @for $i from 1 through $decimals {
            $n: $n * 10;
        }
    }
    @return math.div(math.round($number * $n), $n);
}

@function px-to-rem($px) {
    $rems: math.div($px, 16px) * 1rem;
    @return $rems;
}

@function fluid($min-size, $max-size, $min-breakpoint: $default-min-bp, $max-breakpoint: $default-max-bp, $unit: vw) {
    $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
    $slope-to-unit: round($slope * 100, 2);
    $intercept-rem: round(px-to-rem($min-size - $slope * $min-breakpoint), 2);
    $min-size-rem: round(px-to-rem($min-size), 2);
    $max-size-rem: round(px-to-rem($max-size), 2);
    @return clamp(#{$min-size-rem}, #{$slope-to-unit}#{$unit} + #{$intercept-rem}, #{$max-size-rem});
}
A Final Note: Be A Happy Clamper For All users Out There

If you followed this little tutorial and were amazed by it, you might want to add this clamp() method for everything, but there is an important side note when it comes to accessibility.

Note: When you use vw units or limit how large text can get with clamp(), there is a chance a user may be unable to scale the text to 200% of its original size.

If that happens, it is WCAG failure. As Adrian Bece mentioned, it’s not 100% foolproof. Adrian Roselli has written some examples on this, which you might find interesting.

We can use this method today because of the great browser support. By being smart on the usage, I’m sure it can be a beautiful addition to your upcoming project or as an upgrade to a previous one.