Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Tue, 30 Jul 2024 15:15:05 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 225069128 SVG triangle of compromise https://frontendmasters.com/blog/svg-triangle-of-compromise/ https://frontendmasters.com/blog/svg-triangle-of-compromise/#respond Tue, 30 Jul 2024 15:15:04 +0000 https://frontendmasters.com/blog/?p=3290 tag, it’s cached nicely, but you give up on CSS […]]]> I enjoyed Micah R Ledbetter’s SVG triangle of compromise and generally think it’s a fair analysis of how any-which-way you use SVG on a page, you’re giving up some kind of nice ability it could have. For instance, if you use SVG through an <img> tag, it’s cached nicely, but you give up on CSS reaching in there to style things. If you drop it in as <svg>, you can style, but then it’s not cached well for repeated uses.

Then Scott Jehl chimed in with a way to “have it all”. The crux of it is using the SVG <use> element to reference an SVG file (so you get caching and sizing) and you can set CSS --custom-properties that “pierce” the shadow DOM that <use> creates (that’s right, SVG can have a shadow DOM just like web components) and allow for styling.

This does solve all three angles, the caveats being 1) you can’t cache the SVG (“sprite”, it’s usually called when you combine icons into a single file) on a different domain. 2) it’s a manual pain to set up SVGs to be entirely styled in this way. Scott’s tool might help with 2, but browsers need to help with 1.

]]>
https://frontendmasters.com/blog/svg-triangle-of-compromise/feed/ 0 3290
Blurring https://frontendmasters.com/blog/blurring/ https://frontendmasters.com/blog/blurring/#respond Fri, 26 Jul 2024 19:31:08 +0000 https://frontendmasters.com/blog/?p=3194 , to WebGL. I particularly like the idea of masking an element with a backdrop-filter with a gradient so, for instance, a header can fade out how much […]]]> This 9-minute video from Juxtopposed on blurring is a great watch. There are an awful lot of ways to blur things on the web, from filter, backdrop-filter, <feGaussianBlur>, to WebGL. I particularly like the idea of masking an element with a backdrop-filter with a gradient so, for instance, a header can fade out how much blur is being applied.

]]>
https://frontendmasters.com/blog/blurring/feed/ 0 3194
Morphing Arbitrary Paths in SVG https://frontendmasters.com/blog/morphing-arbitrary-paths-in-svg/ https://frontendmasters.com/blog/morphing-arbitrary-paths-in-svg/#respond Fri, 14 Jun 2024 19:39:34 +0000 https://frontendmasters.com/blog/?p=2739 . There is a fairly big prerequisite to these kind of animations: the […]]]> Shape morphing animations can be awfully cool. They can be artistic and elaborate like the waving cape of a brand character, or simple and understated like video player controls morphing from one state to another. Alexandru-Gabriel Ică digs into doing this in <svg>. There is a fairly big prerequisite to these kind of animations: the shapes need the same number of points. The trick is typically to use a <path d=""> which is capable of drawing anything, then force both shapes to have the same number of points even if you don’t need them all. Alexandru-Gabriel goes deep on this writing script to convert arbitrary paths into morphable shapes, which is the same kind of thing libraries like Greensock do. I love the idea that you can do the morph directly in CSS.

]]>
https://frontendmasters.com/blog/morphing-arbitrary-paths-in-svg/feed/ 0 2739
Icônes https://frontendmasters.com/blog/icones/ https://frontendmasters.com/blog/icones/#respond Mon, 06 May 2024 22:32:19 +0000 https://frontendmasters.com/blog/?p=2050 Icônes is an amazing icon resource site. There are tens of thousands of free-to-use icons which are easy to browse and find. I guess it’s like an alternate UI to Iconify, making it ultra-fast to get the icon in a format you want it in, like a quick download of SVG or PNG, copying to your clipboard in one of those formats, or as a basic component in many of the most popular frameworks.

I still love The Noun Project a bunch, but it costs bucks, doesn’t seem to have innovated much (like it doesn’t have as many nice exporting options), and doesn’t group icons as well.

Jeez, Anthony Fu has done some incredible open-source projects!

