Better Context Menus With Safe Triangles

You’ve no doubt wrestled with menus that have nested menus before. I can’t count how many times I’ve hovered over a menu item that reveals another list of menu items, then tried to hover over that nested menu only to have the entire menu close on me.

That’s the setup for what I think is a pretty common issue when making menus — preventing nested menus from closing inadvertently. It’s not the users’ fault; leaving hover between menu levels is easy. It’s also not exactly the web’s fault; the menu is supposed to close if the pointer leaves the interactive area.

As long as the pointer is hovering over the SVG element, we have something we can use to maintain the nested menu’s open state.

Pointer Events

There are two steps we need to take to achieve this. First, we’ll create a “desired” path that connects our cursor to the submenu.

A triangular shape is the most straightforward path we can construct between a menu item and a nested menu. You can visualize what this triangle might look like in the image below. The green represents the safe area, indicating that it won’t trigger any onMouseLeave events. Conversely, the red area signifies that it will start the onMouseLeave event since we’re likely moving toward a sibling menu item.

I approached this by creating a SafeArea component in React that contains the SVG markup:

<svg
  style={{
    position: "fixed",
    width: svgWidth,
    height: submenuHeight,
    pointerEvents: "none",
    zIndex: 2,
    top: submenuY,
    left: mouseX - 2
  }}
  id="svg-safe-area"
>
  {/* Safe Area */}
  <path
    pointerEvents="auto"
    stroke="red"
    strokeWidth="0.4"
    fill="rgb(114 140 89 / 0.3)"
    d={
      `M 0, ${mouseY-submenuY} 
        L ${svgWidth},${svgHeight}
        L ${svgWidth},0 
        z`
    }
  />
</svg>

Also, to constantly update our safe triangle and position it appropriately, we need a mouse listener, specifically onmousemove. I relied on a React hook from Josh Comeau called useMousePosition in a useMousePosition.tsx file that provides the safe triangle component, designating the mouse position with mouseX and mouseY.

The Safe Triangle

The triangle is the SVG’s only path element. For this to work correctly, we must set the CSS pointer-events property to none, which we can do inline directly in the SVG. Then we set pointer-events to auto inline in the path element. This way, we stop propagating events when they are coming from the path element — the safe triangle — but not when events come from the SVG’s red area.

Let’s break down the path we are drawing, as it’s way more straightforward than it looks:

<path
  pointerEvents="auto"
  stroke="red"
  strokeWidth="0.4"
  fill="rgb(114 140 89 / 0.3)"
  d={
    `M 0, ${mouseY-submenuY} 
      L ${svgWidth},${svgHeight}
      L ${svgWidth},0 
      z`
  }
/>

We set the pointer-events property to auto to capture all mouse events, and it does not trigger the onMouseLeave event as long as the cursor is inside the path.

Next, we provide the path with some basic CSS styles for debugging purposes. This way, we can see the safe area while testing interactions.

The 0, ${mouseY-submenuY} part is the path’s starting point, designating the center of the SVG’s area.

Then we continue our path drawing with two lines: L ${svgWidth},${svgHeight} and L ${svgWidth},0. The former represents the first line (L) based on the SVG’s width and height, while the latter draws the second line (L) based on the SVG’s width.

The z part of the path is what makes everything work. z is what closes the path, making a straight line to the path’s starting point, preventing the need to draw a third line.

You can explore the path in more detail or adjust it using this SVG path editor.

There Are Some Gotchas

This is a relatively simple solution on purpose. There are some situations where this approach may be too simple, and you will need another creative solution, particularly if you’re not working in React like me.

For example, what if the user’s pointer moves diagonally and touches a different menu item? This approach does not capture that interaction to prevent the current nested menu from closing, but that might not be what you want it to do. Perhaps you want the nested menu to close and need to adjust the SVG with a different shape. An “easy” way to solve this is to debounce a cleanup function so that, on every mouse movement, you call the cleanup function. And after some number of milliseconds have passed without a mouse movement, you would remove the SVG element, and the sibling listeners would trigger as expected.

Another example is the navigation paths. A triangle is terrific but might not be the ideal shape for your menu and how it is designed. After doing some of my own tests, I’ve found that a curved path tends to be more effective, closer to Needle’s approach for a safe area:

Needle’s Context Menu Safe Area paths. (Large preview) Wrapping Up

As you now know, coming up with a solution for nested menus that reveal on hover is more of a challenge than it looks on the surface. Whether a hover-based approach and the clicks it saves are worth the additional considerations that make a better user experience versus a click-based approach is totally up to you. If you go with a menu that relies on a mouse hover to reveal a nested menu, you now have a resource that enhances its usability.

What about you? Is this a UX challenge you’ve struggled with? Have you attempted to solve it differently? Is the “safe triangle” concept effective for your particular use case? I’d love to know in the comments!

Recreating YouTube’s Ambient Mode Glow Effect

I noticed a charming effect on YouTube’s video player while using its dark theme some time ago. The background around the video would change as the video played, creating a lush glow around the video player, making an otherwise bland background a lot more interesting.

This effect is called Ambient Mode. The feature was released sometime in 2022, and YouTube describes it like this:

“Ambient mode uses a lighting effect to make watching videos in the Dark theme more immersive by casting gentle colors from the video into your screen’s background.”
— YouTube

It is an incredibly subtle effect, especially when the video’s colors are dark and have less contrast against the dark theme’s background.

Curiosity hit me, and I set out to replicate the effect on my own. After digging around YouTube’s convoluted DOM tree and source code in DevTools, I hit an obstacle: all the magic was hidden behind the HTML <canvas> element and bundles of mangled and minified JavaScript code.

Despite having very little to go on, I decided to reverse-engineer the code and share my process for creating an ambient glow around the videos. I prefer to keep things simple and accessible, so this article won’t involve complicated color sampling algorithms, although we will utilize them via different methods.

Before we start writing code, I think it’s a good idea to revisit the HTML Canvas element and see why and how it is used for this little effect.

HTML Canvas

The HTML <canvas> element is a container element on which we can draw graphics with JavaScript using its own Canvas API and WebGL API. Out of the box, a <canvas> is empty — a blank canvas, if you will — and the aforementioned Canvas and WebGL APIs are used to fill the <canvas> with content.

HTML <canvas> is not limited to presentation; we can also make interactive graphics with them that respond to standard mouse and keyboard events.

But SVG can also do most of that stuff, right? That’s true, but <canvas> is more performant than SVG because it doesn’t require any additional DOM nodes for drawing paths and shapes the way SVG does. Also, <canvas> is easy to update, which makes it ideal for more complex and performance-heavy use cases, like YouTube’s Ambient Mode.

As you might expect with many HTML elements, <canvas> accepts attributes. For example, we can give our drawing space a width and height:

<canvas width="10" height="6" id="js-canvas"></canvas>

Notice that <canvas> is not a self-closing tag, like an <iframe> or <img>. We can add content between the opening and closing tags, which is rendered only when the browser cannot render the canvas. This can also be useful for making the element more accessible, which we’ll touch on later.

Returning to the width and height attributes, they define the <canvas>’s coordinate system. Interestingly, we can apply a responsive width using relative units in CSS, but the <canvas> still respects the set coordinate system. We are working with pixel graphics here, so stretching a smaller canvas in a wider container results in a blurry and pixelated image.

The downside of <canvas> is its accessibility. All of the content updates happen in JavaScript in the background as the DOM is not updated, so we need to put effort into making it accessible ourselves. One approach (of many) is to create a Fallback DOM by placing standard HTML elements inside the <canvas>, then manually updating them to reflect the current content that is displayed on the canvas.

Numerous canvas frameworks — including ZIM, Konva, and Fabric, to name a few — are designed for complex use cases that can simplify the process with a plethora of abstractions and utilities. ZIM’s framework has accessibility features built into its interactive components, which makes developing accessible <canvas>-based experiences a bit easier.

For this example, we’ll use the Canvas API. We will also use the element for decorative purposes (i.e., it doesn’t introduce any new content), so we won’t have to worry about making it accessible, but rather safely hide the <canvas> from assistive devices.

That said, we will still need to disable — or minimize — the effect for those who have enabled reduced motion settings at the system or browser level.

requestAnimationFrame

The <canvas> element can handle the rendering part of the problem, but we need to somehow keep the <canvas> in sync with the playing <video>and make sure that the <canvas> updates with each video frame. We’ll also need to stop the sync if the video is paused or has ended.

We could use setInterval in JavaScript and rig it to run at 60fps to match the video’s playback rate, but that approach comes with some problems and caveats. Luckily, there is a better way of handling a function that must be called on so often.

That is where the requestAnimationFrame method comes in. It instructs the browser to run a function before the next repaint. That function runs asynchronously and returns a number that represents the request ID. We can then use the ID with the cancelAnimationFrame function to instruct the browser to stop running the previously scheduled function.

let requestId;

const loopStart = () => {
  /* ... */

  /* Initialize the infinite loop and keep track of the requestId */
  requestId = window.requestAnimationFrame(loopStart);
};

const loopCancel = () => {
  window.cancelAnimationFrame(requestId);
  requestId = undefined;
};

Now that we have all our bases covered by learning how to keep our update loop and rendering performant, we can start working on the Ambient Mode effect!

The Approach

Let’s briefly outline the steps we’ll take to create this effect.

First, we must render the displayed video frame on a canvas and keep everything in sync. We’ll render the frame onto a smaller canvas (resulting in a pixelated image). When an image is downscaled, the important and most-dominant parts of an image are preserved at the cost of losing small details. By reducing the image to a low resolution, we’re reducing it to the most dominant colors and details, effectively doing something similar to color sampling, albeit not as accurately.

Next, we’ll blur the canvas, which blends the pixelated colors. We will place the canvas behind the video using CSS absolute positioning.

And finally, we’ll apply additional CSS to make the glow effect a bit more subtle and as close to YouTube’s effect as possible.

HTML Markup

First, let’s start by setting up the markup. We’ll need to wrap the <video> and <canvas> elements in a parent container because that allows us to contain the absolute positioning we will be using to position the <canvas> behind the <video>. But more on that in a moment.

Next, we will set a fixed width and height on the <canvas>, although the element will remain responsive. By setting the width and height attributes, we define the coordinate space in CSS pixels. The video’s frame is 1920×720, so we will draw an image that is 10×6 pixels image on the canvas. As we’ve seen in the previous examples, we’ll get a pixelated image with dominant colors somewhat preserved.

<section class="wrapper">
  <video controls muted class="video" id="js-video" src="video.mp4"></video>
  <canvas width="10" height="6" aria-hidden="true" class="canvas" id="js-canvas"></canvas>
</section>
Syncing <canvas> And <video>

First, let’s start by setting up our variables. We need the <canvas>’s rendering context to draw on it, so saving it as a variable is useful, and we can do that by using JavaScript’s getCanvasContext function. We’ll also use a variable called step to keep track of the request ID of the requestAnimationFrame method.

const video = document.getElementById("js-video");
const canvas = document.getElementById("js-canvas");
const ctx = canvas.getContext("2d");

let step; // Keep track of requestAnimationFrame id

