Collective #732


















LocatorJS

In case you didn’t know about it: An extension that allows to click on an UI Component in the browser and open its code in your IDE.

Check it out



Nerfstudio

Nerfstudio provides an API that allows for a simplified end-to-end process of creating, training, and testing NeRFs.

Check it out






Collective #718



Collective 718 item image
Our Sponsor

Front-end engineering interview prep.

Put your tech career into overdrive with Exponent’s expert interview prep and resources. Nail your front-end engineering interviews to advance your career. Codrops readers can save 20% OFF any Exponent plan for the next 48 hours!

Start Learning



Collective 718 item image

Defensive CSS

An article that sheds light on why it’s important to design and write CSS defensively. By Ahmad Shadeed.

Read it





Collective 718 item image

svg-path-morph

A simple library for morphing between variations of SVG paths. Use svg-path-morph to smoothly morph between X variations of the same SVG path (i.e. same commands, different values).

Check it out






Collective 718 item image

PRQL

PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement.

Check it out















The post Collective #718 appeared first on Codrops.

Coloring With Code — A Programmatic Approach To Design

Color is powerful — it can radically shift our mood, inspire us, and help us express ourselves in a way that few other things can. It is a fundamental building block of design, but it can also be a little intimidating.

Often when given the opportunity to play with color, we freeze. Choosing just one can be enough to trigger a kind of iridescent nightmare, not to mention combining lots of them! The options are infinite, and the “rules” somewhat hazy… a potentially overwhelming combination, particularly for those of us used to the (often) more definite world of code.

In this tutorial, we will be learning how to use familiar tools — a text editor and web browser — to make the process of creating striking color palettes a lot less scary and (most importantly!) fun. 

Let’s do it! 

Intended audience

This article is perfect for folks who already have a good grasp of HTML, CSS (knowledge of HSL and RGB colors will be helpful), and JavaScript. If you love to make things on the web, but often reach for a pre-curated selection or an automatic “generator” when adding color, you’re in the right spot. 

Tutorial format 

We won’t be building one single, strictly defined project here. Instead, we will be learning to create three special JavaScript functions, all uniquely suited to generating beautiful color palettes. Once written, these functions will form a solid foundation for our very own suite of programmatic color tools, which can be carried from project to project and iterated on/personalized over time.

A short introduction to LCH color

Throughout this tutorial, we will be working almost exclusively with LCH colors. LCH stands for lightness (how dark/light a color is), chroma (how vivid/saturated a color is), and hue (whether a color is red, green, blue…).

In short, LCH is a way of representing color just like RGB or HSL, but with a few notable advantages — the most important for this tutorial being its perceptual uniformity. I know this sounds a little scary, but I promise it’s not; let me show you what it means!

To start, take a look at these two pairs of HSL colors: 

Two pairs of colored rectangles, with accompanying text describing the HSL value needed to create them. The two pairs of colors both have 20 degree hue difference in their HSL definition, but the rate of change between them is wildly different.

Notice how, despite both the top and bottom pairs having the same 20-degree hue variance, the difference we actually see is wildly different? This imbalance exists because HSL is not perceptually uniform.

Now take a look at the same experiment, this time using LCH colors:

Two pairs of colored rectangles, with accompanying text describing the LCH value needed to create them. The two pairs of colors both have 20 degree hue difference in their LCH definition, and the rate of change between them is consistent.

👋hue values do not align perfectly between HSL and LCH. In LCH, a hue of 0 is more pink, while in HSL, it is a pure red. 

Ah, much better! The change in hue seen here is far more balanced because LCH is perceptually uniform

Next, let’s take a peek at another two HSL colors: 

Two colored rectangles, and the relevant HSL values needed to create them. The two HSL values have identical lightness definitions, but appear very different. The left rectangle is far brighter.

These two colors have identical lightness values but appear very different to our human eyes. The yellow on the left is far “brighter” than the blue on the right.

Here’s a similar setup, but with LCH colors rather than HSL: 

Two colored rectangles, and the relevant LCH values needed to create them. The two LCH values have identical lightness definitions, and reflect this visually, being similarly bright.

That’s more like it! As demonstrated by the image above, lightness values in LCH are far more accurate representations of what we perceive — this, in combination with LCH’s uniform hue distribution, will make our lives a lot easier when creating harmonious color palettes. 

For now, this is all we need to know, but if you would like to learn more, I highly recommend this article by Lea Verou