Oh, and hey, if the idea of exporting to framework components is appealing to you, check out iconbundler which can take a whole bunch of them and do it for you right from the browser. I tend to use SVGR, though, to automate it.

]]>
https://frontendmasters.com/blog/icones/feed/ 0 2050
Faces.js and Playing with Densely Packed Grids https://frontendmasters.com/blog/faces-js-and-playing-with-densely-packed-grids/ https://frontendmasters.com/blog/faces-js-and-playing-with-densely-packed-grids/#respond Mon, 22 Apr 2024 16:09:35 +0000 https://frontendmasters.com/blog/?p=1757 I only just recently saw Faces.js, a library for producing controllable cartoony avatars. It was launched in 2012, so I’m a little late to the party. It produces faces (busts, really) randomly, or with certain parameters locked to what you want.

# Generate a random face that always has blue skin
const face = generate({ body: { color: "blue" } });
display("my-div-id", face);

I think that’s a really cool idea, and if you needed this kind of thing on a project, you can install it yourself which is safer than using a hosted service for random avatars. Like, Pravatar is neat, but these services have a super high churn rate. Do not count on it sticking around.

I wanted to have a quick play and have it output a bunch of random faces on a grid. First I made 100 <div>s. You could do this any number of ways, including JavaScript, but I did it with Pug just for fun:

.faces
  - let i = 1;
  - while (i < 100)
    div(id=`face-${i}` class="face")
    - i++;

Then I made that into a grid:

.faces {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
}
.face {
  width: 100%
  aspect-ratio: 1 / 1;
}

Fun:

Hm. That makes me want to make the grid more interesting to look at, perhaps with some of the avatars spanning two columns (and thus two rows accommodate the height).

To do that I put a random number 1-10 on each face when creating the <div>s, as a data-* attribute:

.faces
  - let i = 1;
  - while (i < 100)
    - var n = Math.floor(Math.random() * 10 + 1);
    div(id=`face-${i}` class="face" data-size=n)
    - i++;

Then I can select some of them and make them bigger:

.face {
  width: 100%
  aspect-ratio: 1 / 1;
  
  &[data-size="5"] {
    grid-column: span 2;
    grid-row: span 2;
    scale: 1.2;
  }
}

I used scale there to juice the size even more and make them overlap:

I wanted to do a few more thing though. One, this left some gaps in the grid, as in, literal blank grid positions.

It’s incredibly satisfying to fill those in with just one extra grid declaration:

grid-auto-flow: dense;

Then I wanted the edges of the screen to allow for overlap, so the edges don’t seem perfectly lined up. I wanted it to look more like randomly cut wrapping paper. To do that, all I did was scale up the body (much like we did with the enlarged avatars) and then clip off the horizontal overlow.

body {
  scale: 1.2;
  overflow-x: clip;
}

I added a few more random sizes and scalings and, I dunno, it just ended up a satisfying little thing to play with.

Have you actually used a random avatar generator in a production app? On CodePen we just use a consistent generic one, which I hope kinda encourages people to replace it. I imagine random avatars are more useful in mockups and during development.

]]>
https://frontendmasters.com/blog/faces-js-and-playing-with-densely-packed-grids/feed/ 0 1757
Building a TODO App from Scratch — Step 4 — Styling & Interactive Choices https://frontendmasters.com/blog/building-a-todo-app-from-scratch-step-4-styling-interactive-choices/ https://frontendmasters.com/blog/building-a-todo-app-from-scratch-step-4-styling-interactive-choices/#respond Wed, 28 Feb 2024 21:17:43 +0000 https://frontendmasters.com/blog/?p=1020 I’m already a little over all the green. What was I thinking? I’m much more of a nice gray color palette kinda guy with some fun poppy colors. That’s what we need to get to here. When we left off, we just had wireframes going. It’s time to get some style on this thing!

Article Series

Custom Properties

We can get rid of that wireframe look by removing all those 1px borders and applying some backgrounds. Let’s give ourselves some Custom Properties to work with, so we can apply colors in such a way they are easy to change later.

html {
  --gray800: oklch(10% 0% 0);
  --gray600: oklch(40% 0% 0);
  --gray100: oklch(92% 0% 0);
  --brand: oklch(85% 0.3 145);

  background: var(--gray100);
}

