Optimizing SVG Patterns to Their Smallest Size

I recently created a brick wall pattern as part of my #PetitePatterns series, a challenge where I create organic-looking patterns or textures in SVG within 560 bytes (or approximately the size of two tweets). To fit this constraint, I have gone through a journey that has taught me some radical ways of optimizing SVG patterns so that they contain as little code as possible without affecting the overall image quality.

I want to walk you through the process and show you how we can take an SVG pattern that starts at 197 bytes all the way down to a mere 44 bytes — a whopping 77.7% reduction!

The SVG pattern

This is what’s called a “running bond” brick pattern. It’s the most common brick pattern out there, and one you’ve surely seen before: each row of bricks is offset by one half the length of a brick, creating a repeating staggered pattern. The arrangement is pretty simple, making SVG’s <pattern> element a perfect fit to reproduce it in code.

The SVG <pattern> element uses a pre-defined graphic object which can be replicated (or “tiled”) at fixed intervals along the horizontal and vertical axes. Essentially, we define a rectangular tile pattern and it gets repeated to paint the fill area.

First, let’s set the dimensions of a brick and the gap between each brick. For the sake of simplicity, let’s use clean, round numbers: a width of 100 and a height of 30 for the brick, and 10 for the horizontal and vertical gaps between them.

Showing a highlighted portion of a brick wall pattern, which is the example we are using for optimizing SVG patterns.

Next, we have to identify our “base” tile. And by “tile” I’m talking about pattern tiles rather than physical tiles, not to be confused with the bricks. Let’s use the highlighted part of the image above as our pattern tile: two whole bricks in the first row, and one whole sandwiched between two half bricks in the second row. Notice how and where the gaps are included, because those need to be included in the repeated pattern tile.

When using <pattern>, we have to define the pattern’s width and height, which correspond to the width and height of the base tile. To get the dimensions, we need a little math:

Tile Width  = 2(Brick Width) + 2(Gap) = 2(100) + 2(10) = 220
Tile Height = 2(Bright Height) + 2(Gap) = 2(30) + 2(10) = 80

Alright, so our pattern tile is 220✕80. We also have to set the patternUnits attribute, where the value userSpaceOnUse essentially means pixels. Finally, adding an id to the pattern is necessary so that it can be referenced when we are painting another element with it.

<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse">
  <!-- pattern content here -->
</pattern>

Now that we have established the tile dimensions, the challenge is to create the code for the tile in a way that renders the graphic with the smallest number of bytes possible. This is what we hope to end up with at the very end:

The bricks (in black) and gaps (in white) of the final running bond pattern

Initial markup (197 bytes)

The simplest and most declarative approach to recreate this pattern that comes to my mind is to draw five rectangles. By default, the fill of an SVG element is black and the stroke is transparent. This works well for optimizing SVG patterns, as we don’t have to explicitly declare those in the code.

Each line in the code below defines a rectangle. The width and height are always set, and the x and y positions are only set if a rectangle is offset from the 0 position.

<rect width="100" height="30"/>
<rect x="110" width="100" height="30"/>
<rect y="40" width="45" height="30"/>
<rect x="55" y="40" width="100" height="30"/>
<rect x="165" y="40" width="55" height="30"/>

The top row of the tile contained two full-width bricks, the second brick is positioned to x="110" allowing 10 pixels of gap before the brick. Similarly there’s 10 pixels of gap after, because the brick ends at 210 pixels (110 + 100 = 210) on the horizontal axis even though the <pattern> width is 220 pixels. We need that little bit of extra space; otherwise the second brick would merge with the first brick in the adjacent tile.

The bricks in the second (bottom) row are offset so the row contains two half bricks and one whole brick. In this case, we want the half-width bricks to merge so there’s no gap at the start or the end, allowing them to seamlessly flow with the bricks in adjoining pattern tiles. When offsetting these bricks, we also have to include half gaps, thus the x values are 55 and 165, respectively.

Element reuse, (-43B, 154B total)

It seems inefficient to define each brick so explicitly. Isn’t there some way to optimize SVG patterns by reusing the shapes instead?

I don’t think it’s widely known that SVG has a <use> element. You can reference another element with it and render that referenced element wherever <use> is used. This saves quite a few bytes because we can omit specifying the widths and heights of each brick, except for the first one.

That said, <use> does come with a little price. That is, we have to add an id for the element we want to reuse.

<rect id="b" width="100" height="30"/>
<use href="#b" x="110"/>
<use href="#b" x="-55" y="40"/>
<use href="#b" x="55" y="40"/>
<use href="#b" x="165" y="40"/>

The shortest id possible is one character, so I chose “b” for brick. The <use> element can be positioned similarly to <rect>, with the x and y attributes as offsets. Since each brick is full-width now that we’ve switched to <use> (remember, we explicitly halved the bricks in the second row of the pattern tile), we have to use a negative x value in the second row, then make sure the last brick overflows from the tile for that seamless connection between bricks. These are okay, though, because anything that falls outside of the pattern tile is automatically cut off.

Can you spot some repeating strings that can be written more efficiently? Let’s work on those next.

Rewriting to path (-54B, 100B total)

<path> is probably the most powerful element in SVG. You can draw just about any shape with “commands” in its d attribute. There are 20 commands available, but we only need the simplest ones for rectangles.

Here’s where I landed with that:

<path d="M0 0h100v30h-100z
         M110 0h100v30h-100
         M0 40h45v30h-45z
         M55 40h100v30h-100z
         M165 40h55v30h-55z"/>

I know, super weird numbers and letters! They all have meaning, of course. Here’s what’s happening in this specific case:

  • M{x} {y}: Moves to a point based on coordinates.
  • z: Closes the current segment.
  • h{x}: Draws a horizontal line from the current point, with the length of x in the direction defined by the sign of x. Lowercase x indicates a relative coordinate.
  • v{y}: Draws a vertical line from the current point, with the length of y in the direction defined by the sign of y. Lowercase y indicates a relative coordinate.

This markup is much more terse than the previous one (line breaks and indentation whitespace is only for readability). And, hey, we’ve managed to cut out half of the initial size, arriving at 100 bytes. Still, something makes me feel like this could be smaller…

Tile revision (-38B, 62B total)