👋 — We will be using a library in this tutorial, but native LCH support is heading to the browser! In fact, it is already in Safari, with other browsers currently working on it.

Following along

Before we write any code, we need a simple development environment. This setup is entirely your choice, but I recommend spinning up a CodePen to follow along with the examples, then moving to a custom setup/repository as and when you need to. Really, all we need here is an HTML/JavaScript file, and we will be using Skypack for all library imports, so there’s no need for any fancy build processes, etc.

Function #1 — “Scientific”

OK! First off, we are generating colors using “traditional” color theory. To get started with this method, let’s take a look at something called a color wheel:

12 colors, arranged in a "rainbow" around a 360 degree color wheel.

Look familiar?

A color wheel is a visual representation of the hues in a color space. The wheel above represents the hues in LCH, incrementing in 30-degree steps, from 0 to 360-degrees — a well-established format. In fact, for hundreds of years, we have used wheels to find colors that work well together!

Here’s how:

We start with a base color. Then, we rotate around the wheel by a certain number of degrees a certain number of times; for a perfect complementary palette, we move 180 degrees once: 

A 12 item color wheel, with 2 hues highlighted. The two highlighted hues appear at 0, and 180 degrees on the wheel (the top and bottom) - the colors themselves are a shade of pink and green.

Lovely! For a triadic palette, we move 120 degrees, twice: 

A 12 item color wheel, with 3 hues highlighted. The 3 highlighted hues appear at 0, and 120, and 240 degrees. The colors themselves are a shade of pink and green, and blue.

See where this is going? By altering the number of steps and rotation amount, we can create several “classic” color palettes:

A selection of 5 traditional color palettes plotted around color wheels.

Cool! Let’s take this method and turn it into 1s and 0s.

To keep things moving throughout this tutorial, I’ll show you the code, then break it down step-by-step:

The code

function adjustHue(val) {
  if (val < 0) val += Math.ceil(-val / 360) * 360;

  return val % 360;
}

function createScientificPalettes(baseColor) {
  const targetHueSteps = {
    analogous: [0, 30, 60],
    triadic: [0, 120, 240],
    tetradic: [0, 90, 180, 270],
    complementary: [0, 180],
    splitComplementary: [0, 150, 210]
  };

  const palettes = {};

  for (const type of Object.keys(targetHueSteps)) {
    palettes[type] = targetHueSteps[type].map((step) => ({
      l: baseColor.l,
      c: baseColor.c,
      h: adjustHue(baseColor.h + step),
      mode: "lch"
    }));
  }

  return palettes;
}

To break this down:

  1. Define a function createScientificPalettes that expects a single baseColor argument.
  2. Define the hue steps for several “classic” color palettes.
  3. For each palette type: iterate over each hue step, add the step value to the base hue, and store the resulting color — making sure its chroma and lightness values match the base. Use a small adjustHue function to ensure all hue values are between 0 and 360.
  4. Return the palettes in LCH format.  

Usage

Awesome! We can call our createScientificPalettes function like so:

const baseColor = {
  l: 50,
  c: 100,
  h: 0,
  mode: "lch"
};

const palettes = createScientificPalettes(baseColor);

In the example above, we pass a baseColor object, and the function returns a variety of palettes, all centered around that base. Thanks to LCH, the lightness and intensity of the colors in these palettes will be visually consistent, and the hue modulations highly accurate; this is great for accessibility, as, unlike other color spaces, each color in the palette will have the same perceived contrast. 

Cool! All that’s left to do now is convert the LCH colors to a more usable format. To do so, we can use Culori — an excellent color utility library used throughout this tutorial — to transform the LCH objects to, say, HEX:

import { formatHex } from "https://cdn.skypack.dev/culori@2.0.0";

const baseColor = {
  l: 50,
  c: 100,
  h: 0,
  mode: "lch"
};

const palettes = createScientificPalettes(baseColor);
const triadicHex = palettes.triadic.map((colorLCH) => formatHex(colorLCH));

// ["#ff007c", "#1f8a00", "#0091ff"]

👋 — Culori requires an explicit mode on all color objects. You will notice this included in the code examples throughout this tutorial. 

For our first function, that’s it! Let’s take a look at how we can use it in real life.

Practical application