Next, we’ll create the drawing and update loop functions. We can actually draw the current video frame on the <canvas> by passing the <video> element to the drawImage function, which takes four values corresponding to the video’s starting and ending points in the <canvas> coordinate system, which, if you remember, is mapped to the width and height attributes in the markup. It’s that simple!

const draw = () => {
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
};

Now, all we need to do is create the loop that calls the drawImage function while the video is playing, as well as a function that cancels the loop.

const drawLoop = () => {
  draw();
  step = window.requestAnimationFrame(drawLoop);
};

const drawPause = () => {
  window.cancelAnimationFrame(step);
  step = undefined;
};

And finally, we need to create two main functions that set up and clear event listeners on page load and unload, respectively. These are all of the video events we need to cover:

  • loadeddata: This fires when the first frame of the video loads. In this case, we only need to draw the current frame onto the canvas.
  • seeked: This fires when the video finishes seeking and is ready to play (i.e., the frame has been updated). In this case, we only need to draw the current frame onto the canvas.
  • play: This fires when the video starts playing. We need to start the loop for this event.
  • pause: This fires when the video is paused. We need to stop the loop for this event.
  • ended: This fires when the video stops playing when it reaches its end. We need to stop the loop for this event.
const init = () => {
  video.addEventListener("loadeddata", draw, false);
  video.addEventListener("seeked", draw, false);
  video.addEventListener("play", drawLoop, false);
  video.addEventListener("pause", drawPause, false);
  video.addEventListener("ended", drawPause, false);
};

const cleanup = () => {
  video.removeEventListener("loadeddata", draw);
  video.removeEventListener("seeked", draw);
  video.removeEventListener("play", drawLoop);
  video.removeEventListener("pause", drawPause);
  video.removeEventListener("ended", drawPause);
};

window.addEventListener("load", init);
window.addEventListener("unload", cleanup);

Let’s check out what we’ve achieved so far with the variables, functions, and event listeners we have configured.

Creating A Reusable Class

Let’s make this code reusable by converting it to an ES6 class so that we can create a new instance for any <video> and <canvas> pairing.

class VideoWithBackground {
  video;
  canvas;
  step;
  ctx;

  constructor(videoId, canvasId) {
    this.video = document.getElementById(videoId);
    this.canvas = document.getElementById(canvasId);

    window.addEventListener("load", this.init, false);
    window.addEventListener("unload", this.cleanup, false);
  }

  draw = () => {
    this.ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
  };

  drawLoop = () => {
    this.draw();
    this.step = window.requestAnimationFrame(this.drawLoop);
  };

  drawPause = () => {
    window.cancelAnimationFrame(this.step);
    this.step = undefined;
  };

  init = () => {
    this.ctx = this.canvas.getContext("2d");
    this.ctx.filter = "blur(1px)";

    this.video.addEventListener("loadeddata", this.draw, false);
    this.video.addEventListener("seeked", this.draw, false);
    this.video.addEventListener("play", this.drawLoop, false);
    this.video.addEventListener("pause", this.drawPause, false);
    this.video.addEventListener("ended", this.drawPause, false);
  };

  cleanup = () => {
    this.video.removeEventListener("loadeddata", this.draw);
    this.video.removeEventListener("seeked", this.draw);
    this.video.removeEventListener("play", this.drawLoop);
    this.video.removeEventListener("pause", this.drawPause);
    this.video.removeEventListener("ended", this.drawPause);
  };
    }

Now, we can create a new instance by passing the id values for the <video> and <canvas> elements into a VideoWithBackground() class:

const el = new VideoWithBackground("js-video", "js-canvas");
Respecting User Preferences

Earlier, we briefly discussed that we would need to disable or minimize the effect’s motion for users who prefer reduced motion. We have to consider that for decorative flourishes like this.

The easy way out? We can detect the user’s motion preferences with the prefers-reduced-motion media query and completely hide the decorative canvas if reduced motion is the preference.

@media (prefers-reduced-motion: reduce) {
  .canvas {
    display: none !important;
  }
}

Another way we respect reduced motion preferences is to use JavaScript’s matchMedia function to detect the user’s preference and prevent the necessary event listeners from registering.

constructor(videoId, canvasId) {
  const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");

  if (!mediaQuery.matches) {
    this.video = document.getElementById(videoId);
    this.canvas = document.getElementById(canvasId);

    window.addEventListener("load", this.init, false);
    window.addEventListener("unload", this.cleanup, false);
  }
}
Final Demo

We’ve created a reusable ES6 class that we can use to create new instances. Feel free to check out and play around with the completed demo.

See the Pen Youtube video glow effect - dominant color [forked] by Adrian Bece.

Creating A React Component

Let’s migrate this code to the React library, as there are key differences in the implementation that are worth knowing if you plan on using this effect in a React project.

Creating A Custom Hook

Let’s start by creating a custom React hook. Instead of using the getElementById function for selecting DOM elements, we can access them with a ref on the useRef hook and assign it to the <canvas> and <video> elements.

We’ll also reach for the useEffect hook to initialize and clear the event listeners to ensure they only run once all of the necessary elements have mounted.

Our custom hook must return the ref values we need to attach to the <canvas> and <video> elements, respectively.

import { useRef, useEffect } from "react";

export const useVideoBackground = () => {
  const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
  const canvasRef = useRef();
  const videoRef = useRef();

  const init = () => {
    const video = videoRef.current;
    const canvas = canvasRef.current;
    let step;

    if (mediaQuery.matches) {
      return;
    }

    const ctx = canvas.getContext("2d");

    ctx.filter = "blur(1px)";

    const draw = () => {
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
    };

    const drawLoop = () => {
      draw();
      step = window.requestAnimationFrame(drawLoop);
    };

    const drawPause = () => {
      window.cancelAnimationFrame(step);
      step = undefined;
    };

    // Initialize
    video.addEventListener("loadeddata", draw, false);
    video.addEventListener("seeked", draw, false);
    video.addEventListener("play", drawLoop, false);
    video.addEventListener("pause", drawPause, false);
    video.addEventListener("ended", drawPause, false);

    // Run cleanup on unmount event
    return () => {
      video.removeEventListener("loadeddata", draw);
      video.removeEventListener("seeked", draw);
      video.removeEventListener("play", drawLoop);
      video.removeEventListener("pause", drawPause);
      video.removeEventListener("ended", drawPause);
    };
  };

  useEffect(init, []);

  return {
    canvasRef,
    videoRef,
  };
};

Defining The Component

We’ll use similar markup for the actual component, then call our custom hook and attach the ref values to their respective elements. We’ll make the component configurable so we can pass any <video> element attribute as a prop, like src, for example.

import React from "react";
import { useVideoBackground } from "../hooks/useVideoBackground";

import "./VideoWithBackground.css";

export const VideoWithBackground = (props) => {
  const { videoRef, canvasRef } = useVideoBackground();

  return (
    <section className="wrapper">
      <video ref={ videoRef } controls className="video" { ...props } />
      <canvas width="10" height="6" aria-hidden="true" className="canvas" ref={ canvasRef } />
    </section>
  );
};

All that’s left to do is to call the component and pass the video URL to it as a prop.

import { VideoWithBackground } from "../components/VideoWithBackground";

function App() {
  return (
    <VideoWithBackground src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
  );
}

export default App;
Conclusion

We combined the HTML <canvas> element and the corresponding Canvas API with JavaScript’s requestAnimationFrame method to create the same charming — but performance-intensive — visual effect that makes YouTube’s Ambient Mode feature. We found a way to draw the current <video> frame on the <canvas>, keep the two elements in sync, and position them so that the blurred <canvas> sits properly behind the <video>.

We covered a few other considerations in the process. For example, we established the <canvas> as a decorative image that can be removed or hidden when a user’s system is set to a reduced motion preference. Further, we considered the maintainability of our work by establishing it as a reusable ES6 class that can be used to add more instances on a page. Lastly, we converted the effect into a component that can be used in a React project.

Feel free to play around with the finished demo. I encourage you to continue building on top of it and share your results with me in the comments, or, similarly, you can reach out to me on Twitter. I’d love to hear your thoughts and see what you can make out of it!

References

6 Common SVG Fails (and How to Fix Them)

Someone recently asked me how I approach debugging inline SVGs. Because it is part of the DOM, we can inspect any inline SVG in any browser DevTools. And because of that, we have the ability to scope things out and uncover any potential issues or opportunities to optimize the SVG.

But sometimes, we can’t even see our SVGs at all. In those cases, there are six specific things that I look for when I’m debugging.

1. The viewBox values

The viewBox is a common point of confusion when working with SVG. It’s technically fine to use inline SVG without it, but we would lose one of its most significant benefits: scaling with the container. At the same time, it can work against us when improperly configured, resulting in unwanted clipping.

The elements are there when they’re clipped — they’re just in a part of the coordinate system that we don’t see. If we were to open the file in some graphics editing program, it might look like this:

Cat line art with part of the drawing outside the artwork area in Illustrator.
Screenshot of SVG opened in Illustrator.

The easiest way to fix this? Add overflow="visible" to the SVG, whether it’s in our stylesheet, inline on the style attribute or directly as an SVG presentation attribute. But if we also apply a background-color to the SVG or if we have other elements around it, things might look a little bit off. In this case, the best option will be to edit the viewBox to show that part of the coordinate system that was hidden:

Demo applying overflow="hidden" and editing the viewBox.

There are a few additional things about the viewBox that are worth covering while we’re on the topic:

How does the viewBox work?

SVG is an infinite canvas, but we can control what we see and how we see it through the viewport and the viewBox.

The viewport is a window frame on the infinite canvas. Its dimensions are defined by width and height attributes, or in CSS with the corresponding width and height properties. We can specify any length unit we want, but if we provide unitless numbers, they default to pixels.

The viewBox is defined by four values. The first two are the starting point at the upper-left corner (x and y values, negative numbers allowed). I’m editing these to reframe the image. The last two are the width and height of the coordinate system inside the viewport — this is where we can edit the scale of the grid (which we’ll get into in the section on Zooming).

Here’s simplified markup showing the SVG viewBox and the width and height attributes both set on the <svg>:

<svg viewBox="0 0 700 700" width="700" height="700">
  <!-- etc. -->
</svg>

Reframing

So, this:

<svg viewBox="0 0 700 700">

…maps to this:

<svg viewBox="start-x-axis start-y-axis width height">

The viewport we see starts where 0 on the x-axis and 0 on the y-axis meet.

By changing this:

<svg viewBox="0 0 700 700">

…to this:

<svg viewBox="300 200 700 700">

…the width and height remain the same (700 units each), but the start of the coordinate system is now at the 300 point on the x-axis and 200 on the y-axis.

In the following video I’m adding a red <circle> to the SVG with its center at the 300 point on the x-axis and 200 on the y-axis. Notice how changing the viewBox coordinates to the same values also changes the circle’s placement to the upper-left corner of the frame while the rendered size of the SVG remains the same (700×700). All I did was “reframe” things with the viewBox.

Zooming