You can see we’re using the lightest gray right away in colorizing the background.

I’m using OKLCH color function here because I find it the generally-nicest of all the color models. One very immediate thing it does for us here is that it unlocks P3 colors. That --brand color you see there is actually a bit more bright and vibrant than is even possible in the sRGB color space (like a hex code or any of the more common color functions you may be familiar with, like rgb()).

Applying those colors to the relevant sections gets us a start:

Fonts

We don’t need to go too far down the typography rabbit hole here, as all our design calls for is the header, one input, and a list of to-do items. Keeping it simple and fast (not loading fonts is… fast), is ideal, so:

html {
  font-family: system-ui, sans-serif;
}

You’d think system-ui would be boring, but as a little fun and progressive enhancement, we can use some font-variation-settings on it in the case that the font that loads supports them. It’s not the only one, but Apple’s San Francisco font ships as a variable font, so we can do fun things like stretch it out.

header {
  background: var(--gray800);
  color: white;
  h1 {
    font-variation-settings: "wght" 900, "wdth" 700;
  }
}

I love this look, myself:

SVGs

We need SVG’s for the “check” of the logo and that we’ll use when a user checks off a to-do. I’m kinda thinking we just re-use the same one for both as that just makes good sense. Plus we’ll make a “+” SVG too. It might be tempting to find unicode characters for these, but I’m telling you, just use SVG. The predictable bounding box of SVG will be your friend (no fighting against weird space).

There are loads of resources for finding SVGs to use. I’m usually a Noun Project guy, but if we needed, say, 10 or more icons I’d probably try to find a more cohesive set somewhere. These are also so simple that could (should) draw them ourselves (even directly in code), but we’ll save that for another day.

Ultimately, we’ll just get our hands on a “check” icon and use it in the logo…

<header>
  <svg ... class="svg-check site-logo">
    <polyline ... />
  </svg>
  <h1>TODOs</h1>
</header>

… in our template literal for each to-do …

HTML += `<li id="${todo.id}">
  ${todo.title}
  <button aria-label="Complete" class="button-complete">
    <svg ... class="svg-check">
      <polyline ... />
    </svg>
  </button>
</li>`;

… and then our “+” in the form:

<form id="todo-form" class="todo-form">
  <label>
    <span class="screen-reader-text">New TODO</span>
    <input type="text" name="todo">
  </label>
  <button id="button-add-todo" class="button-add-todo">
    <svg ... class="svg-plus">
      <g>
        <line ... />
        <line ... />
      </g>
    </svg>
  </button>
</form>

There is a lot to know about SVG so I’m really just dropping the basic usage in here. I’ve chosen to drop the SVG code right “inline” with the HTML because I generally think that’s best because it’s fast and offers the most styling control. If you wanna dig more into SVG, I did write a book about it one time you could check out.

With the SVG’s I chose in there:

Adding To-Do Interactions

When we add a new to-do, I want to give the adding button a temporary class so we can do something fun with it. So we’ll use a saved reference to that button and do that:

buttonAddTodo.classList.add("added");

But this is an opportunity! We could add a different class upon a failure. What failure? Not entering any text, for one. We could make the call not to allow entering blank to-dos, which at this point I think is a decent idea.

So the form event handler then becomes like:

form.addEventListener("submit", (event) => {
  event.preventDefault();
  // Don't allow empty todo
  if (!form[0].value) {
    buttonAddTodo.classList.add("shake");
    return;
  }
  addTodo(event);
  form.reset();
});

Now check out these little @keyframe animations I can do with those classes:

.button-add-todo {
  ...

  &.shake {
    rotate: 0deg;
    transform-origin: bottom right;
    animation: shake 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  }
  &.added {
    transform-origin: center center;
    animation: added 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  }
}
@keyframes shake {
  50% {
    rotate: -12deg;
  }
}
@keyframes added {
  50% {
    rotate: 1turn;
    translate: 0 50px;
  }
}

A little fun, maybe?

Let’s be careful to remove those classes after the animation happens, so that another animation can be triggered freshly when needed:

buttonAddTodo.addEventListener("animationend", () => {
  buttonAddTodo.classList.remove("shake", "added");
});