One benefit of creating our color palettes with code (programmatically) is that it makes rapid prototyping/experimentation super easy. Say, for example, we were working on a design and got completely stuck with what color palette to use. Using our createScientificPalettes function, alongside some simple CSS custom properties, we can generate near-infinite palettes and test them with our UI in real-time! 

Here’s a CodePen to demonstrate:

light

Challenge

Right now, our createScientificPalettes function accounts for all palette types, apart from monochromatic. Can you update it to support monochromatic palettes

Function #2 — “Discovery”

So, this function is similar to the previous one but with quite a twist. We are still generating “classic” color combinations, but rather than calculating them scientifically (adding set “steps” to the hue of a base color), we are discovering them! That’s right; our discovery function will take an array of colors and find the best palette matches within it — analogous, triadic, tetradic, etc. 

Here’s an illustrated example:

A selection of colors, and a color wheel. Two colors on the wheel (at 0 and 180 degrees) are highlighted, and the closest matches for these two within the selection of colors is also highlighted, with lines drawn to their closest match.

Using this function, we can discover beautiful palettes within images, color datasets, and more! Let’s see how it works.

The code

import {
  nearest,
  differenceEuclidean,
} from "https://cdn.skypack.dev/culori@2.0.0";

function isColorEqual(c1, c2) {
  return c1.h === c2.h && c1.l === c2.l && c1.c === c2.c;
}

function discoverPalettes(colors) {
  const palettes = {};

  for (const color of colors) {
    const targetPalettes = createScientificPalettes(color);

    for (const paletteType of Object.keys(targetPalettes)) {
      const palette = [];
      let variance = 0;

      for (const targetColor of targetPalettes[paletteType]) {
        // filter out colors already in the palette
        const availableColors = colors.filter(
          (color1) => !palette.some((color2) => isColorEqual(color1, color2))
        );

        const match = nearest(
          availableColors,
          differenceEuclidean("lch")
        )(targetColor)[0];

        variance += differenceEuclidean("lch")(targetColor, match);

        palette.push(match);
      }

      if (!palettes[paletteType] || variance < palettes[paletteType].variance) {
        palettes[paletteType] = {
          colors: palette,
          variance
        };
      }
    }
  }

  return palettes;
}

To break this down: 

  1. Pass an array of LCH colors to the discoverPalettes function. 
  2. For every color, create the “optimum” target palettes based on it using our createScientificPalettes function. 
  3. For every palette, find the closest match for each of its colors. We calculate color matches here using Culori’s nearest and differenceEuclidian functions. 
  4. Determine how similar/different the “discovered” palette is to the target. Keep a record of the closest palette matches. 
  5. Return the closest match of each palette type! 

Awesome! This method is super exciting, as it operates much as a human would — looking at a selection of colors and finding the best (but never perfect) palettes; this is great, as sometimes, purely mathematic color theory can appear a touch sterile/predictable. 

Usage 

As a quick reference, here’s how we could use discoverPalettes with an array of HEX colors: 

import {
  converter,
} from "https://cdn.skypack.dev/culori@2.0.0";

const toLCH = converter("lch");

const baseColors = [
  "#FFB97A",
  "#FF957C",
  "#FF727F",
  "#FF5083",
  "#F02F87",
  "#C70084",
  "#9A007F",
  "#6A0076",
  "#33006B"
];

const baseColorsLCH = baseColors.map((color) => toLCH(color));

const palettes = discoverPalettes(baseColorsLCH);

// { analogous: [...], complementary: [...], ... }

👋  discoverPalettes expects a minimum of four colors to function correctly.

Practical application

One of the most compelling aspects of discoverPalettes is its ability to pull coherent color combinations out of just about any source. Here it is, discovering palettes based on images from Unsplash:

light

Cool eh? Extracting palettes from photographs is a fantastic way of working when stuck for ideas, and discoverPalettes makes the process incredibly easy. This kind of approach, previously available only through “magic” color generators/apps, is now right at our fingers and ready to be tweaked, iterated, and improved to suit our own personal use-cases and preferences!

Challenge

Right now, our discoverPalettes function finds the best matches it can in an array of colors, but it isn’t too easy to control. Can you add a degree of bias/weighting to its selection? How might you modify the function to prioritize brighter colors, for example?

Function #3 — “Hue Shift”

For our third and final function, we will be taking inspiration from the world of pixel art!

Often when adding shades/highlights to a sprite, pixel artists will not only modulate the lightness/chroma of a color (saturation if working with HSL) but also shift its hue. Here’s an excellent video on the subject, but in short, this is what it looks like:

So pretty! As a color becomes lighter, its hue shifts up; as it becomes darker, it shifts down. When applied subtly, this technique helps ensure shades/tints of a color are vivid and impactful. When “dialed up” a little, it is a fantastic way of generating stunning standalone color palettes.

The code

function adjustHue(val) {
  if (val < 0) val += Math.ceil(-val / 360) * 360;

  return val % 360;
}

function map(n, start1, end1, start2, end2) {
  return ((n - start1) / (end1 - start1)) * (end2 - start2) + start2;
}

function createHueShiftPalette(opts) {
  const { base, minLightness, maxLightness, hueStep } = opts;

  const palette = [base];

  for (let i = 1; i < 5; i++) {
    const hueDark = adjustHue(base.h - hueStep * i);
    const hueLight = adjustHue(base.h + hueStep * i);
    const lightnessDark = map(i, 0, 4, base.l, minLightness);
    const lightnessLight = map(i, 0, 4, base.l, maxLightness);
    const chroma = base.c;

    palette.push({
      l: lightnessDark,
      c: chroma,
      h: hueDark,
      mode: "lch"
    });

    palette.unshift({
      l: lightnessLight,
      c: chroma,
      h: hueLight,
      mode: "lch"
    });
  }

  return palette;
}

To break this down into steps: 

  1. Pass a base color, min/max lightness, and hue step parameters to a createHueShiftPalette function. The min/max lightness values determine how dark/light our palette will be at either extreme. The step controls how much the hue will shift at each color.
  2. Store the base color in an array. In the illustration above, this is the middle color. 
  3. Create a loop that iterates four times. Each iteration, add a darker shade to the start of the array and a lighter tint to the end. Here, we use map to calculate our lightness values — a function that takes a number that usually exists in one range and converts it to another — and increase or decrease the hue using our hueStep variable. Again, adjustHue is used here to ensure all hue values are between 0 and 360.
  4. Return the palette! 

Usage 

Once our createHueShiftPalette function is defined, we can use it like so:

import { formatHex } from "https://cdn.skypack.dev/culori@2.0.0";

const hueShiftPalette = createHueShiftPalette({
  base: {
    l: 55,
    c: 75,
    h: 0,
    mode: "lch"
  },
  minLightness: 10,
  maxLightness: 90,
  hueStep: 12
});

const hueShiftPaletteHex = hueShiftPalette.map((color) => formatHex(color));

// ["#ffb97a", "#ff957c", "#ff727f", "#ff5083", "#f02f87", "#c70084", "#9a007f", "#6a0076", "#33006b"]

Practical application

The palettes generated by createHueShiftPalette work fantastically for patterns/graphics; here’s an example using it to create random/generative patterns that differ ever-so-slightly each time they render: 

light

Cool, right? As just one example using this approach, we can create UI elements that are always fresh and unique to the current user — a lovely way to bring a little joy to the folks who use our websites/applications! 

Challenge

Right now, the lightness/hue values scale linearly in our createHueShiftPalette function. Could you apply some easing to them? Perhaps, starting with a larger/smaller hue shift and reducing/increasing it with each step?

Wrapping up

Well, folks, that’s all for now! We have learned how to create three beautiful color generation functions, seen how they can be applied and considered how they could be improved/changed. From here, I hope you take these functions and change them to suit you, and hopefully, even write your own!

As developers, we have a unique skill set that is perfect for creating truly innovative, stunning design. Whether that means creating a color generation tool for designers you work with or adding mind-blowing generative palettes to your website — we should all feel confident in our ability to work with color.

Until next time! 

The post Coloring With Code — A Programmatic Approach To Design appeared first on Codrops.

Boost Your Skills Online: Smashing Workshops On Front-End And Design

How do we build and establish a successful design system? What about modern CSS and JavaScript? What’s the state of HTML Email? And what are new, smart design patterns we could use? What will it take us to move to TypeScript or Vue.js? With our online workshops, we try to answer these questions well.

Our workshops bring in knowledgeable, kind folks from the community to explore real-life solutions to real-life problems, live, with you. All sessions are broken down into 2.5h-segments across days, so you always have time to ask questions, share your screen and get immediate feedback. Jump to all workshops.

Meet Smashing Online Workshops: live, interactive sessions on front-end & UX.