Doesn’t our pattern tile have repeating parts? It’s clear that in the first row a whole brick is repeated, but what about the second row? It’s a bit harder to see, but if we cut the middle brick in half it becomes obvious.

The left half preceding the red line is the same as the right side.

Well, the middle brick isn’t exactly cut in half. There’s a slight offset because we also have to account for the gap. Anyways, we just found a simpler base tile pattern, which means fewer bytes! This also means we have to halve the width of our <pattern> element from 220 to 110.

<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse">
  <!-- pattern content here -->
</pattern>

Now let’s see how the simplified tile is drawn with <path>:

<path d="M0 0h100v30h-100z
         M0 40h45v30h-45z
         M55 40h55v30h-55z"/>

The size is reduced to 62 bytes, which is already less than a third of the original size! But why stop here when there’s even more we can do!

Shortening path commands (-9B, 53B total)

It’s worth getting a little deeper into the <path> element because it provides more hints for optimizing SVG patterns. One misconception I’ve had when working with <path> is regarding how the fill attribute works. Having played a lot with MS Paint in my childhood, I’ve learned that any shape I want to fill with a solid color has to be closed, i.e. have no open points. Otherwise, the paint will leak out of the shape and spill over everything.

In SVG, however, this is not true. Let me quote the spec itself:

The fill operation fills open subpaths by performing the fill operation as if an additional “closepath” command were added to the path to connect the last point of the subpath with the first point of the subpath.

This means we can omit the close path commands (z), because the subpaths are considered automatically closed when filled.

Another useful thing to know about path commands is that they come in uppercase and lowercase variations. Lowercase letters mean that relative coordinates are used; uppercase letters mean absolute coordinates are used instead.

It’s a little trickier than that with the H and V commands because they only include one coordinate. Here’s how I would describe these two commands:

  • H{x}: Draws a horizontal line from the current point to coordinate x.
  • V{y}: Draws a vertical line from the current point to coordinate y.

When we are drawing the first brick in the pattern tile, we start from the (0,0) coordinates. We then draw a horizontal line to (100,0) and a vertical line to (100,30), and finally, draw a horizontal line to (0,30). We used the h-100 command in the last line, but it is the equivalent of H0, which is two bytes instead of five. We can replace two similar occurrences and pare the code of our <path> down to this:

<path d="M0 0h100v30H0
         M0 40h45v30H0
         M55 40h55v30H55"/>

Another 9 bytes shaved off — how much smaller can we go?

Bridging (-5B, 48B total)

The longest commands standing in our way of a fully-optimized SVG pattern are the “move to” commands which take up 4, 5, and 6 bytes, respectively. One constraint we have is that:

A path data segment (if there is one) must begin with a “moveto” command.

But that’s okay. The first one is the shortest anyways. If we swap the rows, we can come up with a path definition where we only have to move either horizontally or vertically between the bricks. What if we could use the h and v commands there instead of M?

The path starts from the red dot in the top-left corner. Red are the path commands supported with arrows, black are the coordinates the arrows point to.

The above diagram shows how the three shapes can be drawn with a single path. Note that we are leveraging the fact that the fill operation automatically closes the open part between (110,0) and (0,0). With this rearrangement, we also moved the gap to the left of the full-width brick in the second row. Here’s how the code looks, still broken into one brick per line:

<path d="M0 0v30h50V0
         h10v30h50
         v10H10v30h100V0"/>

Surely, we’ve found the absolute smallest solution now that we’re down to 48 bytes, right?! Well…

Digit trimming (-4B, 44B total)

If you can be a bit flexible with the dimensions, there’s another little way we can optimize SVG patterns. We’ve been working with a brick width of 100 pixels, but that’s three bytes. Changing it to 90 means one less byte whenever we need to write it. Similarly, we used a gap of 10 pixels — but if we change it to 8 instead, we save a byte on each of those occurrences.

<path d="M0 0v30h45V0
         h8v30h45
         v8H8v30h90V0"/>

Of course, this also means we have to adjust the pattern dimensions accordingly. Here’s the final optimized SVG pattern code:

<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse">
  <path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>

The second line in the above snippet — not counting the indentations — is 44 bytes. We got here from 197 bytes in six iterations. That’s a chunky 77.7% size reduction!

I’m wondering though… is this really the smallest size possible? Have we looked at all possible ways to optimize SVG patterns?

I invite you to try and further minify this code, or even experiment with alternative methods for optimizing SVG patterns. I would love to see if we could find the true global minimum with the wisdom of the crowd!

More on creating and optimizing SVG patterns

If you are interested to learn more about creating and optimizing SVG patterns, read my article about creating patterns with SVG filters. Or, if you want to check out a gallery of 60+ patterns, you can view the PetitePatterns CodePen Collection. Lastly, you’re welcome to watch my tutorials on YouTube to help you get even deeper into SVG patterns.


Optimizing SVG Patterns to Their Smallest Size originally published on CSS-Tricks. You should get the newsletter.

Collective #703







What Is ARIA Even For?

Heydon Pickering’s amusing video about common pitfalls of using ARIA with the help of Ada Lovelace, a goat, and a dinosaur with a table for a head.

Watch it


Texture

Bring your generative work to life with three simple textures inspired by the natural world. By George Francis.

Check it out



Roland50.studio

Emulate the sound of Roland’s most famous and influential musical instruments from Yuri Suzuki and Roland.

Check it out










In Defense of Sass

Stephanie Eckles writes how organization, linting, and ease of theming are the primary reasons she’ll continue to use Sass.

Read it


Skateboard-Like Preloader

A fun preloader concept featuring a worm that does a flip when reaching the left or right sides of the ring. Made with SVG and CSS animation by Jon Kantner.

Check it out



SILVER FCTRY

Experience the playful and immersive AMBUSH® universe, in a virtual spaceship to take you on a journey to the other side.

Check it out



The post Collective #703 appeared first on Codrops.

Collective #701









Collective Item image 701

Huemint

Huemint uses machine learning to create unique color schemes for your brand, website or graphic.

Check it out






Collective Item image 701

Garbage

A beautifully made website to raise awareness on the global garbage problem.

Check it out












Collective Item image 701

A new year, a new MDN

The MDN website has changed to create a more holistic experience for its users. Hermina from Mozilla explains.

Read it