Completing To-Do Interactions

We’ve got a big ol’ box sitting there waiting for to-do items to be checked. And now that we’re using SVG, my temptation is to do a little fun “line drawing” with that. That is, see the checkbox be “drawn” as we complete the to-do, before removing it.

Here’s the trick.

First we go find the SVG itself in our template literal, and add this special pathLength attribute to the actual shape:

<svg width="20" height="20" viewBox="0 0 241.44 259.83" class="svg-check">
  <polyline pathLength="1" points="16.17 148.63 72.17 225.63 225.17 11.63" />
</svg>

This just makes animating it much easier.

Then we set and animate some specific SVG properties to do the drawing:

.todo-list {
  ...
  li {
    ...
    .svg-check {
      opacity: 0;
    }
    &.complete {
      .svg-check {
        opacity: 1;
        stroke-dasharray: 1;
        stroke-dashoffset: 1;
        animation: do-check 1s infinite alternate;
      }
    }
  }
}

@keyframes do-check {
  from {
    stroke-dashoffset: 1;
  }
  to {
    stroke-dashoffset: 0;
  }
}

Check out the drawing effect at work:

The trick to getting the animation to run before being removed is just to … wait.

function removeTodo(event) {
  const listItem = event.target.parentElement;
  listItem.classList.toggle("complete");
  setTimeout(() => {
    TODOs = TODOs.filter((todo) => todo.id !== event.target.parentElement.id);
    localStorage["data"] = JSON.stringify(TODOs);
    buildUI();
  }, 1000);
}

You can see I’m just waiting for a second there, which is about the same, a little longer, than the animation itself. We could tie it to the animationend event here also, but for some reason it felt better to me to leave this bit of business logic in the code here rather than tying it to a CSS thing.

Really smoooooth completions

What if we remove a to-do from the middle of the list. It’ll be jerky won’t it? It certainly could be. This is one of those classic situations in CSS where being able to “animate to/from auto” would be nice. In this case, we want to animate from “however tall a list item is” to zero. That way the bottom items in the list would slide up, making a much more comfortable animation. But honestly, even animating the height here feels like slight of hand, what I actually want is just not to think very hard about it and for the list items to just slide up into place, if at all possible.

Turns out this is a lovely use-case for View Transitions. Without getting very deep at all into it, essentially all we need to do is:

  1. Give all our to-dos a unique view-transition-name
  2. When we update the DOM, e.g. buildUI(), wrap that in a startViewTransition() function.