We can change the last two values inside the viewBox to zoom in or out of the image. The larger the values, the more SVG units are added to fit in the viewport, resulting in a smaller image. If we want to keep a 1:1 ratio, our viewBox width and height must match our viewport width and height values.

Let’s see what happens in Illustrator when we change these parameters. The artboard is the viewport which is represented by a white 700px square. Everything else outside that area is our infinite SVG canvas and gets clipped by default.

Figure 1 below shows a blue dot at 900 along the x-axis and 900 along the y-axis. If I change the last two viewBox values from 700 to 900 like this:

<svg viewBox="300 200 900 900" width="700" height="700">

…then the blue dot is almost fully back in view, as seen in Figure 2 below. Our image is scaled down because we increased the viewBox values, but the SVG’s actual width and height dimensions remained the same, and the blue dot made its way back closer to the unclipped area.

Figure 1.
Figure 1
Figure 2

There is a pink square as evidence of how the grid scales to fit the viewport: the unit gets smaller, and more grid lines fit into the same viewport area. You can play with the same values in the following Pen to see that work in action:

2. Missing width and height

Another common thing I look at when debugging inline SVG is whether the markup contains the width or height attributes. This is no big deal in many cases unless the SVG is inside a container with absolute positioning or a flexible container (as Safari computes the SVG width value with 0px instead of auto). Excluding width or height in these cases prevents us from seeing the full image, as we can see by opening this CodePen demo and comparing it in Chrome, Safari, and Firefox (tap images for larger view).

Chrome
Safari
Firefox

The solution? Add a width or height, whether as a presentation attribute, inline in the style attribute, or in CSS. Avoid using height by itself, particularly when it is set to 100% or auto. Another workaround is to set the right and left values.

You can play around with the following Pen and combine the different options.

3. Inadvertent fill and stroke colors

It may also be that we are applying color to the <svg> tag, whether it’s an inline style or coming from CSS. That’s fine, but there could be other color values throughout the markup or styles that conflict with the color set on the <svg>, causing parts to be invisible.

That’s why I tend to look for the fill and stroke attributes in the SVG’s markup and wipe them out. The following video shows an SVG I styled in CSS with a red fill. There are a couple of instances where parts of the SVG are filled in white directly in the markup that I removed to reveal the missing pieces.

4. Missing IDs

This one might seem super obvious, but you’d be surprised how often I see it come up. Let’s say we made an SVG file in Illustrator and were very diligent about naming our layers so that you get nice matching IDs in the markup when exporting the file. And let’s say we plan to style that SVG in CSS by hooking into those IDs.

That’s a nice way to do things. But there are plenty of times where I’ve seen the same SVG file exported a second time to the same location and the IDs are different, usually when copy/pasting the vectors directly. Maybe a new layer was added, or one of the existing ones was renamed or something. Whatever the case, the CSS rules no longer match the IDs in the SVG markup, causing the SVG to render differently than you’d expect.

Underscores with numbers after the element IDs
Pasting Illustrator’s exported SVG file into SVGOMG.

In large SVG files we might find it difficult to find those IDs. This is a good time to open the DevTools, inspect that part of the graphic that’s not working, and see if those IDs are still matching.

So, I’d say it’s worth opening an exported SVG file in a code editor and comparing it to the original before swapping things out. Apps like Illustrator, Figma, and Sketch are smart, but that doesn’t mean we aren’t responsible for vetting them.

5. Checklist for clipping and masking

If an SVG is unexpectedly clipped and the viewBox checks out alright, I usually look at the CSS for clip-path or mask properties that might interfere with the image. It’s tempting to keep looking at the inline markup, but it’s good to remember that an SVG’s styling might be happening elsewhere.

CSS clipping and masking allow us to “hide” parts of an image or element. In SVG, <clipPath> is a vector operation that cuts parts of an image with no halfway results. The <mask> tag is a pixel operation that allows transparency, semi-transparency effects, and blurred edges.

This is a small checklist for debugging cases where clipping and masking are involved:

  • Make sure the clipping path (or mask) and the graphic overlap one another. The overlapping parts are what gets displayed.
  • If you have a complex path that is not intersecting your graphic, try applying transforms until they match.
  • You can still inspect the inner code with the DevTools even though the <clipPath> or <mask> are not rendered, so use it!
  • Copy the markup inside <clipPath> and <mask> and paste it before closing the </svg> tag. Then add a fill to those shapes and check the SVG’s coordinates and dimensions. If you still do not see the image, try adding overflow="hidden" to the <svg> tag.
  • Check that a unique ID is used for the <clipPath> or <mask>, and that the same ID is applied to the shapes or group of shapes that are clipped or masked. A mismatched ID will break the appearance.
  • Check for typos in the markup between the <clipPath> or <mask> tags.
  • fill, stroke, opacity, or some other styles applied to the elements inside <clipPath> are useless — the only useful part is the fill-region geometry of those elements. That’s why if you use a <polyline> it will behave as a <polygon> and if you use a <line> you won’t see any clipping effect.
  • If you don’t see your image after applying a <mask>, make sure that the fill of the masking content is not entirely black. The luminance of the masking element determines the opacity of the final graphic. You’ll be able to see through the brighter parts, and the darker parts will hide your image’s content.

You can play with masked and clipped elements in this Pen.

6. Namespaces

Did you know that SVG is an XML-based markup language? Well, it is! The namespace for SVG is set on the xmlns attribute:

<svg xmlns="http://www.w3.org/2000/svg">
  <!-- etc. -->
</svg>

There’s a lot to know about namespacing in XML and MDN has a great primer on it. Suffice to say, the namespace provides context to the browser, informing it that the markup is specific to SVG. The idea is that namespaces help prevent conflicts when more than one type of XML is in the same file, like SVG and XHTML. This is a much less common issue in modern browsers but could help explain SVG rendering issues in older browsers or browsers like Gecko that are strict when defining doctypes and namespaces.

The SVG 2 specification does not require namespacing when using HTML syntax. But it’s crucial if support for legacy browsers is a priority — plus, it doesn’t hurt anything to add it. That way, when the <html> element’s xmlns attribute is defined, it will not conflict in those rare cases.

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <svg xmlns="http://www.w3.org/2000/svg" width="700px" height="700px">
      <!-- etc. -->
    </svg>
  </body>
</html>

This is also true when using inline SVG in CSS, like setting it as a background image. In the following example, a checkmark icon appears on the input after successful validation. This is what the CSS looks like:

textarea:valid {
 background: white url('data:image/svg+xml,\
    <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26">\
    <circle cx="13" cy="13" r="13" fill="%23abedd8"/>\
    <path fill="none" stroke="white" stroke-width="2" d="M5 15.2l5 5 10-12"/>\
    </svg>') no-repeat 98% 5px;
}

When we remove the namespace inside the SVG in the background property, the image disappears:

Another common namespace prefix is xlink:href. We use it a lot when referencing other parts of the SVG like: patterns, filters, animations or gradients. The recommendation is to start replacing it with href as the other one is being deprecated since SVG 2, but there might be compatibility issues with older browsers. In that case, we can use both. Just remember to include the namespace xmlns:xlink="http://www.w3.org/1999/xlink" if you are still using xlink:href.

Level up your SVG skills!

I hope these tips help save you a ton of time if you find yourself troubleshooting improperly rendered inline SVGs. These are just the things I look for. Maybe you have different red flags you watch for — if so, tell me in the comments!

The bottom line is that it pays to have at least a basic understanding of the various ways SVG can be used. CodePen Challenges often incorporate SVG and offer good practice. Here are a few more resources to level up:

There are a few people I suggest following for SVG-related goodness:


6 Common SVG Fails (and How to Fix Them) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Collective #733




New patterns for amazing apps

Dive into a fantastic collection of new patterns for amazing apps, including clipboard patterns, file patterns, and advanced app patterns. By Thomas Steiner.

Read it















Satori

In case you didn’t know about it: a library to convert HTML and CSS to SVG.

Check it out




How I Made an Icon System Out of CSS Custom Properties

SVG is the best format for icons on a website, there is no doubt about that. It allows you to have sharp icons no matter the screen pixel density, you can change the styles of the SVG on hover and you can even animate the icons with CSS or JavaScript.

There are many ways to include an SVG on a page and each technique has its own advantages and disadvantages. For the last couple of years, I have been using a Sass function to import directly my icons in my CSS and avoid having to mess up my HTML markup.

I have a Sass list with all the source codes of my icons. Each icon is then encoded into a data URI with a Sass function and stored in a custom property on the root of the page.

TL;DR

What I have for you here is a Sass function that creates a SVG icon library directly in your CSS.

The SVG source code is compiled with the Sass function that encodes them in data URI and then stores the icons in CSS custom properties. You can then use any icon anywhere in your CSS like as if it was an external image.

This is an example pulled straight from the code of my personal site:

.c-filters__summary h2:after {
  content: var(--svg-down-arrow);
  position: relative;
  top: 2px;
  margin-left: auto;
  animation: closeSummary .25s ease-out;
}

Demo

Sass structure

/* All the icons source codes */
$svg-icons: (
  burger: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0...'
);

/* Sass function to encode the icons */
@function svg($name) {
  @return url('data:image/svg+xml, #{$encodedSVG} ');
}

/* Store each icon into a custom property */
:root {
  @each $name, $code in $svg-icons {
    --svg-#{$name}: #{svg($name)};
  }
}

/* Append a burger icon in my button */
.menu::after {
  content: var(--svg-burger);
}		

This technique has both pros and cons, so please take them into account before implementing this solution on your project:

Pros

  • There are no HTTP requests for the SVG files.
  • All of the icons are stored in one place.
  • If you need to update an icon, you don’t have to go over each HTML templates file.
  • The icons are cached along with your CSS.
  • You can manually edit the source code of the icons.
  • It does not pollute your HTML by adding extra markup.
  • You can still change the color or some aspect of the icon with CSS.

Cons

  • You cannot animate or update a specific part of the SVG with CSS.
  • The more icons you have, the heavier your CSS compiled file will be.

I mostly use this technique for icons rather than logos or illustrations. An encoded SVG is always going to be heavier than its original file, so I still load my complex SVG with an external file either with an <img> tag or in my CSS with url(path/to/file.svg).

Encoding SVG into data URI

Encoding your SVG as data URIs is not new. In fact Chris Coyier wrote a post about it over 10 years ago to explain how to use this technique and why you should (or should not) use it.

There are two ways to use an SVG in your CSS with data URI:

  • As an external image (using background-image,border-image,list-style-image,…)
  • As the content of a pseudo element (e.g. ::before or ::after)

Here is a basic example showing how you how to use those two methods:

The main issue with this particular implementation is that you have to convert the SVG manually every time you need a new icon and it is not really pleasant to have this long string of unreadable code in your CSS.

This is where Sass comes to the rescue!

Using a Sass function

By using Sass, we can make our life simpler by copying the source code of our SVG directly in our codebase, letting Sass encode them properly to avoid any browser error.

This solution is mostly inspired by an existing function developed by Threespot Media and available in their repository.

Here are the four steps of this technique:

  • Create a variable with all your SVG icons listed.
  • List all the characters that needs to be skipped for a data URI.
  • Implement a function to encode the SVGs to a data URI format.
  • Use your function in your code.