The post Collective #701 appeared first on Codrops.

Animate Anything Along an SVG Path

SVG is a very neat format to display any illustration, icon or logo on a website. Furthermore, they can be animated in CSS or JavaScript to make them more attractive. But SVG can also be used for their data only, without the visual! Let me explain…

An SVG is a vector image format, which means it is not made of coloured pixels, but math functions that, once interpreted, can be rendered on screen. Since the browser must convert the file from functions to actual pixels, it also let us access a wide variety of methods to either manipulate, or retrieve data from the math.

In today’s article, we will explore the function getPointAtLength() and see how we can use the data of an SVG path for creative use cases such as the demo below.

See the Pen Random dots along path – CodePen Challenge by Louis Hoebregts (@Mamboleoo) on CodePen.

⚠ If you are not familiar with SVG, this CSS-Tricks article is a good start!

The method getPointAtLength()

If we take a look at the MDN documentation page about the method it says:
The SVGGeometryElement.getPointAtLength() method returns the point at a given distance along the path.

The method will give us the coordinates of a point that is precisely along the path at a specific distance that we send as a parameter.

For example path.getPointAtLength(10) will return an SVGPoint (an object) with x & y coordinates.

Since we need to give the distance of our point, it means we will most likely need to know how long is our path. Luckily, the SVG API has a method getTotalLength() available to any SVGGeometryElement that returns the total length of our element!

⚠ The SVGGeometryElement variable refers to all SVG tags that are created from a geometry (path, rect, circle,…) so this does not include image, filter, clip-path,… tags.

Let’s see how we can then use this function to animate a circle along a given path using the GreenSock library.

To do so, we need a JavaScript object that will contain the animated values (as gsap cannot animate number variables directly) and set a property distance to zero.
We then create a tween that will update the distance value from 0 to the total length of our path.
Finally on each frame, we retrieve a point along the path based on the animated distance value, and we update the cx and cy attributes of our circle to make it move ✨

// Create an object that gsap can animate
const val = { distance: 0 };
// Create a tween
gsap.to(val, {
  // Animate from distance 0 to the total distance
  distance: path.getTotalLength(),
  // Function call on each frame of the animation
  onUpdate: () => {
    // Query a point at the new distance value
    const point = path.getPointAtLength(val.distance);
    // Update the circle coordinates
    circle.setAttribute('cx', point.x);
    circle.setAttribute('cy', point.y);
  }
});

See the Pen Animate single element along path by Louis Hoebregts (@Mamboleoo) on CodePen.

⚠ If the effect you want to achieve is just animating one element along an SVG path such as in the demo above, you could check the MotionPathPlugin by GreenSock. It will let you animate easily any DOM element from a path you provide. (plus it’s free!)

Using the points coordinates for particles

I love particles, it’s no breaking news. Which is why, when I learn a new technique I always try to implement something with them!
Let’s see how instead of a single circle moving along a path, we could make many more circles exploding like a bomb fuse 💣

The overall logic of this animation is exactly the same as before, except that on each frame we will create a new circle element and animate it. As you can see, the setup is very similar.

const svg = document.querySelector('svg');
const fuse = svg.querySelector('.fuse');

const val = { distance: 0 };
gsap.to(val, {
  distance: fuse.getTotalLength(),
  repeat: -1,
  duration: 5,
  onUpdate: () => {
    // Query a point at the new distance value
    const point = fuse.getPointAtLength(val.distance);
    // Create a new particle
    createParticle(point);
  }
});

The createParticle function will be called on each frame to make a new particle pop and fade out. Here are the steps of the animation:

  1. Create a new circle element and append it to the SVG
  2. Set the coordinates from the point we calculated with getPointAtLength
  3. Define a random radius and color for each
  4. Animate that particle cx & cy attributes to a random position
  5. Once the animation is complete, remove the particle from the DOM
function createParticle (point) {
  // Create a new circle element
  const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
  // Prepend the element to the SVG
  svg.prepend(circle);
  // Set the coordinates of that circle
  circle.setAttribute('cx', point.x);
  circle.setAttribute('cy', point.y);
  // Define a random radius for each circle
  circle.setAttribute('r', (Math.random() * 2) + 0.2);
  // Define a random color
  circle.setAttribute('fill', gsap.utils.random(['#ff0000', '#ff5a00', '#ff9a00', '#ffce00', '#ffe808']));
  
  // Animate the circle
  gsap.to(circle, {
    // Random cx based on its current position
    cx: '+=random(-20,20)',
    // Random cy based on its current position
    cy: '+=random(-20,20)',
    // Fade out
    opacity: 0,
    // Random duration for each circle
    duration: 'random(1, 2)',
    // Prevent gsap from rounding the cx & cy values
    autoRound: false,
    // Once the animation is complete
    onComplete: () => {
      // Remove the SVG element from its parent
      svg.removeChild(circle);
    }
  });
}

See the Pen Bomb fuse particles by Louis Hoebregts (@Mamboleoo) on CodePen.

⚠ To make the animation prettier I’m also animating the stroke-dashoffset of the fuse to make it more realistic. You can check this article for more details on such animation.

Using the coordinates in WebGL

So far we have only animated SVG elements next to the path. But sometimes all we need are the raw coordinates from the path, but not the path itself. For example, if we want to animate particles in a 2D Canvas or in WebGL like in the animation below.

See the Pen Make it POP! by Louis Hoebregts (@Mamboleoo) on CodePen.

⚠ The following chapter expects you to know how to setup and run a three.js scene.

Here are the key concepts of this animation:

  1. Get the path and its total length
  2. Loop along the path until you reach its length
  3. Get the point that exists on the index distance
  4. On each iteration, create a Vector3 at the point’s coordinates
  5. Push the vector into an array of vertices
  6. Create a geometry from the vertices
  7. Create a Points mesh and add it into your scene
// Get the Path in the DOM
const path = document.querySelector("path");
// Store the total length of the path
const length = path.getTotalLength();