In fact, live discussions and interactive exercises are at the very heart of every workshop, with group work, homework, reviews and live interaction with people around the world. Plus, you get all video recordings of all sessions, so you can re-watch at any time, in your comfy chair in familiar comfort of your workspace.

Upcoming Workshops (May-September 2021)

No pre-recorded sessions, no big picture talks. Our workshops take place live and span multiple days. They are split into 2.5h-sessions, plus you’ll get all workshop video recordings, slides and a friendly Q&A in every session.

We also have friendly bundles for larger teams and agencies.

Workshops in May–July

Meet our friendly front-end & UX workshops. Boost your skills online and learn from experts — live.

Workshops in August–September

What Are Online Workshops Like?

Do you experience Zoom fatigue as well? After all, who really wants to spend more time in front of their screen? That’s exactly why we’ve designed the online workshop experience from scratch, accounting for the time needed to take in all the content, understand it and have enough time to ask just the right questions.

In our workshops, everybody is just a slightly blurry rectangle on the screen; everybody is equal, and invited to participate.

Our online workshops take place live and span multiple days across weeks. They are split into 2.5h-sessions, and in every session there is always enough time to bring up your questions or just get a cup of tea. We don’t rush through the content, but instead try to create a welcoming, friendly and inclusive environment for everyone to have time to think, discuss and get feedback.

There are plenty of things to expect from a Smashing workshop, but the most important one is focus on practical examples and techniques. The workshops aren’t talks; they are interactive, with live conversations with attendees, sometimes with challenges, homework and team work.

Of course, you get all workshop materials and video recordings as well, so if you miss a session you can re-watch it the same day.

TL;DR

  • Workshops span multiple days, split in 2.5h-sessions.
  • Enough time for live Q&A every day.
  • Dozens of practical examples and techniques.
  • You’ll get all workshop materials & recordings.
  • All workshops are focused on front-end & UX.
  • Get a workshop bundle and save $250 off the price.

Thank You!

We hope that the insights from the workshops will help you improve your skills and the quality of your work. A sincere thank you for your kind, ongoing support and generosity — for being smashing, now and ever. We’d be honored to welcome you.

New Live Workshops On Front-End & UX

There is something magical about people from all over the world coming together, live. Camera on, mic nearby, in a comfy chair, with fingertips eagerly hitting your beloved keyboard. We've been so humbled to welcome over 2000 wonderful people like you in our workshops already — from Montevideo to Delhi; from Perth to Cape Town; from Austin to remote corners of Lapland.

Meet Smashing Online Workshops: live, interactive sessions on front-end & UX.

Every attendee has their own story and experiences to share, all from the comfort of their home, and the convenience of their working space. And so we’ve just announced new dates and speakers for upcoming months. And we thought, you know, maybe you’d like to join in as well.

Just in case you are wondering: here's what the workshops are like.

Upcoming Workshops in March–July

No pre-recorded sessions, no big picture talks. Our online workshops take place live and span multiple days across weeks. They are split into 2.5h-sessions, plus you’ll get all workshop video recordings, slides and a friendly Q&A in every session. (Ah, you can save up to 25% off with a Smashing Membershipjust sayin’!.)

Workshops in March–April

Meet our friendly front-end & UX workshops. Boost your skills online and learn from experts — live.

Workshops in May–July

What Are Online Workshops Like?

Do you experience Zoom fatigue as well? After all, who really wants to spend more time in front of their screen? That’s exactly why we’ve designed the online workshop experience from scratch, accounting for the time needed to take in all the content, understand it and have enough time to ask just the right questions.

In our workshops, everybody is just a slightly blurry rectangle on the screen; everybody is equal, and invited to participate.

Our online workshops take place live and span multiple days across weeks. They are split into 2.5h-sessions, and in every session there is always enough time to bring up your questions or just get a cup of tea. We don’t rush through the content, but instead try to create a welcoming, friendly and inclusive environment for everyone to have time to think, discuss and get feedback.

There are plenty of things to expect from a Smashing workshop, but the most important one is focus on practical examples and techniques. The workshops aren’t talks; they are interactive, with live conversations with attendees, sometimes with challenges, homework and team work.

Of course, you get all workshop materials and video recordings as well, so if you miss a session you can re-watch it the same day.

TL;DR

  • Workshops span multiple days, split in 2.5h-sessions.
  • Enough time for live Q&A every day.
  • Dozens of practical examples and techniques.
  • You’ll get all workshop materials & recordings.
  • All workshops are focused on front-end & UX.
  • Get a workshop bundle and save $250 off the price.