So our template literal is updated to be like…

  TODOs.forEach((todo) => {
    HTML += `<li id="${todo.id}" style="view-transition-name: list-item-${todo.id};">

We already have a unique ID so we’ll steal that for the dual purpose here.

Then wrap our DOM updating function like:

if (!document.startViewTransition) {
  buildUI();
} else {
  document.startViewTransition(() => {
    buildUI();
  });
}

That’s the same and progressive-enhancement friendly way to do it. It’s kind of required here, as that startViewTransition will throw in browsers that don’t support it and it could break our whole site.

Look at us know:

This is friggin’ cool if you ask me.

So far

Just a little bit more functionality to go!

Article Series

]]>
https://frontendmasters.com/blog/building-a-todo-app-from-scratch-step-4-styling-interactive-choices/feed/ 0 1020
Playing with Raster to SVG to 3D Tools https://frontendmasters.com/blog/playing-with-raster-to-svg-to-3d-tools/ https://frontendmasters.com/blog/playing-with-raster-to-svg-to-3d-tools/#respond Fri, 09 Feb 2024 18:07:22 +0000 https://frontendmasters.com/blog/?p=773 I happen to have bookmarked a few new-to-me SVG tools that all seemed to fit together in interesting ways, so I thought I would have a play and share.

Raster to SVG

One type of these tools is Raster-to-SVG. That is, taking something like a photo and “vectorizing” it. I happen to have my multivitamin bottle on my desk, so I “drew” that (no laughing).

Now I want that in vector. So my first step was SVGcode, a Progressive Web App (PWA) from Thomas Steiner that I think is pretty cool. (Feels in the spirit of SVGOMG for optimization that I also think is cool.) First I took a “better” straight on photo of the “drawing”.

The I dropped this on SVGcode. It died. That is, it “spun” for a while then ultimately threw some “Out of Bounds” error. I think the graphic was just too big. All I did was crop it down to closer around the drawings edges and tried again, and then it worked. After fiddling all the knobs, this was the best I could do.

There must have been too much shading on the photo on the paper so that crud at the bottom right seemed unavoidable. Certainly a cleaner picture (perhaps even a good scan) would have avoided this. That stuff is pretty easy to grab and clean up in something like Adobe Illustrator.

But — if you’re going to use Illustrator, might as well us it’s raster-to-vector abilities anyway. I tried that, just dragging the image onto a new document and clicking the Image Trace button. This ended up cleaner was faster than SVGcode (but, SVGcode is free, and Illustrator is expensive, so there’s that.)

I also had Vectorizer.AI on my list to try, and this was the result there:

It’s clearly designed for full color raster images and conversion and had no options to control anything. Certainly worth trying if you’re shooting for that look.

SVG to 3D

The other tools on my list was this Vector to 3D tool. So all I did was upload the nicest vectorized copy I had, and…

Ha! There was a solid white background behind my SVG, so I had to go and delete that. But the look with just lines wasn’t really doing much, so I fiddled with the SVG such that it was transparent around the object, but the objects had backgrounds. Then I uploaded that and fiddled with the controls and got this!

I think that’s pretty darn cool! There is a lot of controls on this app, and more to unlock with paid plans, so that’s certainly worth checking out.

The other 3D tool I had bookmarked that took SVG was design.glyf.space. I took my same SVG asset, the one with the colored backgrounds, and dropped it on there, choosing the simplest looking model first. This is probably easiest to understand as a video since the model I picked kinda went with stacked flat layers:

This tool is more in the AI-weirdness category so you’ll have to have a good play with the different possibilities there.

I don’t even know what to tell you there.

]]>
https://frontendmasters.com/blog/playing-with-raster-to-svg-to-3d-tools/feed/ 0 773
Making your SVG icons CSS masks instead of inline HTML or backgrounds has some benefits https://frontendmasters.com/blog/mask-your-icons/ https://frontendmasters.com/blog/mask-your-icons/#respond Fri, 02 Feb 2024 18:30:22 +0000 https://frontendmasters.com/blog/?p=704 I’m a fan of just chucking SVG icons right into the HTML where you need them:

<svg class="icon icon-eye-dropper" width="16" height="16" viewBox="0 0 16 16">
  <path d="..." />
  <path d="..." />
</svg>

This has lots of benefits, like instant loading with no additional requests, and is 100% styleable, including all the internal parts (i.e. multicolor icons).

But, putting your SVG icons in CSS can be advantageous too. This converter is handy. For example:

.icon-menu {
  width: 1rem; height: 1rem;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="..."/></svg>');
}

You give up some styling control there — the icon is just going to look how that SVG looks, barring stuff like filter and background-blend-mode.

The advantage here is that your icons are now cached within your CSS and shared across everywhere on your site that is using that CSS file. If you have the (rare) issue of your DOM being too big, this would help.

A minor alteration to this is to use a mask instead of background-image to show the icon. This actually frees up the background to use to color it. Still getting the caching, just a bit easier to style now.

David Bushell just blogged about this and says:

This is technique perfect for:

  • Button icons
  • List bullet points
  • Custom checkbox icons

Especically when the icon changes colour depending on interactive state, or light and dark theming, for example.

]]>
https://frontendmasters.com/blog/mask-your-icons/feed/ 0 704
Draw on Scroll https://frontendmasters.com/blog/draw-on-scroll/ https://frontendmasters.com/blog/draw-on-scroll/#respond Mon, 29 Jan 2024 23:14:01 +0000 https://frontendmasters.com/blog/?p=685 Clever idea idea from Rauno Freiberg then explained by Brad Woods.

To create a line drawn on scroll effect, we render a line down the page. Then, use a clip-path to show only part of it. As the user scrolls, we translate the clip-path down the page.

]]>
https://frontendmasters.com/blog/draw-on-scroll/feed/ 0 685