// Empty array to store all vertices
const vertices = [];
// Loop along the path
for (let i = 0; i < length; i += 0.2) {
  // Get the coordinates of a point based on the index value
  const point = path.getPointAtLength(i);
  // Create a new vector at the coordinates
  const vector = new THREE.Vector3(point.x, -point.y, 0);
  // Randomize a little bit the point to make the heart fluffier
  vector.x += (Math.random() - 0.5) * 30;
  vector.y += (Math.random() - 0.5) * 30;
  vector.z += (Math.random() - 0.5) * 70;
  // Push the vector into the array
  vertices.push(vector);
}
// Create a new geometry from the vertices
const geometry = new THREE.BufferGeometry().setFromPoints(vertices);
// Define a pink material
const material = new THREE.PointsMaterial( { color: 0xee5282, blending: THREE.AdditiveBlending, size: 3 } );
// Create a Points mesh based on the geometry and material
const particles = new THREE.Points(geometry, material);
// Offset the particles in the scene based on the viewbox values
particles.position.x -= 600 / 2;
particles.position.y += 552 / 2;
// Add the particles in to the scene
scene.add(particles);

See the Pen Untitled by Louis Hoebregts (@Mamboleoo) on CodePen.

Creating a heart of particles is fun, but animating those particles is even FUNNIER 🥳

First we setup a global timeline so that all tweens will be grouped together and we’ll be able to repeat all of them once the last animation is complete.

// Create a global gsap timeline that contains all tweens
const tl = gsap.timeline({
  repeat: -1,
  yoyo: true
});

While creating the vector, we attach a gsap tween on it so that it animates from the center of the heart. We can calculate the x & y coordinates for the start based on the SVG viewBox attributes viewBox="0 0 600 552".
Since y axis are in the other direction in SVG, we apply a negative value. (y is going up in WebGL, while in CSS & SVG it’s going down).

Each vector’s animation will have a delay that is calculated from its own distance along the path so that will create a nice flow of particles going round.

for (let i = 0; i < length; i += 0.1) {
  [...]
  // Create a tween for that vector
  tl.from(vector, {
      x: 600 / 2, // Center X of the heart
      y: -552 / 2, // Center Y of the heart
      z: 0, // Center of the scene
      ease: "power2.inOut",
      duration: "random(2, 5)" // Random duration
    },
    i * 0.002 // Delay calculated from the distance along the path
  );
}

Finally, we need to update the geometry of the Points mesh on each frame as the Vector3 objects are being animated but the geometry is not aware of that.

function render() {
  requestAnimationFrame(render);
  // Update the geometry from the animated vertices
  geometry.setFromPoints(vertices);
}

And voilà 💖

See the Pen Create word from SVG Path – WebGL by Louis Hoebregts (@Mamboleoo) on CodePen.

What’s next?

You tell me! Now that you can extract the coordinates of points along an SVG path, try to apply those data anywhere else 🚀
What about animating lines instead of particles? Or create a new path based on random points that you pick along another one?

Explore this technique and share your results with me on Twitter, I can’t wait to see what you’ll come up with 💙

See you another time,
Mamboleoo

The post Animate Anything Along an SVG Path appeared first on Codrops.

Freebie: Wayfinding Icon Sets (164 Icons, PNG, SVG, AI, EPS)

Whenever we visit new places, we often need to quickly navigate and find the right direction. As a result, wayfinding has become an integral part of modern architecture. Navigation can be accompanied by icons for intuitive perception, and if you want to help people navigate more effectively, then intuitive visuals are a great place to start.

There are three navigation kits developed by GraphicSurf that can be used in various places, e.g. in exhibitions, concerts and museums. Each of the sets presented here includes basic navigation in the form of directional arrows, elevators, escalators, and even ramps for people with limited mobility. There are also fire safety icons denoting fire extinguishing hydrants and a medical office. Each set also includes specialized icons that can be used to identify specific areas of a particular event. Enjoy!

Editor’s Note: Please make sure to check the license details. No attribution is required, however, reselling of bundles or individual icons is not cool. If you want to spread the word in blog posts or anywhere else, feel free to do so, but please remember to credit the designers and provide a link to this article.

Wayfinding Icon Set “Exhibition”

Download The “Exhibition” Icon Set

Wayfinding Icon Set “Museum”

Download The “Museum” Icon Set

Wayfinding Icon Set “Concert”

Download The “Concert” Icon Set

Editor’s Note: A big thank you to the good folks at GraphicSurf — we sincerely appreciate your time and efforts. Keep up the brilliant work!

More Freebies On Smashing Magazine

Collective #689



Codrops Collective 687 image

Our Sponsor

Black Friday is Coming

Over $1,000,000 worth of free prizes, free bonus gifts, dozens of exclusive discounts and perks from our partners, and our biggest discount ever on Divi memberships and upgrades (plus tons of discounts in the Divi Marketplace)!

Check it out





Codrops Collective 689 item image

Tiny UI Toggle

Toggle the state of a UI element to easily create components e.g. collapse, accordion, tabs, dropdown, dialog/modal.

Check it out







Codrops Collective 689 item image

AppFlowy.IO

AppFlowy is an open source alternative to Notion where you are in charge of your
data and customizations.

Check it out




Codrops Collective 689 item image

State of CSS 2021

Philip Jägenstedt gives an overview of the State of CSS 2021 survey results and how they will influence priorities in 2022.

Check it out


Codrops Collective 689 item image

Tamagui

In case you didn’t know about it: Universal React design systems that optimize for native and web.

Check it out


Codrops Collective 689 item image

Caffeine

A very basic REST service for JSON data – enough for prototyping and MVPs.

Check it out



Codrops Collective 689 item image

Backgrounds

In this “Learn CSS!” module you’ll learn how you can style backgrounds of boxes using CSS.

Check it out







The post Collective #689 appeared first on Codrops.

Which SVG technique performs best for way too many icons?

Tyler Sticka digs in here in the best possible way: by making a test page and literally measuring performance. Maybe 1,000 icons is a little bit of an edge case, but hey, 250 rows of data with four icons in each gets you there. Tyler covers the nuances carefully in the post. The different techniques tested: inline <svg>, same-document sprite <symbol>s, external-document sprite <symbol>s, <img> with an external source, <img> with a data URL, <img> with a filter, <div> with a background-image of an external source, <div> with a background-image of a data URL, and a <div> with a mask. Phew! That’s a lot — and they are all useful techniques in their own right.

Which technique won? Inline <svg>, unless the SVGs are rather complex, then <img> is better. That’s what I would have put my money on. I’ve been on that train for a while now.