Thank You!

We hope that the insights from the workshops will help you improve your skills and the quality of your work. A sincere thank you for your kind, ongoing support and generosity — for being smashing, now and ever. We’d be honored to welcome you.

Collective #635




Back/forward cache

Learn how to optimize your pages for instant loads when using the browser’s back and forward buttons. By Philip Walton.

Read it





ThreeJS starter

A fantastic Three.js boilerplate made by Francesco Michelini. It uses Parcel to create the bundle and Tweakpane for live updates. Here’s the demo.

Check it out


Tailwind CSS v2.0

Read about the new Tailwind CSS version that includes an all-new color palette, dark mode support, and a lot more.

Read it



Masonry Layout

Level 3 of the CSS Grid Layout specification includes a masonry value for grid-template-columns and grid-template-rows. This MDN guide details what masonry layout is, and how to use it.

Read it
















The post Collective #635 appeared first on Codrops.

The :focus-visible Trick

Always worth repeating: all interactive elements should have a focus style. That way, a keyboard user can tell when they have moved focus to that element.

But if you use :focus alone for this, it has a side effect that a lot of people don’t like. It means that when you click (with a mouse) on an interactive element, you’ll see the focus style. Arguably, you don’t need that feedback as a mouse user, because you just moved your cursor there and clicked. Whatever you think of that, it’s annoyed so many people over the years that they remove focus styles entirely, which is a gnarly net loss for accessibility on the web.

What if we could apply focus styles only when the keyboard is used to focus something, not the mouse? Lea Verou put a finger on this a few years back:

That was in response to Chrome dropping the feature behind a flag. Clever clever.

Fast forward a couple of years, Chrome is releasing it without a flag. They are on board with Lea’s idea:

By combining :focus-visible with :focus you can take things a step further and provide different focus styles depending on the user’s input device. This can be helpful if you want the focus indicator to depend on the precision of the input device:

/* Focusing the button with a keyboard will show a dashed black line. */
button:focus-visible {
  outline: 4px dashed black;
}
  
/* Focusing the button with a mouse, touch, or stylus will show a subtle drop shadow. */
button:focus:not(:focus-visible) {
  outline: none;
  box-shadow: 1px 1px 5px rgba(1, 1, 0, .7);
}

I might suggest trying those selectors without the button, making them globally applied!

There is more to dig into, so I’ll link up some more stuff here:

  • The Chromium Blog post covers the heuristics of the selector. It’s tricky. It’s like there is an algorithm to determine if :focus-visible is going to match or not, which you just largely need to trust. It also covers the idea that Firefox has long had :-moz-focusring, but the behavior is different enough that they don’t recommend using it if you’re shooting for consistent behavior.
  • Matthias Ott blogged about it with some good info, like using the official polyfill and how to look at the styles properly in DevTools (there is a new checkbox for it).
  • We’ve covered this before. In that, we noted Lea’s tweet that she thought usage would explode when it ships for real. Let’s see (and hope)!
  • Our almanac entry has a bunch of details.


The post The :focus-visible Trick appeared first on CSS-Tricks.

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

While You Weren’t Looking, CSS Gradients Got Better

One thing that caught my eye on the list of features for Lea Verou's conic-gradient() polyfill was the last item:

Supports double position syntax (two positions for the same color stop, as a shortcut for two consecutive color stops with the same color)

Surprisingly, I recently discovered most people aren't even aware that double position for gradient stops is something that actually exists in the spec, so I decided to write about it.

According to the spec:

Specifying two locations makes it easier to create solid-color "stripes" in a gradient, without having to repeat the color twice.

I completely agree, this was the first thing I thought of when I became aware of this feature.

Let's say we want to get the following result: a gradient with a bunch of equal width vertical stripes (which I picked up from an earlier post by Chris):

Screenshot. Shows 8 vertical rainbow stripes, from left to right: violet, magenta, red, orange, yellow, yellowish green, teal, blue.
Desired gradient result.

The hex values are: #5461c8, #c724b1, #e4002b, #ff6900, #f6be00, #97d700, #00ab84 and #00a3e0.

Let's first see how we'd CSS this without using double stop positions!

We have eight stripes, which makes each of them one-eighth of the gradient width. One eighth of 100% is 12.5%, so we go from one to the next at multiples of this value.

