Front End Web Development

Flash’s Web Tech Legacy

Css Tricks - Mon, 01/11/2021 - 11:54am

Tiffany B. Brown on how Flash paved the way for some things we might think of as fairly modern web technologies:

Flash wasn’t just good for playing multimedia. It was also good for manipulating it. Using ActionScript, you could pan audio, adjusting the input for the user’s left and right speakers, perhaps when they shifted their mouse from one side of the screen to the other. Now we can do that using the Web Audio API.

Web Storage and the localStorage/ sessionStorage APIs are conceptually similar to SharedObjects, or Flash cookies. And the demand for rich web typography enabled by Flash and sIFR, helped bring us @font-face, WOFF, and web-licensed fonts.

Flash also popularized the idea of the cross-domain policy file, an XML file that specifies whether one domain can read the content and data of another. It’s a precursor to cross-origin resource sharing (CORS), which uses HTTP headers instead of an XML configuration file.

Mike Davidson had some nostolgic thoughts as well:

Most technology is transitional if your window is long enough. Cassette tapes showed us that taking our music with us was possible. Tapes served their purpose until compact discs and then MP3s came along. Then they took their rightful place in history alongside other evolutionary technologies. Flash showed us where we could go, without ever promising that it would be the long-term solution once we got there.

Direct Link to ArticlePermalink

The post Flash’s Web Tech Legacy appeared first on CSS-Tricks.

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

Animating with Lottie

Css Tricks - Mon, 01/11/2021 - 6:09am

I believe animation on the web is not only fun, but engaging in such a way that it has converted site visitors into customers. Think of the “Like” button on Twitter. When you “like” a tweet, tiny colorful bubbles spread around the heart button while it appears to morph into a circle around the button before settling into the final “liked” state, a red fill. It would be much less exciting if the heart just went from being outlined to filled. That excitement and satisfaction is a perfect example of how animation can be used to enhance user experience.

This article is going to introduce the concept of rendering Adobe After Effects animation on the web with Lottie, which can make advanced animations— like that Twitter button — achievable.

Bodymovin is a plugin for Adobe After Effects that exports animations as JSON, and Lottie is the library that renders them natively on mobile and on the web. It was created by Hernan Torrisi. If you’re thinking Oh, I don’t use After Effects, this article is probably not for me, hold on just a moment. I don’t use After Effects either, but I’ve used Lottie in a project.

You don’t have to use Lottie to do animation on the web, of course. An alternative is to design animations from scratch. But that can be time-consuming, especially for the complex types of animations that Lottie is good at. Another alternative is using GIF animations, which are limitless in the types of animation they can display, but are typically double the size of the JSON files that Bodymovin produces.

So let’s jump into it and see how it works.

Get the JSON

To use Lottie, we need a JSON file containing the animation from After Effects. Luckily for us, Icons8 has a lot of free animated icons here in JSON, GIF, and After Effects formats.

Add the script to HTML

We also need to get the Bodymovin player’s JavaScript library in our HTML, and call its loadAnimation() method. The fundamentals are demonstrated here:

<div id="icon-container"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"> <script> var animation = bodymovin.loadAnimation({ // animationData: { /* ... */ }, container: document.getElementById('icon-container'), // required path: 'data.json', // required renderer: 'svg', // required loop: true, // optional autoplay: true, // optional name: "Demo Animation", // optional }); </script> Activate the animation

After the animation has loaded in the container, we can configure it to how we want it to be activated and what action should activate it with event listeners. Her are the properties we have to work with:

  • container: the DOM element that the animation is loaded into
  • path: the relative path of the JSON file that contains the animation
  • renderer: the format of the animation, including SVG, canvas, and HTML
  • loop: boolean to specify whether or not the animation should loop
  • autoplay: boolean to specify whether or not the animation should play as soon as it’s loaded
  • name: animation name for future referencing

Note in the earlier example that the animationData property is commented out. It is mutually exclusive with the path property and is an object that contains the exported animated data.

Let’s try an example

I’d like to demonstrate how to use Lottie with this animated play/pause control icon from Icons8:

The Bodymovin player library is statically hosted here and can be dropped into the HTML that way, but it is also available as a package:

npm install lottie-web ### or yarn add lottie-web

And then, in your HTML file, include the script from the dist folder in the installed package. You could also import the library as a module from Skypack:

import lottieWeb from "https://cdn.skypack.dev/lottie-web";

For now, our pause button is in a loop and it also plays automatically:

CodePen Embed Fallback

Let’s change that so the animation is triggered by an action.

Animating on a trigger

If we turn autoplay off, we get a static pause icon because that was how it was exported from After Effects.

CodePen Embed Fallback

But, worry not! Lottie provides some methods that can be applied to animation instances. That said, the documentation of the npm package is more comprehensive.

We need to do a couple things here:

  • Make it show as the “play” state initially.
  • Animate it to the “paused” state on click
  • Animate between the two on subsequent clicks.

The goToAndStop(value, isFrame) method is appropriate here. When the animation has loaded in the container, this method sets the animation to go to the provided value, then stop there. In this situation, we’d have to find the animation value when it’s at play and set it. The second parameter specifies whether the value provided is based on time or frame. It’s a boolean type and the default is false (i.e., time-based value). Since we want to set the animation to the play frame, we set it to true.

A time-based value sets the animation to a particular point in the timeline. For example, the time value at the beginning of the animation, when it’s paused, is 1. However, a frame-based value sets the animation to a particular frame value. A frame, according to TechTerms, is an individual picture in a sequence of images. So, if I set the frame value of the animation to 5, the animation goes to the fifth frame in the animation (the “sequence of images” in this situation).

CodePen Embed Fallback

After trying different values, I found out the animation plays from frame values 11 through 16. Hence, I chose 14 to be on the safe side.

Now we have to set the animation to change to pause when the user clicks it, and play when the user clicks it again. Next, we need the playSegments(segments, forceFlag) method. The segments parameter is an array type containing two numbers. The first and second numbers represent the first and last frame that the method should read, respectively. The forceFlag is a boolean that indicates whether or not the method should be fired immediately. If set to false, it will wait until the animation plays to the value specified as the first frame in the segments array before it is triggered. If true, it plays the segments immediately.

CodePen Embed Fallback

Here, I created a flag to indicate when to play the segments from play to pause, and from pause to play. I also set the forceFlag boolean to true because I want an immediate transition.

So there we have it! We rendered an animation from After Effects to the browser! Thanks Lottie!

Canvas?

I prefer to use SVG as my renderer because it supports scaling and I think it renders the sharpest animations. Canvas doesn’t render quite as nicely, and also doesn’t support scaling. However, if you want to use an existing canvas to render an animation, there are some extra things you’d have to do.

Doing more

Animation instances also have events that can also be used to configure how the animation should act.

For example, in the Pen below, I added two event listeners to the animation and set some text to be displayed when the events are fired.

CodePen Embed Fallback

All the events are available on the npm package’s docs. With that I say, go forth and render some amazing animations!

The post Animating with Lottie appeared first on CSS-Tricks.

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

CSS Snapshot 2020

Css Tricks - Mon, 01/11/2021 - 6:08am

I think it’s great that the CSS Working Group does these. It’s like planting a flag in the ground saying this is what CSS looks like at this specific point in time. They do specifically say it’s not for us CSS authors though…

This document collects together into one definition all the specs that together form the current state of Cascading Style Sheets (CSS) as of 2020. The primary audience is CSS implementers, not CSS authors, as this definition includes modules by specification stability, not Web browser adoption rate.

Remember “CSS3”? That was the closest thing we had to a “snapshot” that was designed for CSS authors (and learners). Because CSS3 was so wildly successful, we saw a short round of enthusiasm for CSS4, me included. There is zero marketing panache on that snapshot page, which is exactly what CSS4 would need to succeed. Remember, HTML5 and friends (including CSS3) even had fancy logos!

If someone were to say to me “Chris, when CSS3 came around, I boned up on all that, but I haven’t kept up with CSS since, what should I learn?” I’d say “That’s a damn fine question, developer that has a normal healthy relationship with technology.” But honestly, I might struggle to answer cohesively.

I’d say: Uhm, CSS grid for sure. Custom properties. Clipping and Offset paths I suppose. prefers-reduced-motion. I dunno. There are probably like 100 things, but there is no great single reference point to see them all together.

I’ll work on putting a list together. I don’t think I’ll have the gumption to call it CSS4, but at least I’ll be able to answer that question. Feel free to suggest ideas in the comments.

The post CSS Snapshot 2020 appeared first on CSS-Tricks.

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

Painters Tape and Fault Tolerance

Css Tricks - Fri, 01/08/2021 - 2:48pm

Snipping the top bit of Nicholas C. Zakas’s Top of the Month newsletter (go sign up!), with permission.

One of my favorite things in the world is painters tape (also called masking tape). It seems like something silly: some tape you put on a wall when you’re painting to avoid getting paint on the wall. The tape doesn’t have a strong adhesive, so it can be pulled back off the wall without damaging it. What I love about painters tape is the philosophy behind it: painting is messy, and rather than trying to avoid making a mess, painters tape allows you to make a mess initially and then clean it up easily. Even the best, most talented painter is going to splatter some paint here and there, get distracted, or otherwise end up with paint going where it shouldn’t. It’s a lot faster, easier, and less frustrating to use painters tape to cover up areas where paint is likely to go and then remove the tape to create a nice, clean, finished area. What does this have to do with software engineering?

Painters tape is all about a concept called fault tolerance. Instead of expecting everything to go well, you instead expect that there will be mistakes. When you expect there to be mistakes, you make decisions not to avoid all mistakes but rather to easily recover when a mistake occurs. Got paint where it shouldn’t be? It doesn’t matter if that spot was covered by painters tape. Forgot to put on the painters tape? Now that mistake is a bigger deal. As software engineers, we can think the same way with the code we write.

Making your code fault tolerant is about asking yourself the question: how will this fail? Not if it will fail, but assuming that it will fail, and in which ways will it fail?

The post Painters Tape and Fault Tolerance appeared first on CSS-Tricks.

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

`aspect-ratio` is going to deprecate FitVids

Css Tricks - Fri, 01/08/2021 - 11:23am

Jen was just tweetin’ about how the latest Safari Technical Preview has aspect-ratio. Looks like Chrome and Firefox both have it behind a flag, so with Safari joining the party, we’ll all have it soon.

I played with it a while back. It’s awesome and much needed. There are ways to make `aspect-ratio` boxes, but they largely revolve around “padding hacks.”

Dave is excited about being released from jail:

Yesssss! Soon I will be released from my Open Source prison!

Seeing it working in Edge Dev 89 (M1) as well. https://t.co/bSKrWEPQyE

— Dave Rupert (@davatron5000) January 7, 2021

Once we can rely on it, FitVids (which I use on literally every site I make in one form or another) can entirely go away in favor of a handful of CSS applied directly to the elements (usually videos-in-<iframe>s).

FitVids 2021:

CodePen Embed Fallback

The post `aspect-ratio` is going to deprecate FitVids appeared first on CSS-Tricks.

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

Svelte and Spring Animations

Css Tricks - Fri, 01/08/2021 - 5:51am

Spring animations are a wonderful way to make UI interactions come to life. Rather than merely changing a property at a constant rate over a period of time, springs allow us to move things using spring physics, which gives the impression of a real thing moving, and can appear more natural to users.

I’ve written about spring animations previously. That post was based on React, using react-spring for the animations. This post will explore similar ideas in Svelte.

CSS devs! It’s common to think of easing when it comes to controling the feel of animations. You could think of “spring” animations as a subcategory of easing that are based on real-world physics.

Svelte actually has springs built into the framework, without needing any external libraries. We’ll rehash what was covered in the first half my previous post on react-spring. But after that, we’ll take a deep-dive into all the ways these springs can be used with Svelte, and leave the real world implementation for a future post. While that may seem disappointing, Svelte has a number of wonderful, unique features with no counterpart in React, which can be effectively integrated with these animation primitives. We’re going to spend some time talking about them.

One other note: Some of the demos sprinkled throughout may look odd because I configured the springs to be extra “bouncy” to create more obvious effect. If you the code for any of them, be sure to find a spring configuration that works for you.

Here’s a wonderful REPL Rich Harris made to show all the various spring configurations, and how they behave.

A quick primer on Svelte Stores

Before we start, let’s take a very, very quick tour of Svelte stores. While Svelte’s components are more than capable of storing and updating state, Svelte also has the concept of a store, which allows you to store state outside of a component. Since Svelte’s Spring API uses Stores, we’ll quickly introduce the salient parts here.

To create an instance of a store, we can import the writable type, and create it like so:

import { writable } from "svelte/store"; const clicks = writable(0);

The clicks variable is a store that has a value of 0. There’s two ways to set a new value of a store: the set and update methods. The former receives the value to which you’re setting the store, while the latter receives a callback, accepting the current value, and returning the new value.

function increment() { clicks.update(val => val + 1); } function setTo5() { clicks.set(5); }

State is useless if you can’t actually consume it. For this, stores offer a subscribe method, which allows you to be notified of new values — but when using it inside of a component, you can prefix the store’s name with the $ character, which tells Svelte to not only display the current value of the store, but to update when it changes. For example:

<h1>Value {$clicks}</h1> <button on:click={increment}>Increment</button> <button on:click={setTo5}>Set to 5</button>

Here’s a full, working example of this code. Stores offer a number of other features, such as derived stores, which allow you to chain stores together, readable stores, and even the ability to be notified when a store is first observed, and when it no longer has observers. But for the purposes of this post, the code shown above is all we need to worry about. Consult the Svelte docs or interactive tutorial for more info.

A crash course on springs

Let’s walk through a quick introduction of springs, and what they accomplish. We’ll take a look at a simple UI that changes a presentational aspect of some elements — opacity and transform — and then look at animating that change.

This is a minimal Svelte component that toggles the opacity of one <div>, and toggles the x-axis transform of another (without any animation).

<script> let shown = true; let moved = 0; const toggleShow = () => (shown = !shown); const toggleMove = () => (moved = moved ? 0 : 500); </script> <div style="opacity: {shown ? 1 : 0}">Content to toggle</div> <br /> <button on:click={toggleShow}>Toggle</button> <hr /> <div class="box" style="transform: translateX({moved}px)">I'm a box.</div> <br /> <button on:click={toggleMove}>Move it!</button>

These changes are applied instantly, so let’s look at animating them. This is where springs come in. In Svelte, a spring is a store that we set the desired value on, but instead of instantly changing, the store internally uses spring physics to gradually change the value. We can then bind our UI to this changing value, to get a nice animation. Let’s see it in action.

<script> import { spring } from "svelte/motion"; const fadeSpring = spring(1, { stiffness: 0.1, damping: 0.5 }); const transformSpring = spring(0, { stiffness: 0.2, damping: 0.1 }); const toggleFade = () => fadeSpring.update(val => (val ? 0 : 1)); const toggleTransform = () => transformSpring.update(val => (val ? 0 : 500)); const snapTransform = () => transformSpring.update(val => val, { hard: true }); </script> <div style="opacity: {$fadeSpring}">Content to fade</div> <br /> <button on:click={toggleFade}>Fade Toggle</button> <hr /> <div class="box" style="transform: translateX({$transformSpring}px)">I'm a box.</div> <br /> <button on:click={toggleTransform}>Move it!</button> <button on:click={snapTransform}>Snap into place</button>

We get our spring function from Svelte, and set up different spring instances for our opacity, and transform animations. The transform spring config is purposefully set up to be extra springy, to help show later how we can temporarily turn off spring animations, and instantly apply desired changes (which will come in handy later). At the end of the script block are our click handlers for setting the desired properties. Then, in the HTML, we bind our changing values directly to our elements… and that’s it! That’s all there is to basic spring animations in Svelte.

The only remaining item is the snapTransform function, where we set our transform spring to its current value, but also pass an object as the second argument, with hard: true. This has the effect of immediately applying the desired value with no animation at all.

This demo, as well as the rest of the basic examples we’ll look at in this post, is here:

Animating height

Animating height is trickier than other CSS properties, since we have to know the actual height to which we’re animating. Sadly, we can’t animate to a value of auto. That wouldn’t make sense for a spring, since the spring needs a real number so it can interpolate the correct values via spring physics. And as it happens, you can’t even animate auto height with regular CSS transitions. Fortunately, the web platform gives us a handy tool for getting the height of an element: a ResizeObserver, which enjoys pretty good support among browsers.

Let’s start with a raw height animation of an element, producing a “slide down” effect that we gradually refine in other examples. We’ll be using ResizeObserver to bind to an element’s height. I should note that Svelte does have an offsetHeight binding that can be used to more directly bind an element’s height, but it’s implemented with some <iframe> hacks that cause it to only work on elements that can receive children. This would probably be good enough for most use cases, but I’ll use a ResizeObserver because it allows some nice abstractions in the end.

First, we’ll bind an element’s height. It’ll receive the element and return a writable store that initializes a ResizeObserver, which updates the height value on change. Here’s what that looks like:

export default function syncHeight(el) { return writable(null, (set) => { if (!el) { return; } let ro = new ResizeObserver(() => el && set(el.offsetHeight)); ro.observe(el); return () => ro.disconnect(); }); }

We’re starting the store with a value of null, which we’ll interpret as “haven’t measured yet.” The second argument to writable is called by Svelte when the store becomes active, which it will be as soon as it’s used in a component. This is when we fire up the ResizeObserver and start observing the element. Then, we return a cleanup function, which Svelte calls for us when the store is no longer being used anywhere.

Let’s see this in action:

<script> import syncHeight from "../syncHeight"; import { spring } from "svelte/motion"; let el; let shown = false; let open = false; let secondParagraph = false; const heightSpring = spring(0, { stiffness: 0.1, damping: 0.3 }); $: heightStore = syncHeight(el); $: heightSpring.set(open ? $heightStore || 0 : 0); const toggleOpen = () => (open = !open); const toggleSecondParagraph = () => (secondParagraph = !secondParagraph); </script> <button on:click={ toggleOpen }>Toggle</button> <button on:click={ toggleSecondParagraph }>Toggle More</button> <div style="overflow: hidden; height: { $heightSpring }px"> <div bind:this={el}> <div>...</div> <br /> {#if secondParagraph} <div>...</div> {/if} </div> </div>

Our el variable holds the element we’re animating. We tell Svelte to set it to the DOM element via bind:this={el}. heightSpring is our spring that holds the height value of the element when it’s open, and zero when it’s closed. Our heightStore is what keeps it up to date with the element’s current height. el is initially undefined, and syncHeight returns a junk writable store that basically does nothing. As soon as el is assigned to the <div> node, that line will re-fire — thanks to the $: syntax — and get our writable store with the ResizeObserver listening.

Then, this line:

$: heightSpring.set(open ? $heightStore || 0 : 0);

…listens for changes to the open value, and also changes to the height value. In either case, it updates our spring store. We bind the height in HTML, and we’re done!

Be sure to remember to set overflow to hidden on this outer element so the contents are properly clipped as the elements toggles between its opened and closed states. Also, changes to the element’s height also animate into place, which you can see with the “Toggle More” button. You can run this in the embedded demo in the previous section.

Note that this line above:

$: heightStore = syncHeight(el);

…currently causes an error when using server-side rendering (SSR), as explained in this bug. If you’re not using SSR you don’t need to worry about it, and of course by the time you read this that bug may have been fixed. But the workaround is to merely do this:

let heightStore; $: heightStore = syncHeight(el);

…which works but is hardly ideal.

We probably don’t want the <div> to spring open on first render. Also, the opening spring effect is nice, but when closing, the effect is janky due to some content flickering. We can fix that. To prevent our initial render from animating, we can use the { hard: true } option we saw earlier. Let’s change our call to heightSpring.set to this:

$: heightSpring.set(open ? $heightStore || 0 : 0, getConfig($heightStore));

…and then see about writing a getConfig function that returns an object with the hard property that was set to true for the first render. Here’s what I came up with:

let shown = false; const getConfig = val => { let active = typeof val === "number"; let immediate = !shown && active; //once we've had a proper height registered, we can animate in the future shown = shown || active; return immediate ? { hard: true } : {}; };

Remember, our height store initially holds null and only gets a number when the ResizeObserver starts running. We capitalize on this by checking for an actual number. If we have a number, and we haven’t yet shown anything, then we know to show our content immediately, and we we do that by setting the immediate value. That value ultimately triggers the hard config value in the spring, which we saw before.

Now let’s tweak the animation to be a bit less, well, springy when we close our content. That way, things won’t flicker when they close. When we initially created our spring, we specified stiffness and damping, like so

const heightSpring = spring(0, { stiffness: 0.1, damping: 0.3 });

It turns out the spring object itself maintains those properties, which can be set anytime. Let’s update this line:

$: heightSpring.set(open ? $heightStore || 0 : 0, getConfig($heightStore));

That detects changes to the open value (and the heightStore itself) to update the spring. Let’s also update the spring’s settings based on whether we’re opening or closing. Here’s what it looks like:

$: { heightSpring.set(open ? $heightStore || 0 : 0, getConfig($heightStore)); Object.assign( heightSpring, open ? { stiffness: 0.1, damping: 0.3 } : { stiffness: 0.1, damping: 0.5 } ); }

Now when we get a new open or height value, we call heightSpring.set just like before, but we also set stiffness and damping values on the spring that are applied based on whether the element is open. If it’s closed, we set damping up to 0.5, which reduces the springiness. Of course, you’re welcome to tweak all these values and configure them as you’d like! You can see this in the “Animate Height Different Springs” section of the demo.

You might notice our code is starting to grow pretty quickly. We’ve added a lot of boilerplate to cover some of these use cases, so let’s clean things up. Specifically, we’ll make a function that creates our spring and that also exports a sync function to handle our spring config, initial render, etc.

import { spring } from "svelte/motion"; const OPEN_SPRING = { stiffness: 0.1, damping: 0.3 }; const CLOSE_SPRING = { stiffness: 0.1, damping: 0.5 }; export default function getHeightSpring() { const heightSpring = spring(0); let shown = false; const getConfig = (open, val) => { let active = typeof val === "number"; let immediate = open && !shown && active; // once we've had a proper height registered, we can animate in the future shown = shown || active; return immediate ? { hard: true } : {}; }; const sync = (open, height) => { heightSpring.set(open ? height || 0 : 0, getConfig(open, height)); Object.assign(heightSpring, open ? OPEN_SPRING : CLOSE_SPRING); }; return { sync, heightSpring }; }

There’s a lot of code here, but it’s all the code we’ve been writing so far, just packaged into a single function. Now our code to use this animation is simplified to just this

const { heightSpring, sync } = getHeightSpring(); $: heightStore = syncHeight(el); $: sync(open, $heightStore);

You can see in the “Animate Height Cleanup” section of the demo.

Some Svelte-specific tricks

Let’s pause for a moment and consider some ways Svelte differs from React, and how we might leverage that to improve what we have even further.

First, the stores we’ve been using to hold springs and change height values are, unlike React’s hooks, not tied to component rendering. They’re plain JavaScript objects that can be consumed anywhere. And, as alluded to above, we can imperatively subscribe to them so that they manually observe changing values.

Svelte also something called actions. These are functions that can be added to a DOM element. When the element is created, Svelte calls the function and passes the element as the first argument. We can also specify additional arguments for Svelte to pass, and provide an update function for Svelte to re-run when those values change. Another thing we can do is provide a cleanup function for Svelte to call when it destroys the element.

Let’s put these tools together in a single action that we can simply drop onto an element to handle all the animation we’ve been writing so far:

export default function slideAnimate(el, open) { el.parentNode.style.overflow = "hidden"; const { heightSpring, sync } = getHeightSpring(); const doUpdate = () => sync(open, el.offsetHeight); const ro = new ResizeObserver(doUpdate); const springCleanup = heightSpring.subscribe((height) => { el.parentNode.style.height = `${ height }px`; }); ro.observe(el); return { update(isOpen) { open = isOpen; doUpdate(); }, destroy() { ro.disconnect(); springCleanup(); } }; }

Our function is called with the element we want to animate, as well as the open value. We’ll set the element’s parent to have overflow: hidden. Then we use the same getHeightSpring function from before, set up our ResizeObserver, etc. The real magic is here.

const springCleanup = heightSpring.subscribe((height) => { el.parentNode.style.height = `${height}px`; });

Instead of binding our heightSpring to the DOM, we manually subscribe to changes, then set the height ourselves, manually. We wouldn’t normally do manual DOM updates when using a JavaScript framework like Svelte but, in this case, it’s for a helper library, which is just fine in my opinion.

In the object we’re returning, we define an update function which Svelte will call when the open value changes. We update the original argument to this function, which the function closes over ( i.e. creates a closure around) and then calls our update function to sync everything. Svelte calls the destroy function when our DOM node is destroyed.

Best of all, using this action is a snap:

<div use:slideAnimate={open}>

That’s it. When open changes, Svelte calls our update function.

Before we move on, let’s make one other tweak. Notice how we remove the springiness by changing the spring config when we collapse the pane with the “Toggle” button; however, when we make the element smaller by clicking the “Toggle More” button, it shrinks with the usual springiness. I dislike that, and prefer shrinking sizes move with the same physics we’re using for collapsing.

Let’s start by removing this line in the getHeightSpring function:

Object.assign(heightSpring, open ? OPEN_SPRING : CLOSE_SPRING);

That line is inside the sync function that getHeightSpring created, which updates our spring settings on every change, based on the open value. With it gone, we can start our spring with the “open” spring config:

const heightSpring = spring(0, OPEN_SPRING);

Now let’s change our spring settings when either the height of our content changes, or when the open value changes. We already have the ability to observe both of those things changing — our ResizeObserver callback fires when the size of the content changes, and the update function of our action fires whenever open changes.

Our ResizeObserver callback can be changed, like this:

let currentHeight = null; const ro = new ResizeObserver(() => { const newHeight = el.offsetHeight; const bigger = newHeight > currentHeight; if (typeof currentHeight === "number") { Object.assign(heightSpring, bigger ? OPEN_SPRING : CLOSE_SPRING); } currentHeight = newHeight; doUpdate(); });

currentHeight holds the current value, and we check it on size changes to see which direction we’re moving. Next up is the update function. Here’s what it looks like after our change:

update(isOpen) { open = isOpen; Object.assign(heightSpring, open ? OPEN_SPRING : CLOSE_SPRING); doUpdate(); },

Same idea, but now we’re only checking whether open is true or false. You can see these iterations in the “Slide Animate” and “Slide Animate 2” sections of the demo.

Transitions

We’ve talked about animating items already on the page so far, but what about animating an object when it first renders? And when it un-mounts? That’s called a transition, and it’s built into Svelte. The docs do a superb job covering the common use cases, but there’s one thing that’s not yet (directly) supported: spring-based transitions.

/explanation Note that what Svelte calls a “transition” and what CSS calls a “transition” are very different things. CSS means transitioning one value to another. Svelte is referring to elements as they “transition” into and out of the DOM entirely (something that CSS doesn’t help with much at all).

To be clear, the work we’re doing here is made for adding spring-based animations into Svelte’s transitions. This is not currently supported, so it requires some tricks and workarounds that we’ll get into. If you don’t care about using springs, then Svelte’s built-in transitions can be used, which are significantly simpler. Again, check the docs for more info.

The way transitions work in Svelte is that we provide a duration in milliseconds (ms) along with an optional easing function, then Svelte provides us a callback with a value running from 0 to 1, representing how far along the transition is, and we turn that into whatever CSS we want. For example:

const animateIn = () => { return { duration: 2000, css: t => `transform: translateY(${t * 50 - 50}px)` }; };

…is used like this:

<div in:animateIn out:animateOut class="box"> Hello World! </div>

When that <div> first mounts, Svelte:

  • calls our animateIn function,
  • rapidly calls the CSS function on our resulting object ahead of time with values from 0 to 1,
  • collects our changing CSS result, then
  • compiles those results into a CSS keyframes animation, which it then applies to the incoming <div>.

This means that our animation will run as a CSS animation — not as JavaScript on the main thread — offering a nice performance boost for free.

The variable t starts at 0, which results in a translation of -50px. As t gets closer to 1, the translation approaches 0, its final value. The out transition is about the same, but in reverse, with the added feature of detecting the box’s current translation value, starting from there. So, if we add it then quickly remove it, the box will start to leave from its current position rather than jumping ahead. However, if we then re-add it while it’s leaving, it will jump, something we’ll talk about in just moment.

You can run this in the “Basic Transition” section of the demo.

Transitions, but with springs

While there’s a number of easing functions that alter the flow of an animation, there’s no ability to directly use springs. But what we could do is find some way to run a spring ahead of time, collect the resulting values, and then, when our css function is called with the a t value running from 0 to 1, look up the right spring value. So, if t is 0, we obviously need the first value from thespring. When t is 0.5, we want the value right in the middle, and so on. We also need a duration, which is number_of_spring_values * 1000 / 60 since there’s 60 frames per second.

We won’t write that code here. Instead, we’ll use the solution that already exists in the svelte-helpers library, a project I started. I grabbed one small function from the Svelte codebase, spring_tick, then wrote a separate function to repeatedly call it until it’s finished, collecting the values along the way. That, along with a translation from t to the correct element in that array (or a weighted average if there’s not a direct match), is all we need. Rich Harris gave a helping hand on the latter, for which I’m grateful.

Animate in

Let’s pretend a big red <div> is a modal that we want to animate in, and out. Here’s what an animateIn function looks like:

import { springIn, springOut } from "svelte-helpers/animation"; const SPRING_IN = { stiffness: 0.1, damping: 0.1 }; const animateIn = node => { const { duration, tickToValue } = springIn(-80, 0, SPRING_IN); return { duration, css: t => `transform: translateY(${ tickToValue(t) }px)` }; };

We feed the values we want to spring to, as well as our spring config to the springIn function. That gives us a duration, and a function for translating the current tickToValue into the current value to apply in the CSS. That’s it!

Animate out

Closing the modal is the same thing, with one small tweak

const SPRING_OUT = { stiffness: 0.1, damping: 0.5, precision: 3 }; const animateOut = node => { const current = currentYTranslation(node); const { duration, tickToValue } = springOut(current ? current : 0, 80, SPRING_OUT); return { duration: duration, css: t => `transform: translateY(${ tickToValue(t) }px)` }; };

Here, we’re check the modal’s current translation position, then use that as a starting point for the animation. This way, if the user opens and then quickly closes the modal, it’ll exit from its current position, rather than teleporting to 0, and then leaving. This works because the animateOut function is called when the element un-mounts, at which point we generate the object with the duration property and css function so the animation can be computed.

Sadly, it seems re-mounting the object while it’s in the process of leaving does not work, at least well. The animateIn function is not called de novo, but rather the original animation is re-used, which means it’ll always start at -80. Fortunately this almost certainly would not matter for a typical modal component, since a modal is usually removed by clicking on something, like the background overlay, meaning we are unable to re-show it until that overlay has finished animating out. Besides, repeatedly adding and removing an element with bidirectional transitions might make for a fun demo, but they’re not really common in practice, at least in my experience.

One last quick note on the outgoing spring config: You may have noticed that I set the precision ridiculously high (3 when the default is 0.01). This tells Svelte how close to get to the target value before deciding it is “done.” If you leave the default at 0.01, the modal will (almost) hit its destination, then spend quite a few milliseconds imperceptibly getting closer and closer before deciding it’s done, then remove itself from the DOM. This gives the impression that the modal is stuck, or otherwise delayed. Moving the precision to a value of 3 fixes this. Now the modal animates to where it should go (or close enough), then quickly goes away.

More animation

Let’s add one final tweak to our modal example. Let’s have it fade in and out while animating. We can’t use springs for this, since, again, we need to have one canonical duration for the transition, and our motion spring is already providing that. But spring animations usually make sense for items actually moving, and not much else. So let’s use an easing function to create a fade animation.

If you need help picking the right easing function, be sure to check out this handy visualization from the Svelte docs. I’ll be using the quintOut and quadIn functions.

import { quintOut, quadIn } from "svelte/easing";

Our new animateIn function looks pretty similar. Our css function does what it did before, but also runs the tickToValue value through the quintOut easing function to get our opacity value. Since t runs from 0 to 1 during an in transition, and 1 to 0 during an out transition, we don’t have to do anything further to it before applying to opacity.

const SPRING_IN = { stiffness: 0.1, damping: 0.1 }; const animateIn = node =>; { const { duration, tickToValue } = springIn(-80, 0, SPRING_IN); return { duration, css: t => { const transform = tickToValue(t); const opacity = quintOut(t); return `transform: translateY(${ transform }px); opacity: ${ opacity };`; } }; };

Our animateOut function is similar, except we want to grab the element’s current opacity value, and force the animation to start there. So, if the element is in the process of fading in, with an opacity of, say, 0.3, we don’t want to reset it to 1, and then fade it out. Instead, we want to fade it out from 0.3.

Multiplying that starting opacity by whatever value the easing function returns accomplishes this. If our t value starts at 1, then 1 * 0.3 is 0.3. If t is 0.95, we do 0.95 * 0.3 to get a value, which is a little less than 0.3, and so on.

Here’s the function:

const animateOut = node => { const currentT = currentYTranslation(node); const startOpacity = +getComputedStyle(node).opacity; const { duration, tickToValue } = springOut( currentT ? currentT : 0, 80, SPRING_OUT ); return { duration, css: t => { const transform = tickToValue(t); const opacity = quadIn(t); return `transform: translateY(${ transform }px); opacity: ${ startOpacity * opacity }`; } }; };

You can run this example in the demo with the “Spring Transition With Fade component.

Parting thoughts

Svelte is a lot of fun! In my (admittedly limited) experience, it tends to provide extremely simple primitives, and then leaves you to code up whatever you need. I hope this post has helped explain how the spring animations can be put to good use in your web applications.

And, hey, just a quick reminder to consider accessibility when working with springs, just as you would do with any other animation. Pairing these techniques with something like prefers-reduced-motion can ensure that only folks who prefer animations are the ones who get them.

The post Svelte and Spring Animations appeared first on CSS-Tricks.

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

Creating CSS APIs without JavaScript With the datasette-css-properties plugin

Css Tricks - Thu, 01/07/2021 - 1:37pm

Simon Willison has a project called Datasette, an open source multi-tool for exploring and publishing data. I’m not sure I’m qualified to explain it, but it’s like a tool to make handling data easier and doing more — through the web — with data you have. Like making that data queryable and giving it an API.

I would think, typically, you’d get the results of an API call against your data in something useful, like JSON. But Simon made a plugin that outputs the results as CSS custom properties instead, and blogged it:

It’s very, very weird—it adds a .css output extension to Datasette which outputs the result of a SQL query using CSS custom property format. This means you can display the results of database queries using pure CSS and HTML, no JavaScript required!

Here’s what I said just recently in “Custom Properties as State”:

This makes me think that a CDN-hosted CSS file like this could have other useful stuff, like today’s date for usage in pseudo content, or other special time-sensitive stuff. Maybe the phase of the moon? Sports scores?! Soup of the day?!

And Simon is like, how about roadside attractions?

CodePen Embed Fallback

My brain automatically worries about the accessibility of that, but… aren’t pseudo-elements fairly and reliably read in screen readers these days? You still can’t select the text though, or find-on-page, which are both usability and accessibility issues, so don’t consider this like a real thing that you really do for production work with unknown users.

His blog post demonstrates a slightly more dynamic example where the time of day outputs a different color. That makes me think of @property and declaring types for custom properties. I think this gets a smidge more useful when you can use the values that come back as specific syntaxes.

The post Creating CSS APIs without JavaScript With the datasette-css-properties plugin appeared first on CSS-Tricks.

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

Win a Copy of Zell Liew’s Learn JavaScript Course

Css Tricks - Thu, 01/07/2021 - 5:48am

Zell Liew is giving away 10 free copies of his Learn JavaScript course, and entering the giveaway is pretty easy: sign up for his newsletter. I’ve personally subscribed for some time now and all I get is as occasional hand-written email with useful JavaScript gems. It’s sort of like winning no matter if you get the course or not.

If you jump down to the lesson plan, you’ll see that the course is chock-full of material, so this is no cheapie. There’s something like 300 lessons in there. Plus, Zell is a master at explaining incredibly complex things in a way that’s accessible to anyone, whether you’re getting started or looking to brush up your skills. Believe me, I’ve edited his work and I learn something from it every time.

That’s the spiel! Enter before Wednesday, January 13, to throw your hat in the ring.

Direct Link to ArticlePermalink

The post Win a Copy of Zell Liew’s Learn JavaScript Course appeared first on CSS-Tricks.

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

Thidrekssaga X: The battle of Gransport

QuirksBlog - Thu, 01/07/2021 - 12:47am

Just now I published part X of the Thidrekssaga: The battle of Gransport.

After helping Attila and Erka, Dietrich asks for their help to return to Bern. He sets out with a large army of Huns and battles Ermenrik at Gransport. His brother Diether and the two sons of Attila and Erka die in the battle, and Dietrich is so upset that he returns to Soest without following up on his (apparent) victory.

Enjoy.

Styling Code In and Out of Blocks

Css Tricks - Wed, 01/06/2021 - 11:31am

We’ll get to that, but first, a long-winded introduction.

I’m still not in a confident place knowing a good time to use native web components. The templating isn’t particularly robust, so that doesn’t draw me in. There is no state management, and I like having standard ways of handling that. If I’m using another library for components anyway, seems like I would just stick with that. So, at the moment, my checklist is something like:

  • Not using any other JavaScript framework that has components
  • Templating needs aren’t particularly complex
  • Don’t need particularly performant re-rendering
  • Don’t need state management

I’m sure there is tooling that helps with these things and more (the devMode episode with some folks from Stencil was good), but if I’m going to get into tooling-land, I’d be extra tempted to go with a framework, and probably not framework plus another thing with a lot of overlap.

The reasons I am tempted to go with native web components are:

  • They are native. No downloads of frameworks.
  • The Shadow DOM is a true encapsulation in a way a framework can’t really do.
  • I get to build my own HTML element that I use in HTML, with my own API design.

It sorta seems like the sweet spot for native web components is design system components. You build out your own little API for the components in your system, and people can use them in a way that is a lot safer than just copy and paste this chunk of HTML. And I suppose if consumers of the system wanted to BYO framework, they could.

So you can use like <our-tabs active-tab="3"> rather than <div class="tabs"> ... <a href="#3" class="tab-is-active">. Refactoring the components certainly gets a lot easier as changes percolate everywhere.

I’ve used them here on CSS-Tricks for our <circle-text> component. It takes the radius as a parameter and the content via, uh, content, and outputs an <svg> that does the trick. It gave us a nice API for authoring that abstracted away the complexity.

So!

It occurred to me a “code block” might be a nice use-case for a web component.

  • The API would be nice for it, as you could have attributes control useful things, and the code itself as the content (which is a great fallback).
  • It doesn’t really need state.
  • Syntax highlighting is a big gnarly block of CSS, so it would be kinda cool to isolate that away in the Shadow DOM.
  • It could have useful functionality like a “click to copy” button that people might enjoy having.

Altogether, it might feel like a yeah, I could use this kinda component.

This probably isn’t really production ready (for one thing, it’s not on npm or anything yet), but here’s where I am so far:

CodePen Embed Fallback

Here’s a thought dump!

  • What do you do when a component depends on a third-party lib? The syntax highlighting here is done with Prism.js. To make it more isolated, I suppose you could copy and paste the whole lib in there somewhere, but that seems silly. Maybe you just document it?
  • Styling web components doesn’t feel like it has a great story yet, despite the fact that Shadow DOM is cool and useful.
  • Yanking in pre-formatted text to use in a template is super weird. I’m sure it’s possible to do without needing a <pre> tag inside the custom element, but it’s clearly much easier if you grab the content from the <pre>. Makes the API here just a smidge less friendly (because I’d prefer to use the <code-block> alone).
  • I wonder what a good practice is for passing along attributes that another library needs. Like is data-lang="CSS" OK to use (feels nicer), and then convert it to class="language-css" in the template because that’s what Prism wants? Or is it better practice to just pass along attributes as they are? (I went with the latter.)
  • People complain that there aren’t really “lifecycle methods” in native web components, but at least you have one: when the thing renders: connectedCallback. So, I suppose you should do all the manipulation of HTML and such before you do that final shadowRoot.appendChild(node);. I’m not doing that here, and instead am running Prism over the whole shadowRoot after it’s been appended. Just seemed to work that way. I imagine it’s probably better, and possible, to do it ahead of time rather than allow all the repainting caused by injecting spans and such.
  • The whole point of this is a nice API. Seems to me thing would be nicer if it was possible to drop un-escaped HTML in there to highlight and it could escape it for you. But that makes the fallback actually render that HTML which could be bad (or even theoretically insecure). What’s a good story for that? Maybe put the HTML in HTML comments and test if <!-- is the start of the content and handle that as a special situation?

Anyway, if you wanna fork it or do anything fancier with it, lemme know. Maybe we can eventually put it on npm or whatever. We’ll have to see how useful people think it could be.

The post Styling Code In and Out of Blocks appeared first on CSS-Tricks.

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

Whack-a-Mole: The CSS Edition

Css Tricks - Wed, 01/06/2021 - 5:39am

We’ve seen the checkbox hack and how it can be used to build a complete state machine in CSS. Today, we’ll take that line of thought a step further and build a simple game of Whack-A-Mole, where the player needs to react quickly to win … all without a touch of JavaScript.

This might seem a little silly in a language that doesn’t have any notion of timers or intervals, but the secret is that CSS does — it’s just packaged in a little feature called CSS Animations.

Take a look at this double-click handler. Note that CSS doesn’t know what a click is, much less a double click:

CodePen Embed Fallback

How does it work? Here’s a recording of the important elements, recolored and slowed down:

The idea is that when you click on the button for the first time, it moves the double-click element in place under the cursor, but it also causes a masking element to start moving up to cover it.

  • If the second click comes quickly enough after the first (as on the left side of the recording), it happens on the double-click (blue) element.
  • Otherwise (as on the right side of the recording), it happens on the masking (yellow) element, which has the effect of a single-click element.

(For an in-depth explanation, take a look at my write-up on the pure-CSS double-click handler here.)

There are two ideas in play here:

  1. Animations can be used to manage states according to a set pattern. (I’m using the term “state” loosely.)
  2. By changing an element’s position, we can change whether or not an user is allowed to take an action.

That’s all we need!

Now, instead of having our target scroll into view, we could instead use animation-timing-function: step-end to have it pop in and out, sort of like a mole in a hole:

CodePen Embed Fallback

I tried a few different options for moving the mole out of the way, and changing its absolute position — here left — seems to work the best. Using a translation would be convenient, but unfortunately leaves Firefox thinking the cursor is on the wrong element because changing transform in the Gecko layout engine doesn’t trigger a re-layout. (Normally, this would be a good thing for performance reasons, but not so much for our little demo!)

With a bit of styling, we can make it look more like a game element:

CodePen Embed Fallback

The “mole” here is basically a restyled CSS label that triggers the checkbox hack. So is the hole. When the mole’s animation takes it over the hole, clicking in the region triggers the mole’s radio input; when the mole is out of the way, clicking triggers the hole’s radio input.

The next step is to put a couple of holes next to each other and have the moles bounce around among them, each on a different negative animation-delay. With a state machine reading off which of the moles have been hit and which of the holes have been collapsed, it turns into a neat little game.

I used short Python scripts to generate both the state machine selectors and the mole keyframes. It gets a bit unwieldy for hand-coding otherwise. This is mostly because CSS also has no concept of random numbers, which leaves us no choice but to hardcode the “random” mole behavior.

CodePen Embed Fallback

There we have it: a complete reaction-based game, entirely in HTML and CSS.

The post Whack-a-Mole: The CSS Edition appeared first on CSS-Tricks.

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

Some Recent Videos About Websites That Are Pretty Good

Css Tricks - Tue, 01/05/2021 - 12:42pm

Here’s a little hodgepodge of videos I’ve recently bookmarked (and watched). I couldn’t decide if each one of them should be a separate blog post or if I should do this combined list post thing. If I had a lot more to say about each video I would have split them off, but I went with the combined post and fewer words here. Let me know in the comments if you have a preference on that kind of thing.

Here’s Tom Scott explaining why the web is such a mess

Cookies. It all cookies fault. Kinda. Also humans.

This is the same Tom that did that super neat video that updated it’s own title with the view count.

I don’t know this person’s name but I appreciated how it gets the details right in this recreation of the Discord sidebar

Except the fact that the :hover was on the <li> itself rather than a link that went somewhere sensible, but hey, I guess it’s slightly better than the actual Discord markup where it’s <div class="listItem_">.

Heydon gets all weird about progressive enhancment

I can’t embed it here because, even though it looks like Heydon switched over to Vimeo rather than just straight up <video> tags, the privacy settings have it locked to briefs.video only.

I liked the point about “the basic layout is not a broken layout,” except, doesn’t it seem like in that exact case it wouldn’t matter if you wrapped the grid-template-rows in a @supports or not? I love that @supports is a thing, and even more so now that we’ve passed the awkward years where @supports itself didn’t have full browser support, but I don’t find myself reaching for it that much, as it’s only really useful if you need to do something different than “just let it not work,” which I don’t find terribly common.

Houssein Djirdeh and Jason Miller get into “modern JavaScript”

We’ve gotta be so careful about compiled code. There is an example where one line of JavaScript gets compiled into 7000 bytes, which is bigger and far slower than intended. Taking care of older browsers when you have a significant number of users using that old browser is a really great thing, but you might be surprised at the browser support of “modern JavaScript” and find you are compiling more than you need to.

The kicker is that you only really control what you write, but probably most of what you ship is third-party. That means npm, which is absolutely loaded with very non-modern JavaScript. The sweet spot, they say, is calling ES 2017 the compile target in general. If you need even older support, use the ol’ differential serving trick.

There’s a blog version of this video, too.

Jessica Chan challenges William Candillon to a CSSBattle

Like literally CSSBattle, the website.

They go back and forth trying to figure out how to make this little spiky virus-looking thing. I totally relate to their approaches! Neither of them are like amazingly clever with either the HTML or CSS — they just try to get it done. That’s why I could never get into CSSBattle myself. I appreciate people’s trickery (duh), but my actual CSS writing style is almost like verbose-on-purpose.

The post Some Recent Videos About Websites That Are Pretty Good appeared first on CSS-Tricks.

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

Custom Properties as State

Css Tricks - Tue, 01/05/2021 - 6:05am

Here’s a fun idea from James Stanley: a CSS file (that presumably updates daily) containing CSS custom properties for “seasonal” colors (e.g. spring is greens, fall is oranges). You’d then use the values to theme your site, knowing that those colors change slightly from day to day.

This is what I got while writing this:

:root { --seasonal-bg: hsl(-68.70967741935485,9.419354838709678%,96%); --seasonal-bgdark: hsl(-68.70967741935485,9.419354838709678%,90%); --seasonal-fg: hsl(-68.70967741935485,9.419354838709678%,30%); --seasonal-hl: hsl(-83.70967741935485,30.000000000000004%,50%); --seasonal-hldark: hsl(-83.70967741935485,30.000000000000004%,35%); }

I think it would be more fun if the CSS file provided was just the custom properties and not the opinionated other styles (like what sets the body background and such). That way you could implement the colors any way you choose without any side effects.

CSS as an API?

This makes me think that a CDN-hosted CSS file like this could have other useful stuff, like today’s date for usage in pseudo content, or other special time-sensitive stuff. Maybe the phase of the moon? Sports scores?! Soup of the day?!

/* <div class="soup">The soup of the day is: </div> */ .soup::after { content: var(--soupOfTheDay); /* lol kinda */ }

It’s almost like a data API that is tremendously easy to use. Pseudo content is even accessible content these days — but you can’t select the text of pseudo-elements, so don’t read this as an actual endorsement of using CSS as a content API.

Custom Property Flexibility

Will Boyd just blogged about what is possible to put in a custom property. They are tremendously flexible. Just about anything is a valid custom property value and then the usage tends to behave just how you think it will.

body { /* totally fine */ --rgba: rgba(255, 0, 0, 0.1); background: var(--rgba); /* totally fine */ --rgba: 255, 0, 0, 0.1; background: rgba(var(--rgba)); /* totally fine */ --rgb: 255 0 0; --a: 0.1; background: rgb(var(--rgb) / var(--a)); } body::after { /* totally fine */ --song: "I need quotes to be pseudo content \A and can't have line breaks without this weird hack \A but still fairly permissive (&#x1f4a7;&#x1f4a7;&#x1f4a7;) "; content: var(--song); white-space: pre; }

Bram Van Damme latched onto that flexiblity while covering Will’s article:

That’s why you can use CSS Custom Properties to:

perform conditional calculations

pass data from within your CSS to your JavaScript

inject skin tone / hair color modifiers onto Emoji 

toggle multiple values with one custom property (--foo: ; hack)

Bram points out this “basic” state-flipping quality that a custom property can pull off:

:root { --is-big: 0; } .is-big { --is-big: 1; } .block { padding: calc( 25px * var(--is-big) + 10px * (1 - var(--is-big)) ); border-width: calc( 3px * var(--is-big) + 1px * (1 - var(--is-big)) ); }

Add a couple of scoops of complexity and you get The Raven (media queries with custom properties).

I’d absolutely love to see something happen in CSS to make this easier. Using CSS custom properties for generic state would be amazing. We could apply arbitrary styles when the UI is in arbitrary states! Think of how useful media queries are now, or that container queries will be, but compounded because it’s arbitrary state, not just state that those things expose.

Bram covered that as well, mentioning what Lea Verou called “higher level custom properties”:

/* Theoretical! */ .square { width: 2vw; padding: 0.25vw; aspect-ratio: 1/1; @if (var(--size) = big) { width: 16vw; padding: 1vw; } } .my-input { @if(var(--pill) = on) { border-radius: 999px; } } About that naming

Will calls them “CSS variables” which is super common and understandable. You’ll read (and I have written) sentences often that are like “CSS variables (a.k.a CSS Custom Properties)” or “CSS Custom Properties (a.k.a CSS Variables.” Šime Vidas recently noted there is a rather correct way to refer to these things: --this-part is the custom property and var(--this-part) is the variable, which comes right from usage in the spec.

JavaScript Library State… Automatically?

I’m reminded of this Vue proposal. I’m not sure if it went anywhere, but the idea is that the state of a component would automatically be exposed as CSS custom properties.

<template> <div class="text">Hello</div> </template> <script> export default { data() { return { color: 'red' } } } </script> <style vars="{ color }"> .text { color: var(--color); } </style>

By virtue of having color as part of the state of this component, then --color is available as state to the CSS of this component. I think that’s a great idea.

What if every time you used useState in React, CSS custom properties were put on the :root and were updated automatically. For example, if you did this:

import React, { useState } from 'https://cdn.skypack.dev/react@^16.13.1'; import ReactDOM from 'https://cdn.skypack.dev/react-dom@^16.13.1'; const App = () => { const [ activeColor, setActiveColor ] = useState("red"); return( <div className="box"> <h1>Active Color: {activeColor}</h1> <button onClick={() => {setActiveColor("red")}}>red</button> <button onClick={() => {setActiveColor("blue")}}>blue</button> </div> ); } ReactDOM.render(<App />, document.getElementById("root"))

And you knew you could do like:

.box { border-color: 2px solid var(--activeColor); }

Because the state automatically mapped itself to a custom property. Someone should make a useStateWithCustomProperties hook or something to do that. #freeidea

Libraries like React and Vue are for building UI. I think it makes a lot of sense that the state that they manage is automatically exposed to CSS.

Could browsers give us more page state as environment variables?

Speaking of state that CSS should know about, I’ve seen quite a few demos that do fun stuff by mapping over things, like the current mouse position or scroll position, over to CSS. I don’t think it’s entirely unreasonable to ask for that data to be natively exposed to CSS. We already have the concept of environment variables, like env(safe-area-inset-top), and I could see that being used to expose page state, like env(page-scroll-percentage) or env(mouseY).

The post Custom Properties as State appeared first on CSS-Tricks.

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

Driver or mechanic?

QuirksBlog - Tue, 01/05/2021 - 5:33am

The tools vs knowledge argument goes something like this:

Random web dev
You don’t have to know CSS. Or basic JavaScript. I mean, our great toolchain has all of that covered, right?
Me
If you’re a web developer you should know something about CSS and JavaScript. What if your tool doesn’t support your use case? Or what if they are terrible for performance?
Random web dev
Nonsense. I’m much more productive with my trusty tools.
Me
But having some basic knowledge is part of being a professional web developer.
Random web dev
I mean, if you drive a car you don’t have to know how it works, right? You just drive it.

Over the past ten years I have heard this drive a car analogy more often than I care to remember. Superficially, it’s an interesting one, but I found I disagree with it.

This article has been translated into Russian.

To me, we are not the drivers of the car called the World Wide Web, but its mechanics. And mechanics should definitely know something about the contraption they’re building and maintaining, right?

In fact, the more I think about it the more I see this as the fundamental question.

What would you prefer to be: a car driver or a car mechanic?

I’m sure a few people’s interest will be piqued by my question. Still, analogies like this one should not be taken too far. They can easily confuse the issue instead of clarifying it.

The problem is that people start to fill in details that the analogy doesn’t support. Exactly what does the car represent? The web, a web site, or the production process? And what does driving mean? Surfing the web, building a site, or something else? And who exactly is symbolised by the mechanic? Or the tools?

The discussion could easily degenerate into a shouting match about the meaning of the analogy while everybody forgets about the actual tools vs knowledge issue the analogy was supposed to clarify.

Something else I noticed over the last twenty years is that people resort to homely comparisons only when they have run out of actual arguments, or when they don’t really understand the issues involved. So they conjure up an image of something that everything can relate to and pretend it settles the discussion.

The question of whether tools are enough to build websites or knowledge of the underlying technologies is also necessary will not be solved by homely car comparisons, but only by actual arguments about the tools vs knowledge issue.

Despite all this I find I like the question I asked above, since it forces you to make a fundamental decision. So I’m going to let it stand, even though it hitches a ride on a potentially confusing car analogy.

As a web developer, would you prefer to be a driver or a mechanic?

The End of Adobe Flash

Css Tricks - Mon, 01/04/2021 - 10:33am

I come to bury Flash, not to praise it, writes Matt May in this excellent thread about the end of Adobe Flash. Not so long ago, web designers used Flash to create striking visuals and animations and games. But shortly after that, it began to replace HTML and CSS which caused a ton of accessibility problems. Most Flash websites weren’t navigable by keyboard and screen readers couldn’t parse them at all.

Matt describes this core problem at the very heart of Flash: the fact that it excluded so many people from the web back then.

The biggest joke of the 2000s around Flash was that it was a tool for making restaurant menus. Flash devs made apps that looked the way restaurateurs wanted them, and sold them in bulk. But how they did it was by faking buttons, scroll bars, etc. It was a picture of an interface.

— Matt May (@mattmay) December 31, 2020

“A picture of an interface” is a good way to think about it. But, overall, I reckon this thread is important because it reinforces the idea that we ought to think about accessibility every single day, regardless of whether we’re working on a large web app, a tiny marketing website, or contributing to an enormous JavaScript framework:

Finally: if you make the platform, you have an immense responsibility to the people who build on it, and the people who use it. In a very real way, you set the upper limit on how many people get to participate equitably within your ecosystem. NEVER, EVER forget this fact.

— Matt May (@mattmay) December 31, 2020

Every day we write code, we’re deciding who is welcome and who is not.

Direct Link to ArticlePermalink

The post The End of Adobe Flash appeared first on CSS-Tricks.

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

Make Your Own Tools

Css Tricks - Mon, 01/04/2021 - 4:59am

Spencer Miskoviak on the Wealthfront blog:

By creating custom DevTools specific to an app, they can operate at an even higher abstraction to handle things like user interactions, or debugging tracking events. While this requires building and maintaining the custom DevTools, it also means it can be tailored to the needs of the app and engineers to streamline development.

I think it’s super cool and smart to build custom tools for your team of developers. Even if custom tools are just for yourself they can be a productivity boon. But by building custom tools for your whole team, and opening the door to their ideas, that’s extra smart and compounds the value.

Spencer showcased a variety of different tools they have, all under the umbrellas of a UI popup widget thingy:

  • Shows current branch and CI status
  • Fills out forms, performs user actions, switches between users
  • Highlights components

Clever stuff.

We don’t have a fancy UI widget like that at CodePen, but do have some productivity-helping fuctionality sprinkled into the app. For example, many forms have a prefill button that only shows up for devs:

And we have a custom tool for our support inbox that gives context to the users and content that the support ticket references:

Not to mention a whole protected admin area on the site itself to perform a whole slew of admin and developer focused tasks:

I think the “component highlighter” that Spencer talked about is particularly neat:

React DevTools can be helpful in seeing what parts of the current page are which components, but that’s not on-page like this. I think it would be rad to have a little &#x1f517; next to each title that would open that file in VS Code.

Speaking of building your own tools, Shawn Wang wrote “You’re Allowed To Make Your Own Tools” recently:

Even the greatest software has parts that aren’t so great for you. But the difference between you and everyone else is that you can code.

Shawn talks about things like…

  • Building your own custom stylesheets
  • Building a UI query generator
  • Building your own CLIs (I’m reminded of Mina Markham’s dotfiles)
  • Building your own proxies

Shawn wrote his own dang proxy for Google Search Results to optimize them and present them how he likes:

Once in a while, I’m in the mood to focus on tooling, which leads me to do stuff like when I decided to “Run Gulp as You Open a VS Code Project using VS Code Tasks” which I had to learn all about and struggle through weird problems. I’d think a great DevOps person at a company would be all over stuff like this—constantly thinking of developer experience for their own people.

I even scripted the opening of a text-based multi-player video game I play not long ago to save myself some time.

And speaking of building your own tools generally, I think of Dick Proenneke’s in Alone in the Wilderness documentary. In this intro clip, you can hear Dick talk about quite literally building tools, which was useful for him as he didn’t need to hand-haul them deep into the Alaskan wilderness.

&#x1f6e0;

The post Make Your Own Tools appeared first on CSS-Tricks.

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

Houdini.how

Css Tricks - Fri, 01/01/2021 - 5:35am

Nice site from Google (and guest contributors) with a bunch of fun demos of what Houdini can do. Plus a write-up from Una. These are all Paint API demos. Houdini is technically a group of seven things that are all pretty cool, and the Paint API is just one of them. Paint is fun, but things will start getting really weird when we get the Layout API, methinks.

I think the value of Houdini becomes more clear when you see an example of something this fun in so little code (Una’s confetti):

CodePen Embed Fallback

You import it, you use it. You control it with CSS custom properties. That one above is less than 1 kB.

I feel like the early story with Houdini was that it will be these really low-level API’s that will pretty much be used by platform people to prototype new platform features in a safer way. Now the story is more like: confetti!!!! I like both stories.

Firefox still feels like a big wildcard here. They say it’s “worth prototyping” but that feels like a bit of a stretch now after their platform team layoffs. The Paint API is polyfillable, for example:

(async function() { if (CSS['paintWorklet'] === undefined) await import('https://unpkg.com/css-paint-polyfill') CSS.paintWorklet.addModule('https://unpkg.com/houdini-tesla/dist/worklet.js'); })()

And that polyfill is only ~5kB, so maybe that’s fine?

Vincent De Oliveira’s demos have long been my favorite though. While they are all fairly artsy, they also feel like somewhat practical UI things that you might wanna try to do on the web, but have traditionally felt a bit hard to pull off nicely.

And here’s a nice written tutorial from Estelle Weyl, Ruth John, and Chris Mills that goes not only into the Paint API, but the Typed OM too (less shiny, super practical).

Direct Link to ArticlePermalink

The post Houdini.how appeared first on CSS-Tricks.

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

Centering in CSS

Css Tricks - Fri, 01/01/2021 - 5:22am


Adam Argyle has a post over on web.dev digging into this. He starts with the assumption that you need to do vertical centering and horizontal centering. It’s that vertical centering that has traditionally been a bit trickier for folks, particularly when the height of the content is unknown.

We have a complete guide to centering that covers a wide variety of situations like a decision tree.

Adam details five(!) methods for handling it, even getting into centering unknown vertical and horizontal dimensions, plus a handful of other restraints like language direction and multiple elements. I guess all the silly jokes about the difficulty of centering things in CSS need to be updated. Maybe they should poke fun about how many great ways there are to center things in CSS.

Adam does a great job listing out the pros and cons of all the techniques, and demonstrating them clearly. There is also a video. He picks “the gentle flex” as the winning approach:

.gentle-flex { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1ch; }

You can always find it in my stylesheets because it’s useful for both macro and micro layouts. It’s an all-around reliable solution with results that match my expectations. Also, because I’m an intrinsic sizing junkie, I tend to graduate into this solution. True, it’s a lot to type out, but the benefits it provides outweighs the extra code.

Remember that when you’re “centering in CSS” it’s not always within these extreme situations. Let’s look at another situation, just for fun. Say you need to horizontally center some inline-*¹ elements… text-align: center; gets you there as a one-liner:

CodePen Embed Fallback

But what if you need to center the parent of those items? You’d think you could do a classic margin: 0 auto; thing, and you can, but it’s likely the parent is block-level and thus either full-width or has a fixed width. Say instead you want it to be as wide as the content it contains. You could make the parent inline-*, but then you need another parent in which to set the text-align on to get it centered.

Stefan Judis talked about this recently. The trick is to leave the element block-level, but use width: fit-content;

CodePen Embed Fallback

The ol’ gentle flex could have probably gotten involved here too, but we’d need an additional parent again. Always something to think about.

  1. What I mean by inline-* is: inline, inline-block, inline-flex, inline-grid, or inline-table. Did I miss any?

Direct Link to ArticlePermalink

The post Centering in CSS appeared first on CSS-Tricks.

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

Thank You (2020 Edition)

Css Tricks - Thu, 12/31/2020 - 12:12pm

Heck of a year, eh? Like we do every year, I’d like to give you a huge thanks for reading CSS-Tricks, and recap the year. More downs than ups, all told. Here at CSS-Tricks, it was more of a wash. Allow me to me share some numbers, milestones, and thoughts with you about our journey of 2020.

Let’s do the basic numbers

The site saw 94m pageviews this year. Last year we lost a smidge of pageviews (from 91m to 90m), so it’s nice to see that number go back up again, setting a new high record. Now I don’t have to tell myself stories like “jeez usage of browser extensions that block Google Analytics must be up.” Hitting 100m pageviews will be a nice milestone some future year. This number, long term, climbs very slowly. It’s a good reminder to me how much time, money, and energy are required to just maintain the traffic to a content site, let alone attempt to drive growth.

I have Cloudflare in front of the site this year. I think that’s a good idea generally, but especially now that they have specific technology to make it extra good. I’m a fan of pushing as much to the edge as we can, and now it’s not only static assets that are CDN-served but the content as well.

I mention that because now I have access to Cloudflare analytics, so I can compare across tools. I can’t see a whole year of data on Cloudflare, but comparing last month’s unique visitors between the two services, I see 6,847,979 unique visitors on Cloudflare compared to 6,125,272 sessions (or 7,449,921 unique page views — I’m not sure which is directly comparable) on Google Analytics. They seem pretty close. Closer than I thought they would be, since Google Analytics requires client-side JavaScript and Cloudflare analytics are, presumably, gathered at the DNS level, and thus not blockable like client-side JavaScript. I’ve turned off the WordPress-powered analytics for now, as having three places for analytics seemed a bit much, although I might flip them back on because, without them, I can’t see top on-site search results, which I definitely like to have.

Traffic that comes from organic¹ search was 77.7% this year, versus 80.6% last year. A 3% swing feels pretty large, yet almost entirely accounted for by a 3% swing from 9% to 12% in “direct” traffic. I have no idea what to make of that, but I suppose I don’t mind a better diversification in sources.

I find these end-of-year looks at the numbers sorta fun, but I’m generally not a big analytics guy. Last year I wrote:

There is a bunch of numbers I just don’t feel like looking at this year. We’ve traditionally done stuff like what countries people are from, what browsers they use (Chrome-dominant), mobile usage (weirdly low), and things like that. This year, I just don’t care. This is a website. It’s for everyone in the world that cares to read it, in whatever country they are in and whatever browser they want to.

I feel even more apathetic toward generalized analytics numbers this year. I think analytics are awesome when you have a question in mind you want an answer to, where the numbers help find that answer. Or for numbers that are obviously important and impactful to your site that you determined for yourself. Just poking around at numbers can fool you into thinking you’re gathering important insights and making considered decisions when you’re kinda just picking your nose.

One question that does interest me is what the most popular content is by traffic (we’ll get to that in a bit). Looking at the most popular content (by actual traffic) gives me a sense of what brings people here. Bringing traffic to the site is a goal. While we don’t generally sell sponsorship/advertising based on page views directly, those numbers matter to sponsors and fairly correlate directly to what we can charge.

Another bit of data I care about is what people search for that bring them to the site. Here’s how that breaks down:

  • Top 10: Various combinations of terms that have to do with flexbox and grid layout
  • Mixed into the top 20: Various alterations of the site’s name

From there, 10-100 are “random specific CSS things.” Beyond 100 is where SVG, JavaScript, design stuff, and CSS are sprinkled into the mix. The 251st ranked search term is the first time React shows up. The insight here is that: (1) our layout guides continue to do very well, (2) a lot of people like to get to the site first, then find what they need, and (3) searches for library-specific content isn’t a particularly common way to land here.

Top posts of the year

Thanks to Jacob, we can look at analytics data based on the year the content was written (and a few other bits of metadata).

Here’s an interesting thing. In 2019, articles written in 2019 generated about 6.3m page views. Those same articles, in 2020, generated 7.3m page views. Neat, huh? The articles drove more traffic as they aged.

Articles written in 2020 generated 12m pageviews. Here’s the top 10:

  1. CSS-Only Carousel
  2. Fluid Width Video (cheat, as this was written a few years ago as a stand-alone page, and I only moved it to the blog in 2020)
  3. How to Create an Animated Countdown Timer With HTML, CSS and JavaScript
  4. A Complete Guide to Links and Buttons
  5. The Hooks of React Router
  6. A Complete Guide to Dark Mode on the Web
  7. Neumorphism and CSS
  8. A Complete Guide to Data Attributes
  9. Why JavaScript is Eating HTML
  10. Front-End Challenges

Interesting backstory on that list. I dug into Google Analytics and created that Top 10 list based on the data it was showing me in a custom report, which Jacob taught me to do. Serendipitously, Jacob emailed me right after that to show me the Top 10 that he calculated, and it was slightly different than mine. Then I went back and re-ran my custom report, and it was slightly different than both the others. Weird! Jacob knew why. When you’re looking at a huge dataset like this in Google Analytics, they will only sample the data used for the report. It will show you a “yellow badge” and tell you what percentage of the data the report is based on. 500,000 sessions is the max, which is only 0.7% of what we needed to look at. That’s low enough that it accounted for the different lists. Jacob had already done some exporting and jiggering of the data to account for that, so the above list is what’s accurate to 100% of all sessions.

The top articles on the entire site from any year:

  1. The Complete Guide to Flexbox
  2. The Complete Guide to Grid
  3. Using SVG
  4. Perfect Full Page Background Images
  5. The Shapes of CSS
  6. Media Queries for Standard Devices
  7. Change Color of SVG on Hover
  8. CSS Triangle
  9. How to Scale SVG
  10. Using @font-face

Nothing from the Almanac made the top 10, but interestingly, right after that, the next 20 are so are heavily sprinkled with random articles from there. All told, the Almanac is about 14.8% of all traffic.

Two other things that I think are very cool that we did with content is:

  1. Published Jay Hoffman’s series on Web History, which include audio adaptations from Jeremy Keith that are served as a podcast.
  2. Published our end-of-year series like we did last year.

One of the many reasons I love being on WordPress is how easy it is to spin up series like these. All we did was toss up a category-specific template file and slapped on a little custom CSS. That gives the posts a cool landing page all to themselves, but are still part of the rest of the “flow” of the site (RSS, search, tags, etc.).

COVID

Perhaps the slight increase in traffic was COVID-related? With more people turning to coding as a good option for working from home, maybe there are more people searching for coding help. Who knows.

What we definitely found was that nearly every sponsor we work with, understandably, tightened their belt. Add in advertising plans with us that were reduced or canceled and, as a rough estimate, I’d say we’re down 25% in sponsorship revenue. That would be pretty devastating except for the fact that we try not to keep too many eggs in one basket.

Feels like a good time to mention that if your company is doing well and could benefit from reaching people who build websites, let’s talk sponsorship.

I’m trying to diversify revenue somewhat, even on this site alone. For example…

eCommerce

We’ve been using WooCommerce here to sell a couple of things.

Posters, mainly. A literal physical bit of printed paper sent through the post to you. The posters are unique designs made from the incredible illustrations that Lynn Fisher created for the flexbox and grid guides. We essentially “drop ship” them, meaning they are printed and shipped on-demand by another company. So, you buy them from this site, but another company takes it from there and does all the rest of it. That’s appealing because the amount of work is so low, but there are two major downsides: (1) Customer support for the orders becomes our problem and I’d say ~20% of orders need some kind of support, and (2) the profit margin is fairly slim compared to what it would be if we took on more of the work ourselves.

We also sell MVP Supporter memberships, which are great in that they don’t require much ongoing work. The trick there is just making sure it is valuable enough for people to buy, which we’ll have to work more on over time. For now, you basically get a book, video downloads, and no ads.

MVP Supporter

For being an MVP supporter of CSS-Tricks, you get: No ads, extra content, easier commenting, and extreme satisfaction for being cool.

$20.00 / year Shop now

Loose math, eCommerce made up 5% of the lost revenue. Long way to go there, but it’s probably worth the journey as my guess is that this kind of revenue is less volatile.

I’m also still optimistic about Web Monetization in the long-term (here’s the last post I wrote on it). But right now, it is not a solution that makes for a significant new revenue stream. Optimistically, it’s something like 0.05% of revenue.

Social media

As far a website traffic driver goes, social media isn’t particularly huge at 2.2% of all traffic (down from 2.3% last year). That’s about where it always is, whether or not we put much effort into it over the course of a year, which is exactly why I try not to spend energy there. What little effort we do expend, 95% of it is toward Twitter. We lean on Jetpack’s social automation features, mostly. It is still cool to have @css as a handle, and we are closing in on half a million followers. You’d think that would be worth something. We’ll have to figure that out someday.

When we hand-write Tweets (rare), those are still the ones with the most potential. I only do that when it feels like something fun to do, because even though they can get the most engagement, the time/value thing still just doesn’t make it worthwhile.

Example hand-written tweet

::marker is fantastic and now available in all the modern browsers. https://t.co/EAIb3HHUX3@simevidas notes a favorite use-case (list markers matching size of headers):https://t.co/KVKMCSq4pg pic.twitter.com/b4fei5Uw3V

— CSS-Tricks (@css) December 13, 2020

Most of our tweets are just auto-generated when a new post is published. And we’ve been doing that for so long, I think that’s what the Twitter followers largely expect anyway, and that’s fine. We do have the ability to customize the Tweet before it goes out, which we try to, but usually don’t.

Example Auto-Generated Tweet

A Complete Guide to CSS Functions https://t.co/ty82onsTvA pic.twitter.com/uaZ2YJKQop

— CSS-Tricks (@css) May 4, 2020

The other 5% of effort is Instagram just because it’s kinda fun. I don’t even wanna think about trying to extract direct value from Instagram. Maybe if we had a lot more products for direct sale it would make sense. But for now, just random tips and stuff to hold an audience.

Example Instagram Post View this post on Instagram

A post shared by Chris Coyier (@real_css_tricks)

Screencasts

I did 22 screencasts this year. That’s a lot compared to the last many years! I’m not sure if I’ll be as ambitious in 2021, but I suspect I might be, because my setup at my desk is getting pretty good for doing them and my editing skills are slowly improving. I enjoy doing them, and it’s an occasional income stream (my favorite being pairing up with someone from a company and digging into their technology together). Plus, we got that cool new intro for our videos done by dina Amin.

The screencasts are published on the site and to iTunes as a videocast, but the primary place people watch is YouTube. I guess we could consider YouTube “social media” but I find that screencasts are more like “real content” in a way that I don’t with other social media. They are certainly much more time-consuming to produce and I hope more evergreen than a one-off tweet or something.

Newsletter

We hit 81,765 subscribers to the newsletter. On one hand, that’s a respectable number. On the other, it’s far too low considering how gosh darn good it is! I was hoping we’d hit 100k this year, but I didn’t actually do all that much to encourage signups, so that’s on me. I don’t think we missed a single week, so that’s a win, and considering we were at 65,000 last year, that’s still pretty good growth.

Comments

Y’all left 4,322 comments on the site this year. That’s down a touch from 4,710, but still decent averaging almost 12 a day.

I rollercoaster emotionally about comments. One day thinking they are too much trouble, requiring too much moderation and time to filter the junk. The vitriol can be so intense (on a site about code, wow) that some days I just wanna turn them off. Other times, I’m glad for the extra insight and corrections. Not to mention, hey, that’s content and content is good. We’ve never not had comments, so, hey, let’s keep ’em for now.

I absolutely always encourage your helpful, insightful, and kind comments, and promise to never publish rude or wrong comments (my call).

The forums completely shut down this year (into “read only” mode), so commenting activity from that didn’t exactly make its way over to the blog area. Closing the forums still feels… weird. I liked having a place to send (especially beginners) to ask questions. But, we just do not have the resources (or business model) to support safe and active forums. So closed they will remain, for now.

Goal review
  • 100k on email list. Fail on that one. That was kind of a moonshot anyway, and we never executed any sort of plan to help get there. For example, we could encourage it on social media more. We could attempt to buy ads elsewhere with a call to action to sign up. We could offer incentives to new subscribers somehow. We might do those things, or we might not. I don’t feel strongly enough right now to make it a goal for next year.
  • Two guides. We crushed this one. We published 9 guides. I consider this stuff our most valuable content. While I don’t want to only do this kind of content (because I think it’s fun to think of CSS-Tricks as a daily newspaper-style site as well), I want to put most of our effort here.
  • Have an obvious focus on how-to referential technical content. I think we did pretty good here. Having this in mind all the time both for ourselves and for guest posts meant making sure we were showcasing how to use tech and less focus on things like guest editorials which are, unfortunately, our least useful content. I’d like to be even stricter on this going forward. We’re so far along in our journey on this site. The expectation people have is that this site has answers for their technical front-end questions, so there is no reason not to lean entirely into that.
  • Get on Gutenberg. We also crushed it here. I think in the first month of the year I had us using Gutenberg on new content, and within a few months after that, we had Gutenberg enabled for all posts. It was work! And we still have a long way to go, as most posts on the site haven’t been “converted” into blocks, which is still not a brainless task. But, I consider it a fantastic success. I think Gutenberg is largely a damn pleasure to work with, making content authoring far more pleasurable, productive, and interesting.
New goals
  • Three guides. I know we did nine this year, but the goal was only two. I actually have ideas for three more, so I’ll make three the goal. Related side goal: I’d like to try to make mini-books out of some of these guides and either sell them individually or make it part of the MVP Supporter subscription.
  • Stay focused on how-to technical content around our strengths. Stuff like useful tips. Technical news with context. Advice on best-practices. I want to reign us in a bit more toward our strengths. HTML, CSS, and JavaScript stuff is high on that list of strengths, but not every framework, serverless technology, or build tool is. I’d like us to be more careful about not publishing things unless we can strongly vouch for it.
  • Complete all missing Almanac entries. There are a good 15-20 Almanac articles that could exist that don’t yet. Like we have place-items in there, but not place-content or place-self. Then there is esoteric stuff, like :current, :past, and :future time-dimensional pseudo-classes which, frankly, I don’t even really understand but are a thing. If you wanna help, please reach out.
Wrapping up

Thank you, again, for being a reader of this site. I hope these little peeks at our business somehow help yours. And I really hope 2021 is better than 2020, for all of us.

&#x1f9e1;

  1. I actually prefer my search grass-fed in addition to organic, but ok. ↩️

The post Thank You (2020 Edition) appeared first on CSS-Tricks.

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

Give your Eleventy Site Superpowers with Environment Variables

Css Tricks - Thu, 12/31/2020 - 9:27am

Eleventy is increasing in popularity because it allows us to create nice, simple websites, but also — because it’s so developer-friendly. We can build large-scale, complex projects with it, too. In this tutorial we’re going to demonstrate that expansive capability by putting a together a powerful and human-friendly environment variable solution.

What are environment variables?

Environment variables are handy variables/configuration values that are defined within the environment that your code finds itself in.

For example, say you have a WordPress site: you’re probably going to want to connect to one database on your live site and a different one for your staging and local sites. We can hard-code these values in wp-config.php but a good way of keeping the connection details a secret and making it easier to keep your code in source control, such as Git, is defining these away from your code.

Here’s a standard-edition WordPress wp-config.php snippet with hardcoded values:

<?php define( 'DB_NAME', 'my_cool_db' ); define( 'DB_USER', 'root' ); define( 'DB_PASSWORD', 'root' ); define( 'DB_HOST', 'localhost' );

Using the same example of a wp-config.php file, we can introduce a tool like phpdotenv and change it to something like this instead, and define the values away from the code:

<?php $dotenv = Dotenv\Dotenv::createImmutable(__DIR__); $dotenv->load(); define( 'DB_NAME', $_ENV['DB_NAME'] ); define( 'DB_USER', $_ENV['DB_USER'] ); define( 'DB_PASSWORD', $_ENV['DB_PASSWORD'] ); define( 'DB_HOST', $_ENV['DB_HOST'] );

A way to define these environment variable values is by using a .env file, which is a text file that is commonly ignored by source control.

We then scoop up those values — which might be unavailable to your code by default, using a tool such as dotenv or phpdotenv. Tools like dotenv are super useful because you could define these variables in an .env file, a Docker script or deploy script and it’ll just work — which is my favorite type of tool!

The reason we tend to ignore these in source control (via .gitignore) is because they often contain secret keys or database connection information. Ideally, you want to keep that away from any remote repository, such as GitHub, to keep details as safe as possible.

Getting started

For this tutorial, I’ve made some starter files to save us all a bit of time. It’s a base, bare-bones Eleventy site with all of the boring bits done for us.

Step one of this tutorial is to download the starter files and unzip them wherever you want to work with them. Once the files are unzipped, open up the folder in your terminal and run npm install. Once you’ve done that, run npm start. When you open your browser at http://localhost:8080, it should look like this:

Also, while we’re setting up: create a new, empty file called .env and add it to the root of your base files folder.

Creating a friendly interface

Environment variables are often really shouty, because we use all caps, which can get irritating. What I prefer to do is create a JavaScript interface that consumes these values and exports them as something human-friendly and namespaced, so you know just by looking at the code that you’re using environment variables.

Let’s take a value like HELLO=hi there, which might be defined in our .env file. To access this, we use process.env.HELLO, which after a few calls, gets a bit tiresome. What if that value is not defined, either? It’s handy to provide a fallback for these scenarios. Using a JavaScript setup, we can do this sort of thing:

require('dotenv').config(); module.exports = { hello: process.env.HELLO || 'Hello not set, but hi, anyway &#x1f44b;' };

What we are doing here is looking for that environment variable and setting a default value, if needed, using the OR operator (||) to return a value if it’s not defined. Then, in our templates, we can do {{ env.hello }}.

Now that we know how this technique works, let’s make it happen. In our starter files folder, there is a directory called src/_data with an empty env.js file in it. Open it up and add the following code to it:

require('dotenv').config(); module.exports = { otherSiteUrl: process.env.OTHER_SITE_URL || 'https://eleventy-env-vars-private.netlify.app', hello: process.env.HELLO || 'Hello not set, but hi, anyway &#x1f44b;' };

Because our data file is called env.js, we can access it in our templates with the env prefix. If we wanted our environment variables to be prefixed with environment, we would change the name of our data file to environment.js . You can read more on the Eleventy documentation.

We’ve got our hello value here and also an otherSiteUrl value which we use to allow people to see the different versions of our site, based on their environment variable configs. This setup uses Eleventy JavaScript Data Files which allow us to run JavaScript and return the output as static data. They even support asynchronous code! These JavaScript Data Files are probably my favorite Eleventy feature.

Now that we have this JavaScript interface set up, let’s head over to our content and implement some variables. Open up src/index.md and at the bottom of the file, add the following:

Here’s an example: The environment variable, HELLO is currently: “{{ env.hello }}”. This is called with {% raw %}{{ env.hello }}{% endraw %}.

Pretty cool, right? We can use these variables right in our content with Eleventy! Now, when you define or change the value of HELLO in your .env file and restart the npm start task, you’ll see the content update.

Your site should look like this now:

You might be wondering what the heck {% raw %} is. It’s a Nunjucks tag that allows you to define areas that it should ignore. Without it, Nunjucks would try to evaluate the example {{ env.hello }} part.

Modifying image base paths

That first example we did was cool, but let’s really start exploring how this approach can be useful. Often, you will want your production images to be fronted-up with some sort of CDN, but you’ll probably also want your images running locally when you are developing your site. What this means is that to help with performance and varied image format support, we often use a CDN to serve up our images for us and these CDNs will often serve images directly from your site, such as from your /images folder. This is exactly what I do on Piccalilli with ImgIX, but these CDNs don’t have access to the local version of the site. So, being able to switch between CDN and local images is handy.

The solution to this problem is almost trivial with environment variables — especially with Eleventy and dotenv, because if the environment variables are not defined at the point of usage, no errors are thrown.

Open up src/_data/env.js and add the following properties to the object:

imageBase: process.env.IMAGE_BASE || '/images/', imageProps: process.env.IMAGE_PROPS,

We’re using a default value for imageBase of /images/ so that if IMAGE_BASE is not defined, our local images can be found. We don’t do the same for imageProps because they can be empty unless we need them.

Open up _includes/base.njk and, after the <h1>{{ title }}</h1> bit, add the following:

<img src="{{ env.imageBase }}mountains.jpg{{ env.imageProps }}" alt="Some lush mountains at sunset" />

By default, this will load /images/mountains.jpg. Cool! Now, open up the .env file and add the following to it:

IMAGE_BASE=https://assets.codepen.io/174183/ IMAGE_PROPS=?width=1275&height=805&format=auto&quality=70

If you stop Eleventy (Ctrl+C in terminal) and then run npm start again, then view source in your browser, the rendered image should look like this:

<img src="https://assets.codepen.io/174183/mountains.jpg?width=1275&height=805&format=auto&quality=70" alt="Some lush mountains at sunset" />

This means we can leverage the CodePen asset optimizations only when we need them.

Powering private and premium content with Eleventy

We can also use environment variables to conditionally render content, based on a mode, such as private mode. This is an important capability for me, personally, because I have an Eleventy Course, and CSS book, both powered by Eleventy that only show premium content to those who have paid for it. There’s all-sorts of tech magic happening behind the scenes with Service Workers and APIs, but core to it all is that content can be conditionally rendered based on env.mode in our JavaScript interface.

Let’s add that to our example now. Open up src/_data/env.js and add the following to the object:

mode: process.env.MODE || 'public'

This setup means that by default, the mode is public. Now, open up src/index.md and add the following to the bottom of the file:

{% if env.mode === 'private' %} ## This is secret content that only shows if we’re in private mode. This is called with {% raw %}`{{ env.mode }}`{% endraw %}. This is great for doing special private builds of the site for people that pay for content, for example. {% endif %}

If you refresh your local version, you won’t be able to see that content that we just added. This is working perfectly for us — especially because we want to protect it. So now, let’s show it, using environment variables. Open up .env and add the following to it:

MODE=private

Now, restart Eleventy and reload the site. You should now see something like this:

You can run this conditional rendering within the template too. For example, you could make all of the page content private and render a paywall instead. An example of that is if you go to my course without a license, you will be presented with a call to action to buy it:

Fun mode

This has hopefully been really useful content for you so far, so let’s expand on what we’ve learned and have some fun with it!

I want to finish by making a “fun mode” which completely alters the design to something more… fun. Open up src/_includes/base.njk, and just before the closing </head> tag, add the following:

{% if env.funMode %} <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" /> <style> body { font-family: 'Comic Sans MS', cursive; background: #fc427b; color: #391129; } h1, .fun { font-family: 'Lobster'; } .fun { font-size: 2rem; max-width: 40rem; margin: 0 auto 3rem auto; background: #feb7cd; border: 2px dotted #fea47f; padding: 2rem; text-align: center; } </style> {% endif %}

This snippet is looking to see if our funMode environment variable is true and if it is, it’s adding some “fun” CSS.

Still in base.njk, just before the opening <article> tag, add the following code:

{% if env.funMode %} <div class="fun"> <p>&#x1f389; <strong>Fun mode enabled!</strong> &#x1f389;</p> </div> {% endif %}

This code is using the same logic and rendering a fun banner if funMode is true. Let’s create our environment variable interface for that now. Open up src/_data/env.js and add the following to the exported object:

funMode: process.env.FUN_MODE

If funMode is not defined, it will act as false, because undefined is a falsy value.

Next, open up your .env file and add the following to it:

FUN_MODE=true

Now, restart the Eleventy task and reload your browser. It should look like this:

Pretty loud, huh?! Even though this design looks pretty awful (read: rad), I hope it demonstrates how much you can change with this environment setup.

Wrapping up

We’ve created three versions of the same site, running the same code to see all the differences:

  1. Standard site
  2. Private content visible
  3. Fun mode

All of these sites are powered by identical code with the only difference between each site being some environment variables which, for this example, I have defined in my Netlify dashboard.

I hope that this technique will open up all sorts of possibilities for you, using the best static site generator, Eleventy!

The post Give your Eleventy Site Superpowers with Environment Variables appeared first on CSS-Tricks.

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

Syndicate content
©2003 - Present Akamai Design & Development.