To Shared LinkPermalink on CSS-Tricks


The post Which SVG technique performs best for way too many icons? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Collective #688



Codrops Collective 687 image

Our Sponsor

Black Friday is Coming

Any time between now and Nov 26th you can enter to win a brand new MacBook Pro. Entering is completely free and the more raffle tickets you submit the better chance you have to win!

Enter now
















Collective 688 item image

Rows

Spreadsheets with built-in integrations from your business apps and team support.

Check it out




Collective 688 item image

Unocss

In case you didn’t know about it: The instant on-demand Atomic CSS engine inspired by Windi CSS, Tailwind CSS, Twind.

Check it out



The post Collective #688 appeared first on Codrops.

A Guide To Modern CSS Colors With RGB, HSL, HWL, LAB and LCH

There’s more to color on the web than meets the eye, and it’s about to get a lot more interesting! Today, we’ll take a look at the best ways to use colors in a design system, and what we can expect from our colors in the not-too-distant future.

Well-Known Color Values

There are many different ways to define colors in CSS. CSS named colors are one of the simplest ways to color an element:

.my-element {
  background-color: red;
}

These are very limited, and rarely fit the designs we are building! We could also use color hex (hexadecimal) values. This code gives our element a red background color:

.my-element {
  background-color: #ff0000;
}

Unless you’re a color expert, hex values are very difficult to read. It’s unlikely you would be able to guess the color of an element by reading the hex value. When building a website we might be given a hex color value by a designer, but if they asked us to make it, say 20% darker, we would have a hard time doing that by adjusting the hex value, without a visual guide or color picker.

RGB

RGB (red, green, blue) notation is an alternative way of writing colors, giving us access to the same range of colors as hex values, in a much more readable form. We have an rgb() function in CSS for this. Colors on the web are additive, meaning the higher the proportion of red, green and blue, the lighter the resulting color will be. If we only use the red channel, the result is red:

.my-element {
  background-color: rgb(255, 0, 0);
}

Setting the red, green and blue channels to the highest value will result in white:

.my-element {
  background-color: rgb(255, 255, 255);
}

We can also add an alpha channel (for transparency), by using the rgba() function:

.my-element {
  background-color: rgba(255, 0, 0, 0.5); // transparency of 50%
}

.my-element {
  background-color: rgba(255, 0, 0, 1); // fully opaque
}

rgb() and rgba() allow us to “mix” colors in our code to some extent, but the results can be somewhat unpredictable.

HSL

More recently, we have been able to use HSL (hue, saturation, lightness) values, with the hsl() and hsla() color functions. As a developer, these are far more intuitive when it comes to adjusting color values. For example, we can get darker and lighter variants of the same color by adjusting the lightness parameter:

.my-element {
  background-color: hsl(0deg, 100%, 20%); // dark red
}

.my-element {
  background-color: hsl(0deg, 100%, 50%); // medium red
}

.my-element {
  background-color: hsl(0deg, 100%, 80%); // light red
}

The hue parameter represents the position on a color wheel, and can be any value between 0 and 360deg. The function also accepts turn units (e.g. 0.5turn), and unitless values.

The following are all valid:

.my-element {
  background-color: hsl(180deg, 50%, 50%);
}

.my-element {
  background-color: hsl(0.5turn, 50%, 50%);
}

.my-element {
  background-color: hsl(180, 50%, 50%);
}

Tip: Holding down SHIFT and clicking the color swatch in the inspector in Chrome and Firefox dev tools will toggle the color value between hex, RGB and HSL!

hsl() and hsla() lend themselves well to manipulation with custom properties, as we’ll see shortly.

currentColor

The currentColor keyword is worth a mention as another way of setting a color on an element that’s been around for a while. It effectively allows us to use the current text color of an element as a variable. It’s pretty limited when compared with custom properties, but it’s often used for setting the fill color of SVG icons, to ensure they match the text color of their parent. Read about it here.

Modern Color Syntax

The CSS Color Module Level 4 provides us with a more convenient syntax for our color functions, which is widely supported in browsers. We no longer need the values to be comma-separated, and the rgb() and hsl() functions can take an optional alpha parameter, separated with a forward slash:

.my-element {
  /* optional alpha value gives us 50% opacity */
  background-color: hsl(0 100% 50% / 0.5);
}

.my-element {
  /* With no alpha value the background is fully opaque*/
  background-color: hsl(0 100% 50%);
}
New CSS Color Functions

HWB

HWB stands for hue, whiteness and blackness. Like HSL, the hue can be anywhere within a range of 0 to 360. The other two arguments control how much white or black is mixed into that hue, up to 100% (which would result in a totally white or totally black color). If equal amounts of white and black are mixed in, the color becomes increasingly gray. We can think of this as being similar to mixing paint. It could be especially useful for creating monochrome color palettes

Try it out with this demo (works in Safari only):

Why do we need LAB and LCH when we have HSL? One reason is that using LAB or LCH, gives us access to a much larger range of colors. LCH and LAB are designed to give us access to the entire spectrum of human vision. Furthermore, HSL and RGB have a few shortcomings: they are not perceptually uniform and, in HSL, increasing or decreasing the lightness has quite a different effect depending on the hue.

In this demo, we can see a stark contrast between LCH and HSL by hitting the grayscale toggle. For the HSL hue and saturation strips, there are clear differences in the perceptual lightness of each square, even though the “lightness” component of the HSL function is the same! Meanwhile, the chroma and hue strips on the LCH side have an almost-uniform perceptual lightness.

We can also see a big difference when using LCH color for gradients. Both these gradients start and end with the same color (with LCH values converted to the HSL equivalents using this converter). But the LCH gradient goes through vibrant shades of blue and purple in the middle, whereas the HSL gradient looks muddier and washed-out by comparison.

LAB and LCH, while perhaps being syntactically a little less intuitive, behave in a way that makes more sense to the human eye. In her article, LCH color in CSS: what, why, and how?, Lea Verou explains in detail the advantages of LCH color. She also built this LCH color picker.

As with other color functions, hwb(), lab() and lch() can also take an optional alpha parameter.

.my-element {
  background-color: lch(80% 240 50 / 0.5); // Resulting color has 50% opacity
}
Browser Support And Color Spaces