This means our linear-gradient() looks as follows:

linear-gradient(90deg, 
             #5461c8 12.5% /* 1*12.5% */, 
  #c724b1 0, #c724b1 25%   /* 2*12.5% */, 
  #e4002b 0, #e4002b 37.5% /* 3*12.5% */, 
  #ff6900 0, #ff6900 50%   /* 4*12.5% */, 
  #f6be00 0, #f6be00 62.5% /* 5*12.5% */, 
  #97d700 0, #97d700 75%   /* 6*12.5% */, 
  #00ab84 0, #00ab84 87.5% /* 7*12.5% */, 
  #00a3e0 0)

Note that we don't need to repeat stop position % values because, whenever a stop position is smaller than a previous one, we automatically have a sharp transition. That's why it's always safe to use 0 (which is always going to be smaller than any positive value) and have #c724b1 25%, #e4002b 0 instead of #c724b1 25%, #e4002b 25%, for example. This is something that can make our life easier in the future if, for example, we decide we want to add two more stripes and make the stop positions multiples of 10%.

Not too bad, especially compared to what gradient generators normally spit out. But if we decide one of those stripes in the middle doesn't quite fit in with the others, then changing it to something else means updating in two places.

Again, not too bad and nothing we can't get around with a little bit of help from a preprocessor:

$c: #5461c8 #c724b1 #e4002b #ff6900 #f6be00 #97d700 #00ab84 #00a3e0;

@function get-stops($c-list) {
  $s-list: ();
  $n: length($c-list);
  $u: 100%/$n;
	
  @for $i from 1 to $n {
    $s-list: $s-list, 
             nth($c-list, $i) $i*$u, 
             nth($c-list, $i + 1) 0
  }

  @return $s-list
}

.strip {
  background: linear-gradient(90deg, get-stops($c)))
}

This generates the exact CSS gradient we saw a bit earlier and now we don't have to modify anything in two places anymore.

See the Pen by thebabydino (@thebabydino) on CodePen.

However, even if a preprocessor can save us from typing the same thing twice, it doesn't eliminate repetition from the generated code.

And we may not always want to use a preprocessor. Leaving aside the fact that some people are stubborn or have an irrational fear or hate towards preprocessors, it sometimes feels a bit silly to use a loop.

For example, when we barely have anything to loop over! Let's say we want to get a much simpler background pattern, such as a diagonal hashes one, which I'd imagine is a much more common use case than an over-the-top rainbow one that's probably not a good fit on most websites anyway.

Screenshot. Shows a pattern of diagonal light grey hashes on a white background.
Desired hashes result

This requires using repeating-linear-gradient() and this means a bit of repetition, even if we don't have the same long list of hex values as we did before:

repeating-linear-gradient(-45deg, 
    #ccc /* can't skip this, repeating gradient won't work */, 
    #ccc 2px, 
    transparent 0, 
    transparent 9px /* can't skip this either, tells where gradient repetition starts */)

Here, we cannot ditch the first and last stops because those are precisely what indicate how the gradient repeats within the rectangle defined by the background-size.

If you want to understand why it's better to use repeating-linear-gradient() instead of a plain old linear-gradient() combined with the proper background-size in order to create such hashes, check out this other article I wrote a while ago.

This is precisely where such feature comes to the rescue — it allows us to avoid repetition in the final CSS code.

For the rainbow stripes case, our CSS becomes:

linear-gradient(90deg, 
    #5461c8 12.5%, 
    #c724b1 0 25%, 
    #e4002b 0 37.5%, 
    #ff6900 0 50%, 
    #f6be00 0 62.5%, 
    #97d700 0 75%, 
    #00ab84 0 87.5%, 
    #00a3e0 0)

And to recreate the hashes, we only need:

repeating-linear-gradient(-45deg, 
    #ccc 0 2px, 
    transparent 0 9px)

See the Pen by thebabydino (@thebabydino) on CodePen.

What about support? Well, glad you asked! It actually happens to be pretty good! It works in Safari, Chromium browsers (which now includes Edge as well!) and Firefox. Pre-Chromium Edge and maybe some mobile browsers could still hold you back, but if you don't have to worry about providing support for every browser under the sun or it's fine to provide a fallback, go ahead and start using this!

The post While You Weren’t Looking, CSS Gradients Got Better appeared first on CSS-Tricks.