1. Icons list

/**
* Add all the icons of your project in this Sass list
*/
$svg-icons: (
  burger: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24.8 18.92" width="24.8" height="18.92"><path d="M23.8,9.46H1m22.8,8.46H1M23.8,1H1" fill="none" stroke="#000" stroke-linecap="round" stroke-width="2"/></svg>'
);

2. List of escaped characters

/**
* Characters to escape from SVGs
* This list allows you to have inline CSS in your SVG code as well
*/
$fs-escape-chars: (
  ' ': '%20',
  '\'': '%22',
  '"': '%27',
  '#': '%23',
  '/': '%2F',
  ':': '%3A',
  '(': '%28',
  ')': '%29',
  '%': '%25',
  '<': '%3C',
  '>': '%3E',
  '\\': '%5C',
  '^': '%5E',
  '{': '%7B',
  '|': '%7C',
  '}': '%7D',
);

3. Encode function

/**
* You can call this function by using `svg(nameOfTheSVG)`
*/
@function svg($name) {
  // Check if icon exists
  @if not map-has-key($svg-icons, $name) {
    @error 'icon “#{$name}” does not exists in $svg-icons map';
    @return false;
  }

  // Get icon data
  $icon-map: map-get($svg-icons, $name);

  $escaped-string: '';
  $unquote-icon: unquote($icon-map);
  // Loop through each character in string
  @for $i from 1 through str-length($unquote-icon) {
    $char: str-slice($unquote-icon, $i, $i);

    // Check if character is in symbol map
    $char-lookup: map-get($fs-escape-chars, $char);

    // If it is, use escaped version
    @if $char-lookup != null {
        $char: $char-lookup;
    }

    // Append character to escaped string
    $escaped-string: $escaped-string + $char;
  }

  // Return inline SVG data
  @return url('data:image/svg+xml, #{$escaped-string} ');
}		

4. Add an SVG in your page

button {
  &::after {
    /* Import inline SVG */
    content: svg(burger);
  }
}

If you have followed those steps, Sass should compile your code properly and output the following:

button::after {
  content: url("data:image/svg+xml, %3Csvg%20xmlns=%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox=%270%200%2024.8%2018.92%27%20width=%2724.8%27%20height=%2718.92%27%3E%3Cpath%20d=%27M23.8,9.46H1m22.8,8.46H1M23.8,1H1%27%20fill=%27none%27%20stroke=%27%23000%27%20stroke-linecap=%27round%27%20stroke-width=%272%27%2F%3E%3C%2Fsvg%3E ");
}		

Custom properties

The now-implemented Sass svg() function works great. But its biggest flaw is that an icon that is needed in multiple places in your code will be duplicated and could increase your compiled CSS file weight by a lot!

To avoid this, we can store all our icons into CSS variables and use a reference to the variable instead of outputting the encoded URI every time.

We will keep the same code we had before, but this time we will first output all the icons from the Sass list into the root of our webpage:

/**
  * Convert all icons into custom properties
  * They will be available to any HTML tag since they are attached to the :root
  */

:root {
  @each $name, $code in $svg-icons {
    --svg-#{$name}: #{svg($name)};
  }
}

Now, instead of calling the svg() function every time we need an icon, we have to use the variable that was created with the --svg prefix.

button::after {
  /* Import inline SVG */
  content: var(--svg-burger);
}

Optimizing your SVGs

This technique does not provide any optimization on the source code of the SVG you are using. Make sure that you don’t leave unnecessary code; otherwise they will be encoded as well and will increase your CSS file size.

You can check this great list of tools and information on how to optimize properly your SVG. My favorite tool is Jake Archibald’s SVGOMG — simply drag your file in there and copy the outputted code.

Bonus: Updating the icon on hover

With this technique, we cannot select with CSS specific parts of the SVG. For example, there is no way to change the fill color of the icon when the user hovers the button. But there are a few tricks we can use with CSS to still be able to modify the look of our icon.

For example, if you have a black icon and you want to have it white on hover, you can use the invert() CSS filter. We can also play with the hue-rotate() filter.

That’s it!

I hope you find this little helper function handy in your own projects. Let me know what you think of the approach — I’d be interested to know how you’d make this better or tackle it differently!


How I Made an Icon System Out of CSS Custom Properties originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

How to Easily Add Custom Code in WordPress (Without Breaking Your Site)

Often while reading WordPress tutorials, you may be asked to add custom code snippets in your theme’s functions.php file or in a site-specific plugin.

The problem is that even the slightest mistake can break your website.

In this article, we will show you an easy way to add custom code in WordPress without breaking your site.

How to easily add custom code in WordPress

The Problem with Custom Code Snippets (And How to Fix It)

Often you’ll find code snippets in WordPress tutorials with instructions to add them into your theme’s functions.php file or a site-specific plugin.

The biggest problem is that even a tiny mistake in the custom code snippet can break your WordPress site and make it inaccessible.

Not to mention, if you update your WordPress theme, then all your customizations get removed.

The other problem is that if you add multiple code snippets in a site-specific plugin, it can become hard to manage the file.

Luckily, there is an easier way for users to add and manage custom code snippets in WordPress.

WPCode is the most popular code snippets plugin used by over 1 million WordPress websites. It makes it easy to add code snippets in WordPress without having to edit your theme’s functions.php file.

WPCode Code Snippets Plugin

WPCode also makes it simple to add tracking codes for Google Analytics, Facebook Pixel, Google AdSense, and more to your site’s header and footer areas.

You’ll never have to worry about breaking your site because the smart code snippet validation helps you prevent common code errors.

In addition, WPCode comes with a built-in snippets library where you can find all of the most popular WordPress code snippets like disable REST API, disable comments, disable Gutenberg, allow SVG file uploads, and much more. This eliminates the need to install separate plugins for each feature request.

The best part is that you can manage all your code snippets from one central screen and add tags to organize them.

With that said, let’s take a look at how to easily add custom code snippets in WordPress with WPCode.

Adding Custom Code Snippets in WordPress

The first thing you need to do is install and activate the WPCode plugin on your website.

For more details, see our step by step guide on how to install a WordPress plugin.

Upon activation, the plugin will add a new menu item labeled ‘Code Snippets’ to your WordPress admin bar. Clicking on it will show you a list of all the custom code snippets you have saved on your site.

Since you just installed the plugin, your list will be empty.

Go ahead and click on the ‘Add New’ button to add your first custom code snippet in WordPress.

Click 'Add New' in WPCode to create a new custom snippet

This will bring you to the ‘Add Snippet’ page. Here you can choose a code snippet from the pre-made library or add your custom code.

To add custom code, click on the ‘Use snippet’ button underneath the ‘Add Your Custom Code (New Snippet)’ option.

Add custom code in WPCode

You need to start by entering a title for your custom code snippet. This could be anything that helps you identify the code.

After that, you can copy and paste your code snippet into the code box. Be sure to also select the correct code type from the drop-down menu on the right.

Adding your first code snippet

In the screenshot above, we have added a custom code snippet to remove the WordPress version number from our test site.

function wpb_remove_version() {
return '';
}
add_filter('the_generator', 'wpb_remove_version');

Below the code box, you will see insertion options. There are two main insertion options: Auto Insert and Shortcode (Default).

Choose insertion option for code snippet

If you chose the ‘Auto Insert’ method, the snippet will be automatically inserted and executed on your site.

You can automatically run the snippet only in the WordPress admin area, on the front-end of your site, or everywhere. If you are unsure, then select the default ‘Run snippet everywhere’ option.

With the ‘Shortcode’ method, the snippet is not automatically inserted. Once you save the snippet, you’ll get a shortcode specific to the snippet that you can use anywhere on your site.

When you scroll further down, you will see a ‘Basic info’ area. You can add anything here that helps you understand what this code does, where you found it, and why you are adding it to your website.

Add code description and tags

You can also assign tags to your code snippet. This will help you sort your code snippets by topic and functionality.

The priority field allows you to control the order in which the snippets are executed when you want to display multiple snippets in the same location. By default, all snippets get a priority of 10. If you want a snippet to display earlier than others, simply set the snippet priority to a lower number, like 5.

Lastly, you can use the powerful ‘Smart Conditional Logic’ section to either show or hide auto-inserted snippets based on a set of rules.

Use smart conditional logic to show or hide snippets

For example, you can show code snippets to logged-in users only, load code snippets only on specific page URLs, and more.

When you’re finished choosing options, you can click the ‘Save Snippet’ button in the top-right corner of the screen and toggle the switch from ‘Inactive’ to ‘Active.’

Save and activate code snippet

If you want to save the code snippet and not activate it, then simply click on the ‘Save Snippet’ button.

Once you have saved and activated the code snippet, it will be added to your site automatically, if that’s the insertion method you chose, or displayed as a shortcode.

Handling Errors in Custom Code

Often, if you make a mistake in adding the custom code in your site-specific plugin or theme file, then it would immediately make your site inaccessible.

You would start seeing a syntax error or a 500 internal server error on your site. To fix this you’ll need to manually undo your code using an FTP client.

The neat part about the WPCode plugin is that it will automatically detect a syntax error in the code and immediately deactivate it.

Error handling in your custom code snippet

It will also show you a helpful error message, so you can debug the error.

WPCode’s smart code snippet validation will also detect any errors as you’re adding your custom code.

Smart code snippet validation to find code errors

Hovering over the error will bring up instructions to help you fix it.

Managing Your Custom Code Snippets

WPCode plugin provides an easy user interface to manage your custom code snippets in WordPress.

You can save code snippets without activating them on your site, and then activate or deactivate the snippet at any time you want. It’s also possible to filter code snippets by type and location, and use tags to organize your code snippets easily.

WPCode - WordPress Snippets Organized by Tags

You can also export specific code snippets or bulk export all of them.

Simply go to Code Snippets » Tools and click on the ‘Export’ tab.

Export your custom code snippets

If you’re moving websites to a different server, you can easily import your code snippets to the new site.

Just visit the Code Snippets » Tools » Import page and upload the export file.

Import code snippets with WPCode

We hope this article helped you learn how to easily add custom code in WordPress. Want to experiment with some code snippets on your website? Check out our list of extremely useful tricks for the WordPress functions file, and don’t forget to see our ultimate guide to speeding up your WordPress site.

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

The post How to Easily Add Custom Code in WordPress (Without Breaking Your Site) first appeared on WPBeginner.

WordPress Performance Team Is Working on a Module for SVG Uploads

If you have ever tried to upload an SVG (Scalable Vector Graphics) file for a logo or favicon in WordPress, you may have been astonished to find that it doesn’t work. There is no core support so users rely on plugins like SVG Support (900K+ installs) or Safe SVG (600k+ installs) to upload SVG files to the media library and use them like any other image file.

The main advantage of SVG files is they can scale to any dimension without losing quality. They are also compact and SEO-friendly because they are stored in XML text files that can be crawled and indexed.