hwb(), lab() and lch() are currently only supported in Safari. It’s possible to start using them straight away by providing a fallback for non-supporting browsers. Browsers that don’t support the color function will simple ignore the second rule:

.my-element {
  background-color: lch(55% 102 360);

  /* LCH color converted to RGB using Lea Verou’s tool: https://css.land/lch/ */
  background-color: rgb(98.38% 0% 53.33%);
}

If other styles depend on newer color functions being supported, we could use a feature query:

.my-element {
  display: none;
}

/* Only display this element if the browser supports lch() */
@supports (background-color: lch(55% 102 360)) {
  .my-element {
    display: block;
    background-color: lch(55% 102 360);
  }
}

It’s worth noting, as Lea explains in her article, that although modern screens are capable of displaying colors beyond RGB, most browsers currently only support colors within the sRGB color space. In the LAB color demo you might notice that moving the sliders beyond a certain point doesn’t actually affect the color, even in Safari where lab() and lch() are supported. Using values outside of the sRGB range will only have an effect when hardware and browsers advance sufficiently.

Safari now supports the color() function, which enables us to display colors in the P3 space, but these are limited to RGB colors for now, and don’t yet give us all the advantages of LAB and LCH.

.my-element {
  background: rgb(98.38% 0% 53.33%); // bright pink
  background: color(display-p3 0.947 0 0.5295); // equivalent in P3 color space
}

Recommended Reading: Wide Gamut Color in CSS with Display-P3” by Nikita Vasilyev

Accessibility

Once they are widely supported, perhaps LAB and LCH can help us choose more accessible color combinations. Foreground text should have the same contrast ratio with background colors with different hue or chroma values, as long as their lightness value remains the same. That’s certainly not the case at the moment with HSL colors.

Color Management

A wider range of color functions means we have more options when it comes to managing colors in our application. Often we require several variants of a given color in our design system, ranging from dark to light.

Custom Properties

CSS custom properties allow us to store values for reuse in our stylesheets. As they allow partial property values, they can be especially useful for managing and manipulating color values. HSL lends itself particularly well to custom properties, due to its intuitiveness. In the previous demo, I’m using them to adjust the hue for each segment of the color strip by calculating a --hue value based on the element’s index (defined in another custom property).

li {
  --hue: calc(var(--i) * (360 / 10));
  background: hsl(var(--hue, 0) 50% 45%);
}

We can also do things like calculate complementary colors (colors from opposite sides of the color wheel). Plenty has been written about this, so I won’t cover old ground here, but if you’re curious then Sara Soueidan’s article on color management with HSL is a great place to start.

Migrating From Hex/RGB To HSL

RGB colors might serve your needs up to a point, but if you need the flexibility to be able to derive new shades from your base color palette then you might be better off switching to HSL (or LCH, once supported). I would recommend embracing custom properties for this.

Note: There are plenty of online resources for converting hex or RGB values to HSL (here’s one example).

Perhaps you have colors stored as Sass variables:

$primary: rgb(141 66 245);

When converting to HSL, we can assign custom properties for the hue, saturation and lightness values. This makes it easy to create darker or lighter, more or less saturated variants of the original color.

:root {
  --h: 265;
  --s: 70%;
  --l: 50%;

  --primary: hsl(var(--h) var(--s) var(--l));
  --primaryDark: hsl(var(--h) var(--s) 35%);
  --primaryLight: hsl(var(--h) var(--s) 75%);
}

HSL can be incredibly useful for creating color schemes, as detailed in the article Building a Color Scheme by Adam Argyle. In the article he creates light, dark and dim color schemes, using a brand color as a base. I like this approach because it allows for some fine-grained control over the color variant (for example, decreasing the saturation for colors in the “dark” scheme), but still retains the big advantage of custom properties: updating the brand color in just one place will be carried through to all the color schemes, so it could potentially save us a lot of work in the future.

Sass Color Functions

When it comes to mixing and adjusting colors, Sass has provided color functions to enable us to do just this for many years. We can saturate or desaturate, lighten or darken, even mix two colors together. These work great in some cases, but they have some limitations: firstly, we can only use them at compile-time, not for manipulating colors live in the browser. Secondly, they are limited to RGB and HSL, so they suffer from the same issues of perceptual uniformity, as we can see in this demo, where a color is increasingly desaturated yet appears increasingly lighter when converted to grayscale.

To ensure that the lightness remains uniform, we could use custom properties with LCH in a similar way to HSL above.

li {
  --hue: calc(var(--i) * (360 / 10));
  background: lch(50% 45 var(--hue, 0));
}
Color Mixing And Manipulation

Color Mixing

One thing CSS doesn’t yet allow us to do is mix colors in the browser. That’s all about to change: the Level 5 CSS Color Specification (working draft) contains proposals for color mixing functions that sound rather promising. The first is the color-mix() function, which mixes two colors much like Sass’s mix() function. But color-mix() in CSS allows us to specify a color space, and uses the LCH by default, with superior mixing as a result. The colors don’t have to be LCH when passed in as arguments either, but the interpolation will use the specified color space. We can specify how much of each color to mix, similar to gradient stops:

.my-element {
  /* equal amounts of red and blue */
  background-color: color-mix(in lch, red, blue);
}

.my-element {
  /* 30% red, 70% blue */
  background-color: color-mix(in lch, red 30%, blue);
}

Color Contrast And Accessibility

color-contrast() is another proposed function, which really does have huge implications for picking accessible colors. In fact, it’s designed with accessibility in mind first and foremost. It permits the browser to pick the most appropriate value from a list, by comparing it with another color. We can even specify the desired contrast ratio to ensure our color schemes meet WCAG guidelines. Colors are evaluated from left to right, and the browser picks the first color from the list that meets the desired ratio. If no colors meet the ratio, the chosen color will be the one with the highest contrast.

.my-element {
  color: wheat;
  background-color: color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to AA);
}

Because this isn’t supported in any browsers right now, I’ve borrowed this example directly from the spec. when the browser evaluates the expression the resulting color will be darkgreen, as it is the first one that meets the AA contrast ratio when compared to wheat, the color of the text.

Browser Support

The Level 5 Color Specification is currently in Working Draft, meaning no browsers yet support the color-contrast() and color-mix() functions and their syntax is subject to change. But it certainly looks like a bright future for color on the web!

Environmental Impact Of Colors

Did you know that your chosen color palette can have an impact on how much energy your website uses? On OLED screens (which account for most modern TVs and laptops), darker colors will use significantly less energy than light colors — with white using the most energy, and black the least. According to Tom Greenwood, author of Sustainable Web Design, blue is also more energy-intensive than colors in the red and green areas of the spectrum. To reduce the environmental impact of your applications, consider a darker color scheme, using less blue, or enabling a dark-mode option for your users. As an added bonus, a more environmentally friendly choice of colors can also reduce the impact on the battery life of mobile devices.

Tools
  • Hexplorer, Rob DiMarzo
    Learn to understand hex colors with this interactive visualization.
  • LCH color picker, Lea Verou and Chris Lilley
    Get LCH colors and their RGB counterparts.
  • HWB color picker
    Visualize HWB colors and convert to HSL, RGB and hex.
  • Ally Color Tokens, Stephanie Eckles
    An accessible color token generator.
Resources

Collective #687



Codrops Collective 687 image

Our Sponsor

Black Friday is Coming

Any time between now and Nov 26th you can enter to win a brand new MacBook Pro. Entering is completely free and the more raffle tickets you submit the better chance you have to win!

Enter now


Codrops Collective 687 image

Layout patterns

A collection of layout patterns built using modern CSS APIs that will help you build common interfaces such as cards, dynamic grid areas, and full-page layouts.

Check it out


Codrops Collective 687 image

#Chatcontrol Explained

The EU is planning to allow messenger and e-mail providers to indiscriminately scan and detect all private communication for suspicious content. Read why this is a dangerous, anti-democratic plan that violates fundamental rights.

Check it out







Codrops Collective 687 image

Papyrus

With Papyrus you can create a privacy-first, beautiful, blazing-fast blog and newsletter.

Check it out








Codrops Collective 687 image

bbburst

A SVG generator that makes it easy to create fun and colorful SVG confetti from your favorite shapes. Perfect to add some pizzazz to your web design projects.

Check it out








The post Collective #687 appeared first on Codrops.

Favicons: How to Make Sure Browsers Only Download the SVG Version

Šime Vidas DM’d me the other day about this thread from subzey on Twitter. My HTML for favicons was like this:

<!-- Warning! Typo! -->
<link rel="icon" href="/favicon.ico" size="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">

The attribute size is a typo there, and should be sizes. Like this:

<!-- Correct -->
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">

And with that, Chrome no longer double downloaded both icons, and instead uses the SVG alone (as it should). Just something to watch out for. My ICO file is 5.8kb, so now that’s 5.8kb saved on every single uncached page load, which feels non-trivial to me.

Šime noted this in Web Platform News #42:

SVG favicons are supported in all modern browsers except Safari. If your website declares both an ICO (fallback) and SVG icon, make sure to add the sizes=any attribute to the ICO <link> to prevent Chrome from downloading and using the ICO icon instead of the SVG icon (see Chrome bug 1162276 for more info). CSS-Tricks is an example of a website that has the optimal icon markup in its <head> (three <link> elements, one each for favicon.ico, favicon.svg, and apple-touch-icon.png).

That note about CSS-Tricks is a bit generous in that it’s only correct because my incorrectness was pointed out ahead of time. I think the root of my typo was Andrey’s article, but that’s been fixed. Andrey’s article is still likely the best reference for the most practical favicon markup.


The post Favicons: How to Make Sure Browsers Only Download the SVG Version appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Freebie: COVID-19 Icon Set (56 Icons, AI, EPS, SVG, PNG)

In the fight against the COVID-19 pandemic, scientists are tirelessly working on vaccines and medicine against COVID-19 in laboratories. In the meantime, the entire world is waiting for good news while people worldwide are hardly waiting to unite and return to a peaceful life. For now, we can still only observe the regime of social distance, self-isolation and wearing masks.

The graphicsurf.com team wanted to contribute to the ongoing fight against COVID-19 and has designed a set of vector icons dedicated to the Coronavirus, or specifically, COVID-19. Anthony Tyschenko and Dennis Machuca took part in the design of the icons.

The goal was to save time for designers who create visual materials to inform people. The well-known stickers remind you to wear a mask and keep your distance. From information material for airports and train stations and simple pointers to help people find the next spot where they can get tested, to badges for hotels and restaurants that an object has been disinfected, and so many other use cases. Jump to the free icon pack.

A Little About The Design Progress

The complete design of icons, from the idea to the final product, took place in two stages. At the first stage, sketches were drawn on paper, and then transferred to digital format in the Paint Tool SAI 2.

Once the sketches of all the icons were ready, the work moved to Adobe Illustrator. And at this stage, the icons were polished and adjusted.

Flexible Customization

We tried very hard to design all icons with great attention to detail — but also with fast editing in mind. You can easily change the path, stroke width or corner radius of any icon right inside Adobe Illustrator within just a few seconds.

  • 56 icons in total
  • 4 file types (AI, EPS, SVG, PNG)
  • 3px editable stroke
  • 2 styles (v, filles outline)
  • 256×256px (PNG)
  • Smooth corners
  • Easy to edit and scale
  • Fully editable
  • Free for personal and commercial use, but please always provide an attribution.

Great Balance And Scalability

All of the icons are balanced among each other. You can control stroke weight and corner rounding at any scale with regular scaling (or use the Scale tool to get proportional scaling).

Download The Icon Set For Free

Editor’s Note: Give Credit Where Credit Is Due

Please note that this set is available for download on graphicsurf.com and is royalty-free for use in both personal and commercial projects.

You can find more details in the license. Please also remember to provide attribution, so if you would like to spread the word in blog posts or anywhere else, please do remember to credit the designers and provide a link to this article.

A big thank you to the creative folks at graphicsurf.com — we sincerely appreciate your time and efforts. Keep up the brilliant work!

Collective #680




Theatre.js

Theatre.js is a new hackable JavaScript animation library with visual tools. It animates the DOM, WebGL, and any other JavaScript variable.

Check it out









Open-Meteo

Open-Meteo offers free weather forecast APIs for open-source developers and non-commercial use. No API key is required.

Check it out


Mechanic

Mechanic is an open source framework that makes it easy to create custom, web-based design tools that export design assets right in your browser.