WordPress contributors have been discussing adding SVG support for more than nine years. It was talked to death with various proofs of concept and no clear path forward, and the ticket eventually became a place where people periodically stop by and comment that it’s “a goddamn tragedy that WordPress doesn’t support this yet.”

Despite the format having nearly universal support across the web, there are security concerns associated with scripted SVGs, where files might contain a malicious script. Without an SVG sanitizer library, users would be uploading SVG files at their own risk.

In the most recent Performance Team meeting, contributors proposed the idea of working on a new SVG uploads module. They are first aiming to allow users to upload SVG files without scripts and provide an SVG preview in the Media Library.

Although more than a million WordPress users have installed a plugin to add SVG uploads to their sites, none of these plugins are feature plugins. This is why the Performance Team has opted to create a module within the Performance Lab plugin. It allows the team to better test the feature in the wild before proposing it for WordPress core.

Looking at current implementations in plugins, the team found many of them offer more features than the scope of this module would include. The most popular plugins all currently use the SVG Sanitizer library, as does a Drupal module and an integration for TYPO3 CMS.

Performance team contributors have identified WordPress 6.2 as a realistic goal for proposing the module for core. Anyone who wants to contribute can join their efforts on GitHub.

How stroke-dasharray Patterns Work

Say you have a line in SVG:

<svg>
  <line x1="0" y1="30" x2="500" y2="30" stroke-color="#f8a100" />
</svg>

You can use the stroke-dasharray property in CSS to make dashes:

line {
  stroke-dasharray: 5;
}

That 5 value is a relative unit based on the size of the SVG’s viewBox. We could use any CSS length, really. But what it does is make a pattern of dashes that are 5 units long with 5 unit gaps between them.

So far, so good. We can use two values where the second value individually sets the gap length:

Now we have dashes that are 5 units and gaps that are 10. Let’s try a third value:

See how we’re forming a pattern here? It goes:

  • Dash: 5 units
  • Gap: 10 units
  • Dash: 15 units

You’d think it repeats after that in the exact same cadence. But no! It if did, we’d have dashes bumping into one another:

  • Dash: 5 units
  • Gap: 10 units
  • Dash: 15 units
  • Dash: 5 units
  • Gap: 10 units
  • Dash: 15 units
  • …and so on.

Instead, stroke-dasharray gets all smart and duplicates the pattern if there are an odd number of values So…

stroke-dasharray: 5 10 15;

/* is the same as */
stroke-dasharray: 5 10 15 5 10 15;

That’s actually why a single value works! Earlier, we declared a single 5 value. That’s really the same as saying stroke-dasharray: 5 5. Without the second value, stroke-dasharray implicitly duplicates the first value to get a repeatable pattern. Otherwise, it’d just be a solid line of dashes that are 5 units long, but no gaps between them!

The pattern also depends on the size of the shape itself. Our SVG line is 500 units. Let’s set larger stroke-dasharray values and add them up:

stroke-dasharray: 10 20 30 40 50; /* 150 units */

If the pattern runs four times (150 units ⨉ 4 iterations), we’re dealing with 600 total units. That additional 100 units is lopped off to prevent the pattern from overflowing itself.

That’s all.

🎩 Hat tip to Joshua Dance for calling this out!


How stroke-dasharray Patterns Work originally published on CSS-Tricks. You should get the newsletter.

SVG Loading Animations

Nobody likes to wait for your web page to load, so of course we want to make the time go by easier with animation. In this post we have provided you with some examples and code for ways to do this via SVG loading animations. Have a look and start using them in your projects today!

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


SVG Loader Animation

Here are 10 different examples, from the simple to the more complex.

See the Pen
SVG Loader Animation
by Nikhil Krishnan (@nikhil8krishnan)
on CodePen.0

Animated – SVG Loader

A clever change of pace from the rotating circle, this animation combines multiple circles rotating back and forth.

See the Pen
Animated – SVG Loader
by Steven Roberts (@matchboxhero)
on CodePen.0

SVG Page Load Animations

Three of the more typical, simple loading animations.

See the Pen
SVG Page Load Animations
by Bridget Reed (@BridgetCReed)
on CodePen.0

SVG Loader

Here’s a complex, very specific loader that you could use all or parts of to make it your own.

See the Pen
SVG Loader
by Swarup Kumar Kuila (@uiswarup)
on CodePen.0

Animated SVG Loader

This is a fun, somewhat mesmerizing loader with several moving parts.

See the Pen
Animated SVG Loader
by Tony (@thgaskell)
on CodePen.0

Electric SVG Loader

Very different from the flatter animations, here’s an electric rotating ring.

See the Pen
Electric SVG Loader
by Shaw (@shshaw)
on CodePen.0

CSS3 + SVG loader animation

A cute cartoon plane flying through the clouds while the page loads.

See the Pen
CSS3 + SVG loader animation
by lionelB (@lionelB)
on CodePen.0

SVG ∞ loader (no JS, cross-browser, minimal code)

A literally infinite animation.

See the Pen
SVG ∞ loader (no JS, cross-browser, minimal code)
by Ana Tudor (@thebabydino)
on CodePen.0

UXBOX pencil loader

Here’s another change of pace from the norm – a rotating pencil!

See the Pen
UXBOX pencil loader
by elhombretecla (@elhombretecla)
on CodePen.0

SVG Spinner / Loader

A clever combination of the word loading and a circle spinner.

See the Pen
SVG Spinner / Loader
by Marcus Hall (@flurrd)
on CodePen.0

Animated Gradient SVG Loader

Another very specific animation that you can use for inspiration or edit to make it your own.

See the Pen
Animated Gradient SVG Loader
by Paul Thomas (@motionimaging)
on CodePen.0

Triangle SVG Loader (pure css)

For our last example we have a simple single line triangle loader.

See the Pen
Triangle SVG Loader (pure css)
by Dominic Kolbe (@dominickolbe)
on CodePen.0

 

 

The Guide To Windows High Contrast Mode

When we talk about accessibility, we tend to talk about many things — such as dark mode, keyboard navigation, prefers-reduced-motion, and screen readers — but there is one thing that does not receive that much attention: Windows High Contrast Mode (from now on, abbreviated as WHCM). This is a tendency I have seen in some websites at a point where we have normalized some practices that can harm users’ experience in WHCM. In this article, I want to explain what it is and give a good set of practices we can keep in mind to make our sites more usable with this mode.

About Windows High Contrast Mode

High Contrast mode is an accessibility feature that changes the look of our website and Windows applications by replacing the color of the different elements (like background, buttons, or text) with some user’s set up colors. This has multiple purposes, like increasing readability, reducing a website’s visual noise by removing certain elements (and by extension, allowing them to have a better focus), and giving users full control of the website’s contrast. You can check out by going to Settings, then clicking on Accessibility, and finally clicking on High Contrast.

To talk about some statistics, according to Melanie Richard in her talk “The tailored web: effectively honoring visual preferences”, around 4% of active devices use Windows High Contrast Mode, and thanks to WebAIM’s Survey of Users with Low Vision we can estimate that around 30% of users with low vision user Windows High Contrast Mode. All this should give you some perspective about the importance of making our website friendly with this mode.

The name “High Contrast Mode” is a bit misleading because the user can choose their preferred colors, leading to a color palette that has lower contrast than usual — which is not a very odd case. According to WebAIM’s survey, around 3% of users of Windows High Contrast Mode set it up to create a low contrast color pallete. The users with migraines or light sensitivity can do that to mitigate their disabilities’ impact. Just to give you a quick example:

I’m sure you understand the importance of making our website friendly with WHCM, and you might think that due to its nature of replacing a big part of our styles, making a website that works for that mode can be hard. Great news, it’s not! We just need to consider some important issues to ensure the user experience is not harmed.

Considerations About Windows High Contrast Mode

Despite how much control we lose when our website is displayed in WHCM, we can make it work without too much effort as long as we keep in mind some considerations. Before I start with that, I’d like you to keep in mind the golden rule with this mode: above all things, High Contrast Mode is about usability, and we need to respect that above any other aesthetics matter. Our biggest priority with this mode is easing readability and not harming the user experience in any way.

How can we ensure readability and usability works in WHCM? We can have certain important considerations for that:

Use Semantic HTML

This has been a very important topic when we talk about accessibility due to its importance for screen readers, and it’s very important in WHCM as well! Why? Because Windows will add the styles depending on the semantics of an element and not because of how it looks outside WHCM. A link will have the hyperlinks styles, a button will have the Button Text styles, and so on.

Some devs (for some reason) decide to use aria roles on divs to camouflage them as buttons for assistive technology. However, in WHCM, aria roles are irrelevant for Windows to determine which style to apply, so we depend on semantics to make our website works properly in this mode.

To validate this point, let’s check how a div that acts as real button and a link would behave in High Contrast Mode using the same styles.

<div role="button" class="button" tabindex=0>
  Not a button
</div>
<button class="button">
  Definitely a button
</button>
<a href="#" class="button">
  This is a link
</a>

.button {
  padding: 0.5em 1em;
  border: 2px solid hotpink;
  background-color: hotpink;
  width: fit-content;
  border-radius: 0.5em;
  font-size: 1.5rem;
  font-family: sans-serif;
  text-decoration: none;
  color: black;
}

In default settings, the div and the button will have the same colors but remember: users can change that. Let’s use this color palette, and let’s check the results:

Notice that semantics have a significant matter in WHCM for styling. Remember, in this mode, we have to focus on not harming the user’s experience, and choosing the wrong element can confuse users.

transparent Properties Are Useful!

When we style certain interactive components like buttons or links, we tend to remove certain properties with border: none, outline: none, or text-decoration: none because those properties might not match with our design system. Usually, that’s not a bad idea as long as you keep in mind things like hover or focus state for those components. For WHCM, however,it is a serious problem because background elements are completely overwritten, and we’ll depend on borders to differentiate those components from the background.

Just to give you an example, a very common design pattern I have seen is with the primary and secondary buttons, where the former has a background color and no border, and the latter has just a border and no background. It looks good, but when you see them under High Contrast Mode:

<button class="primary">
  Primary action
</button>
<button class="secondary">
  Secondary action
</button>

    button {
      font-size: 1.3em;
      padding: 0.5em 1em;
      border: none;
      font-family: sans-serif;
      border-radius: 0.4em;
      background-color: transparent;
    }

    .primary {
      background-color: hotpink;
    }

    .secondary {
      border: 2px solid hotpink
    }

The primary button can be easily mistaken for a normal text! This is where transparent borders come into play because transparencies will be visible under a High Contrast Mode. So by replacing the border property in the button element with this: 2px solid transparent, we’ll have this result:


    button {
      border: 2px solid transparent
    }

As you can imagine, that also happens with links if you use the property text-decoration-color: transparent, and with outlines if you use outline-color: transparent. Let’s check some quick examples about those properties.

text-decoration-color: transparent is useful if you’re using another element to represent a link in your site. Just to give an example, you can use background-image to animate the underline, as you can see in this video made by Kevin Powell. However, in WHCM, you’ll only depend on the color the user has in his settings, so if you want to give an additional visual cue, a transparent underline will work great there!