Check it out




Glass

Glass is a new subscription-based photo sharing app and community for professional and amateur photographers alike without dark patterns driving engagement.

Check it out



Standards

A new way to design brand guidelines. A product from Standards Manual. Apply for early access.

Check it out


Q1K3

An incredible Quake clone made by Dominic Szablewski for the 2021 js13k competition.

Check it out


The post Collective #680 appeared first on Codrops.

Collective #679



Collective 679 item image

Doodle Ipsum

The lorem ipsum of illustrations where you can simply customize your doodles, grab the code, and use them on your web prototypes, landing pages, or no-code tools.

Check it out


Collective 679 item image

Grainy Gradients

Jimmy Chion shows how to generate colorful noise to add texture to a gradient with only a small amount of CSS and SVG.

Read it




Collective 679 item image

WebGL-Memory

A WebGL-Memory tracker that you can add to your page before you initialize WebGL and then you can probe how much WebGL memory you’re using for a given context.

Check it out









Collective 679 item image

Bonsai

A Mac web-browser for research that helps programmers think clearly.

Check it out






Collective 679 item image

Paper

A collection of all paper sizes of the world as CSS variables with or without units (to use with CSS aspect-ratio).

Check it out





Collective 679 item image

Neurocracy

A sci-fi hypertext game: Solve a murder in a near future world by diving into the Wikipedia of that world.

Check it out

The post Collective #679 appeared first on Codrops.

Cutouts

Ahmad Shadeed dug into shape “cutouts” the other day. Imagine a shape with another smaller shape carved out of it. In his typical comprehensive way, Ahmad laid out the situation well—looking at tricky situations that complicate things.

The first thing I’d think of is CSS’ clip-path, since it has that circle() syntax that seems like it a good fit, but no!, we need the opposite of what clip-path: circle() does, as we aren’t drawing a circle to be the clipping path here, but drawing all the way around the shape and then up into that second smaller circle and back out, like a bite out of a cookie. That puts us in clip-path: path() territory, which mercifully exists, and yet!, doesn’t quite get there because the path() syntax in CSS only works with fixed-pixel units which is often too limiting in fluid width layouts.

So that puts us at clip-path: url("#my-path"); (referencing an <svg> path), which is exactly where Ahmad starts this journey. But then he explores other options like a clever use of mask-image and a direct use of SVG <mask> and <image>, which turns out to be the winner.

Ideas like this have a weird way of entering the collective front-end developer consciousness somehow. Jay wrote up a very similar journey of wanting to do a shape cutout. Again, the problem:

clip-path defines a visible region, meaning that if you want all but a tiny chunk of the button to be visible, you need to define a path or polygon which is the inverse of the original. Here’s a demo of what I mean, using Clippy:

Jay Freestone, “Cutouts with CSS Masks”

In this case, polygon() has potential because it supports % units for flexibility (also, don’t miss Ana’s idea where the unit types are mixed within the polygon for a some-fixed-some-fluid concept).

Jay’s conclusion is that SVG has the most benefits of all the options:

[…] my overall impression is that mask-composite remains the more flexible solution, since it becomes trivial to use any SVG shape as the mask, not just a triangle or a simple polygon. The likelihood is that you’ll want to simply export an SVG and drop it in. Engineering the inverse result as clip-path is likely to get pretty hairy quickly.

Direct Link to ArticlePermalink


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

Collective #674



Codrops Collective 674 item image

Our Sponsor

Be is Now The Fastest Way to Build a Website

“I have to say that the new live builder is an absolute masterpiece. So far I used elementor, but switching to your new builder will definitely affect the speed of my work and greater customer satisfaction. Love it!” – DinoMaron

Discover more




Codrops Collective 674 item image

CSS Transforms tutorial

In this blog post, Josh W Comeau takes a deep look at the “transform” property and shows some of the nifty things it can do.

Read it


Codrops Collective 674 item image

Foundations

An important message from Jeremy Keith: “[The web] is for everyone. Not just for everyone to consume, but for everyone to make.”

Read it


Codrops Collective 674 item image

Visualizing a codebase

How can we “fingerprint” a codebase to see its structure at a glance? Amelia Wattenberger explores ways to automatically visualize a GitHub repo.

Read it




Codrops Collective 674 item image

JSKIDPIX

A JavaScript implementation of “Kid Pix”, an old Mac drawing application for children.

Check it out












The post Collective #674 appeared first on Codrops.

Collective #672








Collective 672 item image

Slinkity

A build plugin that can extend any 11ty site for components, page transitions, and more.

Check it out




Collective 672 item image

Shapecatcher

With Shapecatcher you can search through a database of characters by simply drawing your character into a box. It can find the most similar character shapes for your drawing.

Check it out


Collective 672 item image

WCAG colour contrast ratio

Dan Hollick’s super interesting thread on how color contrast gets calculated and why the WCAG colour contrast ratio doesn’t always seem to work.

Check it out













The post Collective #672 appeared first on Codrops.

Collective #670





Codrops Collective 670

Building SDF fractal noise

Inigo Quilez wrote an article about natively building SDF fractal noise by iteratively blending spheres as opposed to traditional noise displacement.

Read it



Codrops Collective 670

gridless.design

A thesis that describes the problems with traditional design techniques when used to prepare web experiences and why the design grid is fundamentally flawed for crafting responsive, reusable interfaces.

Read it




Codrops Collective 670

Inline text editor

A free WYSIWYG editor with a clean UI and easy-to-use features to provide a simple and modern JavaScript rich text editor.

Check it out




Codrops Collective 670

Glass UI

Generate CSS and HTML components using the glassmorphism design specifications based on the Glass UI library.

Check it out






Codrops Collective 670

MeisterNote

MeisterNote is a beautiful, intuitive documentation software that helps teams write and organize information collaboratively.

Check it out




Codrops Collective 670

Lineicons

5000+ essential line icons available in WebFont, SVG, PNG, React, PNG, and PDF Files.

Check it out


Codrops Collective 670

Goomics

Take a look behind Google’s corporate curtain with a former employee’s critical comics.

Check it out



Codrops Collective 670

76.

A very nice demo where an image gets assembled on scroll. By ycw.

Check it out



The post Collective #670 appeared first on Codrops.