Outlines are a particularly important topic in this mode because some developers rely on other properties to add focus states to interactive elements — such as changing the background-color or even the box-shadow hack (even if it’s not necessary nowadays because now the outline will follow the element’s border-radius since Chrome 94 and Firefox 88). However, all those things are completely overwritten in this mode, so outline remains as the only reliable way to apply a focus state on an element in WHCM. Always keep that in mind: if you’re going to use something different than an outline to highlight a focus state in an element, add the property outline-color: transparent as a fallback to not missing a focus state in this mode.

Keep In Mind Scrollbars

Scrollbars can be styled, but does that mean we should style them? There are some usability and accessibility concerns about this topic. The one I want to bring here is the fact that, depending on how you style it in WHCM, they’ll look clunky in the best of cases, or they won’t be visible at all at worst of scenarios.

Is there a way to solve that? That depends on how you decide to style a scrollbar. Some people decide to use a solid color to fill the scrollbar’s thumb, and that does have a very easy fix. Let’s suppose we decided to style our scrollbar that way, then you will go for something like:


    ::-webkit-scrollbar {
      width: 20px; 
    }

    ::-webkit-scrollbar-track {
      background-color: #e4e4e4;
      border-radius: 100px;
    }

    ::-webkit-scrollbar-thumb {
      border-radius: 100px;
      background-color: green;
    }
    

As you might guess, the scrollbar won’t be visible in WHCM due to its background-color property being forcedly replaced. The great news is that we have already seen how to remediate this problem!

Transparent borders can cover this situation. You can use them to cover all the scrollbar’s thumb, and it’ll look like it’ll have a solid color (the one you choose as text color in settings) which will be pretty similar to how it works as a default scrollbar in this mode. To follow our previous example, if we use the property border: 10px solid transparent, it will make it look like it has a solid background in WHCM.

Be careful using this technique with scrollbar thumbs styled with box-shadow insets, though. If you do that, it’ll make it invisible. Not in WHCM, I mean invisible outside of it. You can check this problem in this scrollbar style made by Ahmad Shadeed, go to the scrollbar thumb styles, and add the same style we added before (border: 10px solid transparent) . You’ll see it’ll become invisible, a good way to make it visible (both regularly and in WHCM) is just using a smaller border (something like 2px instead of 10px) to make it look like this in WHCM:

It looks good! The only problem is that it looks a bit weird outside of WHCM, so keep this in mind if you decide to style a scrollbar using an inset box-shadow.

Remember that all that applies only to Chromium-based browsers, as Firefox has a different way to style scrollbars using scrollbar-color and scrollbar-width properties. The good news is that in WHCM, you won’t have to do a thing to make it work properly! The colors will be overwritten, and the scrollbar’s thumb will have the same color user has set up as text color.

Behavior Of Images

We have different ways to use images on a site: using the tag img, the background-image CSS property, using SVGs, and even CSS art! Let’s dig about those quickly. img tag will behave the same in WHCM, so let’s talk about the other three.

First, we have the background-image property — and this one will remain the same in WHCM as long as you’re using the url() value. A gradient made with background-image will be overwritten by WHCM’s background color. However, there is only one catch with this. Even though Firefox supports background images in High Contrast Mode since around 2018-2019, it won’t be visible if you put background-image in the body element.

You can try it out by seeing the CodePen I made to try to open it while using WHCM. So keep that in mind in case you’re using a background image like that.

That’s a bit problematic. Even when we normally want SVG to remain the same, there should be a way to manage those situations for specific scenarios. The good news is that, indeed, there is one! But let’s put a pin on this topic for now.

Keep in mind that this scenario only happens in Chromium-based browsers — Firefox has its own way to manage this. SVGs inside an anchor that use the currentColor property will receive the same color as the link color user has set up. It’ll even respect whatever color the theme uses as a visited link, as this picture shows:

Finally, we have CSS art. Due to its nature of using elements like box shadows and background gradients, you might guess it won’t look good in WHCM — and you’re absolutely right. Due to its artistic nature, it’s no big deal, so you should be fine. But, if it does have a purpose in your website, we need to look for a way to make it visible. Just a quick note about CSS art: remember you can — and should — make your CSS art accessible!

As long as you keep in mind those small suggestions, our website will be almost done for WHCM! As you saw, some elements would need some tweaks to make them work to their full extent in this mode, but luckily for us, CSS has a way to help us to get this last part of the job done!

Media Query Forced-Colors

Microsoft made an effort to create a standard to support WHCM, and the result of this work was the media query forced-colors, which will help us to detect if the browser or operating system has enabled a mode that limits a website’s styles to a user-chosen color palette. As you might guess, WHCM is the most popular choice among them.

This media query will act a bit differently due to how WHCM works. Some properties will be limited to certain values, some won’t be able to be overwritten at all, and we have new properties’ values to work with! But before digging into what we can do with this tool, let’s remember that WHCM (and other modes that restrict user’s color palettes) prioritize usability, and this is something we need to respect. So don’t use those properties unless it’s necessary to tweak some elements in your website to give it good usability in this mode.

With that said, let’s start talking about the media query itself. It has two values: none and active. The former will detect when there is no forced colors mode active, and the second one will detect when there is. Under forced colors mode, the next properties will be replaced with the ones that are set up by the user:

  • color
  • background-color
  • text-decoration-color
  • text-emphasis-color
  • border-color
  • outline-color
  • column-rule-color
  • -webkit-tap-highlight-color
  • SVG fill and stroke attributes

Additionally, there are some properties that will have a forced behavior:

Property Value
box-shadow none
text-shadow none
background-image none (unless it’s url() )
color-scheme light dark
accent-color auto
scrollbar-color (Firefox) auto

With that explained, let’s dig into two tools we have we can use to enhance the experience in this mode.

Forced-Color-Adjust

Now, how can we change how those properties behave? There is a way to avoid WHCM overwrites colors, and this is by using the property forced-color-adjust. This property has two values: auto and none, and it’ll let us decide if we want an element’s colors will be replaced by the user agent’s colors or not, respectively. Let’s check an example of how those work, and there aren’t better examples than the ones we left uncovered in the previous section!

Let’s check the link with the external link’s SVG we used earlier. Keep in mind that in Chromium-based browsers, this SVG won’t change its color to match the one that is used as a link color because SVGs have a default value of none. So, if we add the property forced-color-adjust: auto to our SVG as follows:

.inline-icon {
  /* Previous CSS properties */
  forced-color-adjust: auto;
}

This will be our result:

I know this section is about the media query itself, and usually, what you’d do is put that rule inside the media query like this:

@media screen and (forced-colors: active) {
  .inline-icon {
    forced-color-adjust: auto;
  }
}

That’s a valid approach (and, honestly, the most intuitive one). However, while I did some experiments for this article, I noticed that you can put this property in an element without the need to use the media query, and you’ll get the same result! And because this property will affect only the behavior of this element in a forced colors scenario, it won’t give you any unexpected behavior.

Now, with CSS art, we want the opposite to be true (again, as long as this CSS is necessary to give enough context to the user), so we can use forced-color-adjust: none in the art’s parent element, and now all of it will be visible in WHCM.

You may be thinking that this is not a common use case of forced-color-adjust: none, and you’d be right, so let’s check a more realistic one: showing color palletes on your website! Let’s take a look at any pallete generated by mycolor.space for example:

Those colors are not visible, and it’s an important part of the website, so if we go to the color container element and we add this property, we’ll solve this problem.

System Colors

Now let’s talk about colors. With media query forced-colors we have a handful of system colors we can use. You can see a list of colors in MDN’s documentation, and we can use this list of colors to replace certain properties. Using the property color: LinkText will make it look like a link, for example.

Just remember: those colors are closely related to HTML semantics, so maybe you’d be better changing an element to its correct tag instead of trying to change how it looks in WHCM. That doesn’t mean it doesn’t have its uses. We just have to be sure we are doing this for the right reasons. Which is a good reason to use this? Well, that depends on the complexity of what you are creating. Let’s take, as an example, this link I created with the help of the clip-path property.

.link {
  --clip-path: polygon(0% 0%, calc(100% - 0.8em) 0%, 100% 0.8em, 100% 100%, 0.8em 100%, 0% calc(100% - 0.8em));
  font-size: 2rem;
  padding: 0.1em;
  border: none;
  background-color: #0E0054;
  clip-path: var(--clip-path);
  font-family: sans-serif;
}

.link:focus {
  outline: none;
}

.link:focus span, .link:hover span {
  outline-offset: -0.5em;
  outline: 3px solid transparent;
  background-color: #0E0054;
  color: white;
  text-decoration-color: white;
}

.link span {
  display: inline-block;
  padding: 0.5em 1.2em;
  clip-path: var(--clip-path);
  background-color: white;
  color: #0E0054;
  text-decoration: underline #0E0054;
  text-underline-offset: 2px;
}

.link span {
  display: inline-block;
  padding: 0.5em 1.2em;
  clip-path: var(--clip-path);
  background-color: white;
  color: #0E0054;
  text-decoration: underline #0E0054;
  text-underline-offset: 2px;
}

Let’s make a quick check of the problems with this element in WHCM:

  • I used a background-color to mimic a border with this element, but because it’s a background, it won’t be visible in WHCM.
  • Even if I used a transparent outline to make a focus state in this mode, its color would be the one that the system uses as a link color, instead of the one WHCM’s usual outline color.

With this in mind, we can tweak system colors using the media query forced-colors to give enough visual feedback to users by showing them that that is a link.

@media screen and (forced-colors: active) {
  .link {
    background-color: LinkText;
  }

  .link:visited {
    background-color: VisitedText;
  }

  .link:focus span {
    outline-color: Highlight;
  }
}

Remember Firefox has a visited state color for links, so to respect that. We should add the VisitedText system color in the visited pseudo-class of our link. With that said, this is our result:

Another simple example of how we can use system colors to tweak the experience is something we saw in the previous section: scrollbars! Let’s suppose that, for some reason, transparent borders are not an option. In this case, we can use system colors to make our scrollbar looks good in this mode! Let’s come back to one of the examples we used previously, and instead of using a transparent border, we’ll use the media query to tweak the scrollbar’s thumb’s color.

::-webkit-scrollbar {
  width: 20px;
}

::-webkit-scrollbar-track {
  background-color: #e4e4e4;
  border-radius: 100px;
}

::-webkit-scrollbar-thumb {
  border-radius: 100px;
  background-color: green;
}

@media screen and (forced-colors: active) {
  ::-webkit-scrollbar-thumb {
    background-color: CanvasText;
  }
}

Other Uses Of This Media Query

As you read, forced-color-adjust and system colors are great tools to tweak our design if needed, but that’s not all we can do with this media query. Yes, we saw that some properties are restricted to certain uses, but most of them can be used normally! Remember, this is just to improve usability in WHCM, so there is no need to go too wild with that. Use it just when it’s needed.

Let’s come back to the clip-path link we used. You could decide that the approach to how it looks in WHCM is to use a simpler design, like maybe just using a regular bordered element. We can do that! Let’s ignore the CSS rules I used in my previous example, and let’s use those instead:

@media screen and (forced-colors: active) {
  .link {
    --clip-path: none;
    border: 3px solid transparent;
    border-radius: 8px;
  }

  .link:focus {
    outline: 3px solid transparent;
    outline-offset: 3px;
  }

  .link:focus span {
    outline: none;
  }
}

And this is our result:

With that approach, you still show the user this is a link, and you avoid any possible confusion with this topic. Uses of CSS properties in this media query can open some interesting doors to improve how sites work. You can remove a merely decorative image in this mode — with display: none (if you used an img tag) or background-image: none (if you added it with CSS) — if you consider it can bring a better experience — it might have very bright colors for users with migraine, or it can be a bit distracting, for example.

As long as you prioritize usability in your website with this mode, it should be good enough. However, most of the times you might not need it as long as you keep into consideration the previous suggestions I mentioned.

You can also use custom properties in this mode, which will lead to some interesting uses, as you can see in this article by Eric Bailey.

Other Resources

It’s important to note that in the case you still need to support Internet Explorer, media query forced-colors won’t work. If you want to know how to give support to High Contrast Mode in this browser, you can read about it in this article written by Greg Whitworth and this one by Adrian Roselli. For the topics covered in this article, you can read the following articles:

Wrapping Up

Windows High Contrast Mode is something I have seen some websites overlook, which can create problems for people who use this accessibility feature. The good news is that we have enough tools to make our website works great in WHCM, even more with Microsoft’s efforts to create the media query forced-colors — which opens new doors to make our sites look better in this mode. Just remember: it’s an accessibility and usability feature so keep this in mind when you want to tweak your project in this mode!

Further Reading On Smashing Magazine

Mastering SVG’s stroke-miterlimit Attribute

So, SVG has this stroke-miterlimit presentation attribute. You’ve probably seen it when exporting an SVG from a graphic editor program, or perhaps you find out you could remove it without noticing any change to the visual appearance.

After a good amount of research, one of the first things I discovered is that the attribute works alongside stroke-linejoin, and I’ll show you how as well as a bunch of other things I learned about this interesting (and possibly overlooked) SVG attribute.

TLDR;

stroke-miterlimit depends on stroke-linejoin: if we use round or bevel for joins, then there’s no need to declare stroke-miterlimit. But if we use miter instead, we can still delete it and maybe the default value will be enough. Beware that many graphic software editors will add this attribute even when is not necessary.

What is stroke-linejoin?

I know, we’re actually here to talk about stroke-miterlimit, but I want to start with stroke-linejoin because of how tightly they work together. This is the definition for stroke-linejoin pulled straight from the SVG Working Group (SVGWG):

stroke-linejoin specifies the shape to be used at the corners of paths or basic shapes when they are stroked.

This means we can define how the corner looks when two lines meet at a point. And this attribute accepts five possible values, though two of them have no browser implementation and are identified by the spec as at risk of being dropped. So, I’ll briefly present the three supported values the attribute accepts.

miter is the default value and it just so happens to be the most important one of the three we’re looking at. If we don’t explicitly declare stroke-linejoin in the SVG code, then miter is used to shape the corner of a path. We know a join is set to miter when both edges meet at a sharp angle.

But we can also choose round which softens the edges with — you guessed it — rounded corners.

The bevel value, meanwhile, produces a flat edge that sort of looks like a cropped corner.

What is stroke-miterlimit?

OK, now that we know what stroke-linejoin is, let’s get back to the topic at hand and pick apart the definition of stroke-miterlimit from the book Using SVG with CSS3 and HTML5:

[…] on really tight corners, you have to extend the stroke for quite a distance, before the two edges meet. For that reason, there is a secondary property: stroke-miterlimit. It defines how far you can extend the point when creating a miter corner.

In other words, stroke-miterlimit sets how far the stroke of the edges goes before they can meet at a point. And only when the stroke-linejoin is miter.

Miter join with miter limit in grey.

So, the stroke-miterlimit value can be any positive integer, where 4 is the default value. The higher the value, the further the corner shape is allowed to go.

How they work together

You probably have a good conceptual understanding now of how stroke-linejoin and stroke-miterlimit work together. But depending on the stroke-miterlimit value, you might get some seemingly quirky results.

Case in point: if stroke-linejoin is set to miter, it can actually wind up looking like the bevel value instead when the miter limit is too low. Here’s the spec again to help us understand why:

If the miter length divided by the stroke width exceeds the stroke-miterlimit then [the miter value] is converted to a bevel.

So, mathematically we could say that this:

[miter length] / [stroke width] > [stroke-miterlimit] = miter
[miter length] / [stroke width] < [stroke-miterlimit] = bevel

That makes sense, right? If the miter is unable to exceed the width of the stroke, then it ought to be a flat edge. Otherwise, the miter can grow and form a point.

Sometimes seeing is believing, so here’s Ana Tudor with a wonderful demo showing how the stroke-miterlimit value affects an SVG’s stroke-linejoin:

Setting miter limits in design apps

Did you know that miter joins and limits are available in many of the design apps we use in our everyday work? Here’s where to find them in Illustrator, Figma, and Inkscape.

Setting miter limits in Adobe Illustrator

Illustrator has a way to modify the miter value when configuring a path’s stroke. You can find it in the “Stroke” settings on a path. Notice how — true to the spec — we are only able to set a value for the “Limit” when the path’s “Corner” is set to “Miter Join”.

Applying stroke-miterlimit in Adobe Illustrator.

One nuance is that Illustrator has a default miter limit of 10 rather than the default 4. I’ve noticed this every time I export the SVG file or copy and paste the resulting SVG code. That could be confusing when you open up the code because even if you do not change the miter limit value, Illustrator adds stroke-miterlimit="10" where you might expect 4 or perhaps no stroke-miterlimit at all.

And that’s true even if we choose a different stroke-linejoin value other than “Miter Join”. Here is the code I got when exporting an SVG with stroke-linejoin="round".

<svg viewBox="0 0 16 10"><path stroke-width="2" stroke-linejoin="round" stroke-miterlimit="10" d="M0 1h15.8S4.8 5.5 2 9.5" fill="none" stroke="#000"/></svg>

The stroke-miterlimit shouldn’t be there as it only works with stroke-linejoin="miter". Here are a couple of workarounds for that:

  • Set the “Limit” value to 4, as it is the default in SVG and is the only value that doesn’t appear in the code.
  • Use the “Export As” or “Export for Screen” options instead of “Save As” or copy-pasting the vectors directly.

If you’d like to see that fixed, join me and upvote the request to make it happen.

Setting miter limits in Figma

Miter joins and limits are slightly different in Figma. When we click the node of an angle on a shape, under the three dots of the Stroke section, we can find a place to set the join of a corner. The option “Miter angle” appears by default, but only when the join is set to miter:

Applying stroke-miterlimit in Figma.

This part works is similar to Illustrator except for how Figma allows us to set the miter angle in degree units instead of decimal values. There are some other specific nuances to point out:

  • The angle is 7.17° by default and there is no way to set a lower value. When exporting the SVG, that value is becomes stroke-miterlimit='16‘ in the markup, which is different from both the SVG spec and the Illustrator default.
  • The max value is 180°, and when drawing with this option, the join is automatically switched to bevel.
  • When exporting with bevel join, the stroke-miterlimit is there in the code, but it keeps the value that was set when the miter angle was last active (Illustrator does the same thing).
  • When exporting the SVG with a round join, the path is expanded and we no longer have a stroke, but a path with a fill color.

I was unable to find a way to avoid the extra code that ends up in the exported SVG when stroke-miterlimit is unneeded.

Setting miter limits in Inkscape

Inkscape works exactly the way I’d expect a design app to manage miter joins and limits. When selecting a a miter join, the default value is 4, exactly what it is in the spec. Better yet, stroke-miterlimit is excluded from the exported SVG code when it is the default value!

Applying stroke-miterlimit in Inkscape.

Still, if we export any path with bevel or round after the limit was modified, the stroke-miterlimit will be back in the code, unless we keep the 4 units of the default in the Limit box. Same trick as Illustrator.

These examples will work nicely if we choose the Save AsOptimized SVG option. Inkscape is free and open source and, at the end of the day, has the neatest code as far as stroke-miterlimit goes and the many options to optimize the code for exporting.

But if you are more familiar with Illustrator (like I am), there is a workaround to keep in mind. Figma, because of the degree units and the expansion of the strokes, feels like the more distant from the specs and expected behavior.

Wrapping up

And that’s what I learned about SVG’s stroke-miterlimit attribute. It’s another one of those easy-to-overlook things we might find ourselves blindly cutting out, particularly when optimizing an SVG file. So, now when you find yourself setting stroke-miterlimit you’ll know what it does, how it works alongside stroke-linejoin, and why the heck you might get a beveled join when setting a miter limit value.


Mastering SVG’s stroke-miterlimit Attribute originally published on CSS-Tricks. You should get the newsletter.

Collective #712







Building a button component

The perfect foundational overview of how to build color-adaptive, responsive, and accessible <button> components. Bonus: you’ll learn about very useful CSS selectors, too! By Adam Argyle.

Check it out




Web Applications 101

Everything you need to know about web applications in modern web development. You will learn about traditional websites, full-stack web applications, client-side and server-side rendering/routing and many more topics.

Read it












Dreamwave 3D Demo

Dreamwave is a web-based platform to deliver mind-blowing Virtual Events and Microverses for brands and creators.

Check it out


> hackerforms

Create user interfaces straight from your Python code – no frontend work required.
Deploy instantly and share with anyone.

Check it out





The post Collective #712 appeared first on Codrops.

Magical SVG Techniques

SVGs have become more and more popular in the past few years. For good reasons. They are scalable, flexible, and, most importantly, lightweight. And, well, they have even more to offer than you might think. We came across some magical SVG techniques recently that we’d love to share with you. From SVG grids and fractional SVG stars to SVG masks, fancy grainy SVG gradients, and handy SVG tools. We hope you’ll find something useful in here.

By the way, a while ago, we also looked at SVG Generators — for everything from shapes and backgrounds to SVG path visualizers, cropping tools, and SVG → JSX generators. If you’re tinkering with SVG, these might come in handy, too.

Generative SVG Grids

Generative art is a wonderful opportunity for everyone who would love to create art but feels more at home in code. Let’s say you want to create geometric patterns, for example. Generative art will take away the difficult decisions from you: What shapes do I use? Where do I put them? And what colors should I use? If you want to give it a try, Alex Trost wrote a tutorial on creating generative art with SVG grids that is bound to tickle your creativity — and teach you more about SVG.

The generative art that Alex creates is a grid of blocks with a random number of rows and columns. Each block has a randomly chosen design and colors from a shared color palette. Alex takes you step by step through the process of coding this piece: from setting up the grid and creating isolated functions to draw SVGs to working with color palettes, adding animations, and more. A fun little project — not only if you’re new to generative art and creative coding.

Generative Landscape Rolls

An awe-inspiring project that bridges the gap between a century-old tradition and state-of-the-art coding is {Shan, Shui}. Created by Lingdong Huan and inspired by traditional Chinese landscape rolls, it creates procedurally generated, infinitely-scrolling Chinese landscapes in SVG format. The mountains and trees in the landscape are modeled from scratch using noise and mathematical functions. Fascinating!

Now, if you’re asking yourself how something as complex might work, you’re not alone. Victor Shepelev wanted to get behind the secret of {Shan, Shui}* and made it his advent project to understand how it works. And, indeed, it took him 24 days to fully dig into the code. He summarized his findings in a series of articles.

SVG Paths With Masks

SVGs have a lot of benefits compared to raster images. They are small in size, scalable, animatable, they can be edited with code, and a lot more. You can’t get the textured feel that raster graphics can provide, though. However, we can combine the strengths of vector and raster to create some charming effects. Like Tom Miller did in his Silkscreen Squiggles demo.

Silkscreen Squiggles is an animation where squiggles fill a rectangular canvas. What makes the squiggles special is that they appear to have a paintbrush texture. The secret: a mask with an alpha layer that gives the simple squiggly paths their texture. Alex Trost dissects how it works. Inspiring!

Grainy Gradients

Noise is a simple technique to add texture to an image and make otherwise solid colors or smooth gradients more realistic. But despite designer’s affinity for texture, noise is rarely used in web design. Jimmy Chion explores how we can add texture to a gradient with only a small amount of CSS and SVG.

The trick is to use an SVG filter to create the noise, then apply that noise as a background. Layer it underneath your gradient, boost the brightness and contrast, and that’s already it. Potential use cases could be light and shadows or holographic foil effects, for example. The core of this technique is supported by all modern browsers. A clever visual effect to add depth and texture to a design.

Adding Texture And Depth

“Analog” materials like paint and paper naturally add depth to an artwork, but when working digitally, we often sacrifice the organic depth they provide for precision and speed. Let’s bring some texture back into our work! George Francis shares three ways to do so.

The techniques that George explores are quite simple but effective. Tiny random shapes added to a canvas at random points, solid shape fills with lines, and non-overlapping circles distributed evenly but randomly with an algorithm. Inspiring ideas to tinker with.

Cut-Out Effects With CSS And SVG

In a recent front-end project that Ahmad Shadeed was working on, one of the components included a cut-out effect where an area is cut out of a shape. And because there are multiple ways to create such an effect in CSS or SVG, he decided to explore the pros and cons that each of the solutions brings along.

In his blog post “Thinking About The Cut-Out Effect”, Ahmad takes a look at three different use cases for a cutout effect: an avatar with a cut-out status badge that indicates that a user is currently online, a “seen avatar” that consists of overlapping circle avatars that are indicators that a message has been seen in a group chat, as well as a website header with a cut-out area behind a circular logo. Ahmad presents different solutions for each use case — SVG-only, CSS-only, and a mix of both — and explains the pros and cons of each one of them. A comprehensive overview.

Fractional SVG Stars

Are you building a rating component and you want it to support fractional values like 4.2 or 3.7 stars but without using images? Good news, you can achieve fractional ratings with only CSS and inline SVG. Samuel Kraft explains how it works.

The component basically consists of two parts: a list of star icons based on the max rating and an “overlay” div that will be responsible for changing the colors of the stars underneath. This is the magic that makes the fractional part work. The technique is supported in all modern browsers; for older browsers, you can fall back to opacity instead. Clever!

Generative Mountain Ridge Dividers

When Alistair Shepherd built his personal website, he wanted to have section dividers that match the mountain theme of the site. But not any mountain dividers, but dividers with unique ridges for every divider.

Instead of creating a variety of different dividers manually, Alistair decided to use a combination of SVG and terrain generation, a technique that is usually used in game development, to generate the dividers automatically. In a blog post, he explains how it works.

If you’re up for some more horizontal divider inspiration, also be sure to check out Sara Soueidan’s blog post “Not Your Typical Horizontal Rules” in which she shows how she turned a boring horizontal line into a cute “birds on a wire” divider with the help of some CSS and SVG.

Flexible Repeating SVG Masks

Sometimes it’s a small idea, a little detail in a project that you tinker with and that you can’t let go off until you come up with a tailor-made solution to make it happen. Nothing that seems like a big deal at first glance, but that requires you to think outside the box. In Tyler Gaw’s case, this little detail was a flexible header with a little squiggle at the bottom instead of a straight line. The twist: to make the component future-proof, Tyler wanted to use a seamless, horizontal repeating pattern that he could color with CSS.

To get the job done, Tyler settled on flexible repeating SVG masks. SVG provides the shape, CSS handles the color, and mask-image does the heavy lifting by hiding anything in the underlying div that doesn’t intersect with the shape. A clever approach that can be used as the base for some fun experiments.

Swipey Image Grids

When you think of “SVG animation”, what comes to your mind? Illustrative animation? Well, SVG can be useful for much more than pretty graphics. As Cassie Evans points out, a whole new world of UI styling opens up once you stop looking at SVG purely as a format for illustrations and icons. One of her favorite use cases for SVG: responsive animated image grids.

Cassie doesn’t build her image grid on CSS Grid but uses SVG’s internal coordinate system (which is responsive by design) to design the grid layout. She then adds images to the grid and positions them with preserveAspectRatio. clipPath “swipes” the images in. The final animation relies on GreenSock to ensure that the transforms work consistently across browsers. If you want to dig deeper into the code, be sure to check out Cassie’s blog post in which she explains each step in detail.

Animated SVG Debit Card Illustrations

What if you could animate a debit card design? Probably not on an actual physical card, but rather for a landing page where you’d like to drive interest towards the card’s design or features? Well that’s an unusual challenge to tackle, and Tom Miller decided to take it on.

In a series of SVG debit card animations, Tom uses GreenSock to animate SVG paths and shapes smoothly, so every card literally comes to life on its own, transforming, rotating, and scaling beautifully, alongside just a few lines of JavaScript. A wonderful inspiration for your next landing page design!

Raster Image To SVG Converter

You need to quickly convert a raster image into an SVG? Then SVGcode is for you. The progressive web app converts image formats like JPG, PNG, GIF, WebP, and AVIF to vector graphics in SVG format.

To convert an image, drop your raster image into the SVGcode app, and the app will trace the image, color by color, until a vectorized version of the input appears. You can choose between color SVG and monochrome SVG and there also are a number of customization settings to improve the output further, by suppressing speckles and adjusting the color, for example. If you install the PWA, you can even use it as a default file handler on your machine. A real timesaver.

Download SVGs From Any Site

A handy little tool to enhance your SVG workflow is SVG Gobbler. The browser extension finds the vector content on the page you’re viewing and gives you the option to download, optimize, copy, view the code, or export it as an image.

When you click the browser extension, it shows you all SVGs detected on the site. You can quickly download the ones you like or copy them to your clipboard. When you view the code, you can toggle optimization options from SVGO — to beautify the markup or clean up attributes or numeric values, for example. And if you need a PNG version of an SVG, you can export it in any size you want. A fantastic addition to any developer’s toolkit.

Scaling SVGs Made Simple

Scaling svg elements can be a daunting task, since they act very differently than normal images. Amelia Wattenberger came up with an ingenious comparison to help us make sense of SVGs and their special features: “The svg element is a telescope into another world.”

Based on the idea of the telescope, Amelia explains how to use the viewBox property to zoom in or out with your “telescope”, and, thus, change the size of your <svg>. A small tip that works wonders.

Wrapping Up

We hope that these techniques will tickle your curiosity and inspire you to try some SVG magic yourself. If you came across an interesting SVG technique that left you in awe, please don’t hesitate to share it in the comments below. We’d love to hear about it. Happy creating!

Collective #709





HyperUI

Free Tailwind CSS components that can be used in your next project. Perfect for Laravel, Rails, React, Vue and more.

Check it out












FortuneSheet

A drop-in JavaScript spreadsheet library that provides rich features like Excel and Google Sheets.

Check it out




Fleet (beta)

Fleet is the blazing fast build tool for Rust. Compiling with Fleet is up-to 5x faster than with cargo.

Check it out



The post Collective #709 appeared first on Codrops.

Collective #708





Collective 708 item image

JavascriptDB

Create low code serverless applications: Arrays and Objects operations read and write into your database.

Check it out



Collective 708 item image

Loaders

Free loaders and spinners for your next project. Built with HTML, CSS and some SVG. By Griffin Johnston.

Check it out






Collective 708 item image

SVG passthrough precision

If an SVG is imported into a design tool, then immediately exported as another SVG, how much precision is kept? What’s added, removed, or altered? Find out in this article.

Read it



Collective 708 item image

WeekToDo

WeekToDo is a free minimalist weekly planner app focused on privacy. Available for Windows, Mac, Linux or online.

Check it out



Collective 708 item image

YouTube.js

A full-featured wrapper around YouTube’s private API providing a simple and efficient way to interact with YouTube programmatically.

Check it out



Collective 708 item image

Arteater

Fun offline thing: Arteater digests your hand-drawn art and returns an animated GIF you can save and share.

Check it out





Collective 708 item image

NotepadNext

A cross-platform, reimplementation of Notepad++ available for Windows, Linux, and macOS.

Check it out




The post Collective #708 appeared first on Codrops.

Collective #706












Yaade

Yaade is an open-source, self-hosted, collaborative API development environment.

Check it out





Nitric

Nitric is a framework for rapid development of cloud-native and serverless applications.

Check it out



How We Built r/Place

A super interesting article on how Reddit’s Place was built, a collaborative canvas on which a single user could only place a single tile every five minutes.

Read it



Range

A fun and creative way to use the range slider 🙂 Made by Hakim El Hattab.

Check it out







The post Collective #706 appeared first on Codrops.

Collective #705






Collective 705 item image

Awwwards Conference

Three exciting days with some of the most influential speakers of the industry, who inspire, teach, and guide us as we face the many challenges and opportunities which lie ahead in the future of the web.

Check it out






Collective 705 item image

Canvas + Text

Paul Henschel made a demo that shows how to form self-contained components with their own state and user interaction.

Check it out



Collective 705 item image

Rhea

Rhea is a geometry-shader based grass for Unity’s Universal Render Pipeline (URP).

Check it out


Collective 705 item image

Color Morph

Randomly generate beautiful mesh gradients, export them as an SVG or copy the generated CSS code into your project.

Check it out


Collective 705 item image

Bionic Reading

An amazing project: Bionic Reading revises texts so that the most concise parts of words are highlighted which enables faster, more efficient reading.

Check it out



Collective 705 item image

PWA Resources

A curated collection of resources to learn about Progressive Web Apps. Also, a super cool site design.

Check it out


Collective 705 item image

Building a Vaporwave scene with Three.js

A step-by-step tutorial documenting Maxime Heckel’s attempt at reverse-engineering the vaporwave WebGL scene from the Linear 2021 release page using only fundamental concepts of Three.js like textures, lights, animations, and post-processing effects.

Read it


Collective 705 item image

Tao of Node

Alex Kondov summarizes the set of principles that he has established for building Node applications.

Read it





Collective 705 item image

Coolify

Version 2 of the amazing open-source and self-hostable Heroku/Netlify alternative.

Check it out



The post Collective #705 appeared first on Codrops.

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.