Developer News

Building a Settings Component

Css Tricks - Wed, 04/14/2021 - 11:08am

This is a tremendous CSS-focused tutorial from Adam Argyle. I really like the “just for gap” concept here. Grid is extremely powerful, but you don’t have to use all its abilities every time you reach for it. Here, Adam reaches for it for very light reasons like using it as an in-between border alternative as well as more generic spacing. I guess he’s putting money where his mouth is in terms of gap superseding margin!

I also really like calling out Una Kravet’s awesome name for flexible grids: RAM. Perhaps you’ve seen the flexible-number-of-columns trick with CSS grid? The bonus trick here (which I first saw from Evan Minto) is to use min(). That way, not only are large layouts covered, but even the very smallest layouts have no hard-coded minimum (like if 100% is smaller than 10ch here):

.el { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(10ch, 100%), 35ch)); }

There is a ton more trickery in the blog post. The “color pops” with :focus-within is fun and clever. So much practical CSS in building something so practical! 🧡 more blog posts like this, please. Fortunately, we don’t have to wait, as Adam has other component-focused posts like this one on Tabs and this one on Sidenav.

Direct Link to ArticlePermalink

The post Building a Settings Component appeared first on CSS-Tricks.

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

Why Netlify?

Css Tricks - Tue, 04/13/2021 - 12:48pm

I think it’s fair to think of Netlify as a CDN-backed static file host. But it would also be silly to think that’s all it is. That’s why I think it’s smart for them to have pages like this, comparing Netlify to GitHub Pages. GitHub Pages is a lot closer to only being a static file host. That’s still nice, but Netlify just brings so much more to the table.

Need to add a functional form to the site? Netlify does that.

Need to roll back to a previous version without any git-fu? Netlify does that.

Need to make sure you’re caching assets the best you can and breaking that cache for new versions? Netlify does that.

Need a preview of a pull request before you merge it? Netlify does that.

Need to set up redirects and rewrite rules so that your SPA behaves correctly? Netlify does that.

Need to run some server-side code? Netlify does that.

Need to do some A/B testing? Netlify does that.

That’s not all, just a random spattering of Netlify’s many features that take it to another level of hosting with a developer experience that’s beyond a static file host.

This same kind of thing came up on ShopTalk the other week. Why pick Netlify when you can toss files in a S3 bucket with Cloudfront in front of it? It’s a fair question, as maybe the outcome isn’t that different. But there are 100 other things to think about that, once you do, make Netlify seem like a no-brainer.

The post Why Netlify? appeared first on CSS-Tricks.

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

See You Around

Css Tricks - Mon, 04/12/2021 - 3:19am

Get it? Because this blog post is about Around, the wonderful new video call software. I’ve been using it for my video calls and I’d be happy to deliver you a TLDR right off the bat: It’s nice. It has all the important features of video call software you need while being very design-focused in a way that feels stand-out fresh. Thank god someone is getting this right.

Little floating circles

Make no mistake: chatting with people where you see their faces in a little floating circle is way nicer than a giant rectangle. It may not seem like a massive difference, but it really does feel different and better, particularly when you’re chatting with multiple people and/or people you chat with all the time.

You still get the face, that all-important human connector, but you aren’t seeing my disheveled bookshelf, my laundry hamper, or my dead plant in the corner. Even if you have a nice background or nobody cares about your unmade bed in the background (they probably don’t), there is a literal fatigue that sets in when you have a camera pointed at your whole area for any sustained period. It’s hard to describe, but it just feels like… a lot. That all changes for the better when all you are sharing is a cropped circle of your face. There is less pressure to be maintaining eye contact, for one thing. Here’s me and Geoff:

I have these little circles tucked into the upper right of my monitor.

There is real tech behind these circles. The virtual camera “zooms” to properly size your face in the circle so you’re always in focus. If you move off to any direction, it “pans” to keep you centered (as much as it can). Even the color filters it offers, while on the surface might just seem like a bit of fun, lower video fatigue. I gotta imagine the pressure to wear makeup is a bit lower when you’re green and pixelated:

The circles go everywhere in Around. They don’t have to be floating (although I like that mode the best). If you pop into Campfire mode, you’ll see everyone together in a more normal/dedicated window. If you pop into Notes or Image Sharing view, they come along in there too. Speaking of which…

Notes!

There is a collaborative Notes view in every call. The editor is very polished, and I was impressed. This isn’t some half-baked slapped-on chatroom-esque thing; it’s for actual formatted note-taking. The real-time-ness of it is spot on.

But here’s the actual best feature… when the meeting ends, everyone in the meeting gets emailed a copy of the notes. I’m going to give that three clap-hands emojis: 👏👏👏. Here’s Dave and I having a little call about ShopTalk, writing some silly notes, next to the emailed notes that arrive immediately after the end of the call:

Just the notes. No crap.

Notes can have a chat-like feel to them, but there is literally a chat feature as well. Like everything in Around, it’s got interesting UX to it. When you type and send a chat message, it appears next to your floating head. Just one chat message. It stays there until you remove it. It just makes a ton of sense. You don’t really need a traditional chatroom, you’re already talking to each other! More likely you need to share a link or something, and this UI is perfect for that.

Sharing

Aside from notes, there is Image Sharing, which is a very focused way to get everyone looking at one thing.

And then, of course, Screen Sharing. All the features you need for that are there. You can share your whole screen, or select just one window. Here’s me having a meeting with Dave where we’re looking at different polling options:

I’m sharing my browser window. I have the floaty heads I can move wherever. Dave sees my screen within the Around interface with the floaty heads in there.

You can give control of your computer to the person you are sharing with as well, which is awfully handy for pair programming sessions, which I’m doing constantly.

Audio quality

The first thing that Geoff said to me when we popped on to our first Around call was “Wow, you sound good.” I don’t hear that often at my desk because I work in an office with glass walls and it’s a bit echo-y and I haven’t gotten around to sound-dampening stuff in here yet. There is more real tech at work here with Around’s build-in noise reduction.

Around takes this echo cancellation stuff even further with their EchoTerminator feature (video). Even if you’re in the same room as other people on the same Around call, you don’t have to do that little dance where everyone mutes except one and hope that works. With Around, you don’t have to think about it, it just works (without the echo and feedback).

Fun

I spent a good while today watching Dave basically re-create the Dramatic Chipmunk GIF with his face, playing with how Around detects and zooms on faces. So that was a good time. But many of the features of Around have fun built right in.

You can just straight up turn off your video if you want, of course, or, you can be a cute bear GIF. It just stays that way until you turn it off. So you can use it as a reaction, a replacement for yourself, or just for a quick bit of fun. Emoji reactions pop up and cover your face for a second. Way more fun that it should be. Integrations

The most important integration for a video call app to me is Slack. That’s where my co-workers are, so kicking off a call happens many times a day right from there. As expected, you can do /around and off we go. Google Calendar is also a no-brainer.

On a serious note, it’s good to see Around on top of security. For example, if you share an image, it’s on their servers only until the call is over and then deleted. All text is encrypted. Meeting rooms aren’t going to get bombed as there are one-off ID’s and entrance controls.

Ready to give it a try?

Around is free for anyone to download, with pricing coming later this year.

Sign up for free

The post See You Around appeared first on CSS-Tricks.

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

CSS Is, In Fact, Awesome

Css Tricks - Wed, 04/07/2021 - 10:41am

You’ve seen the iconic image. Perhaps some of what makes that image so iconic is that people see what they want to see in it. If you see it as a critique of CSS being silly, weird, or confusing, you can see that in the image. If you see it as CSS being powerful and flexible, you’ve got that too. That’s what Jim Neilsen is saying here, reacting to a presentation by Hidde de Vries:

This is the power of CSS. It gives you options. Use them or don’t.

Want it to overflow visibly? It can. Want it to lop off overflowing content? It can. Want it to stretch? It can. Want it to ellipse? It can. Want it to wrap or not wrap? It can. Want to scale the type to fit? It can. If you love CSS, this is probably exactly why.

Mandy Michael has a great thread on this from a few years back:

Or you know, how about we just make the box bigger so it fits the text, we could just get rid of the explicit width and height and everything will just work.

— Mandy Michael (@Mandy_Kerr) April 15, 2018

Brandon Smith wrote about all this a few years back as well. I remain chuffed that Eric Meyer asked the original creator of the image, Steve Frank of Panic, about it and Steve once stopped by to explain the real origin:

It was 2009 and I’d spent what seemed like hours trying to do something in CSS that I already knew I could do in seconds with tables. I was trying really hard to do it with CSS because that’s what you’re supposed to do, but I just wasn’t very good at it (spoiler alert: I’m still not very good at it).

I do have a slightly better grasp on the concept of overflow now, but at the time it just blew my mind that someone thought the default behavior should be to just have the text honk right out of the box, instead of just making the box bigger like my nice, sensible tables had always done.

Anyway, I just had this moment of pure frustration and, instead of solving the problem properly, I spent 5 minutes creating a snarky mug and went back to using tables. Because that’s my signature move in times of crisis.

So, the original is indeed born out of frustration, but has nonetheless inspired many love letters to CSS. It has also certainly earned its place in CSS infamy, right alongside Peter Griffin struggling with window blinds, as one of the most iconic CSS images ever.

Direct Link to ArticlePermalink

The post CSS Is, In Fact, Awesome appeared first on CSS-Tricks.

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

SvelteKit is in public beta

Css Tricks - Wed, 04/07/2021 - 5:02am

Rich Harris:

Think of it as Next for Svelte. It’s a framework for building apps with Svelte, complete with server-side rendering, routing, code-splitting for JS and CSS, adapters for different serverless platforms and so on.

Great move. I find Next.js a real pleasure to work with. I’ve hit some rough edges trying to get it to do what are probably non-standard things, but even then, I was able to get past them and have had a pretty great developer experience, while producing something that I’d like to think is going to be a pretty great user experience, too.

I always want server-side rendering. I want a blessed routing solution. I want pre-made smart solutions for common tasks and elegant solutions for hard problems. Packaging something like that up for Svelte in a core project seems very smart, just as it’s smart for Vue to have Nuxt.js. Maybe even smarter, they resisted naming it Svxt.js which was surely the right call.

Direct Link to ArticlePermalink

The post SvelteKit is in public beta appeared first on CSS-Tricks.

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

Coordinating Svelte Animations With XState

Css Tricks - Wed, 04/07/2021 - 3:09am

This post is an introduction to XState as it might be used in a Svelte project. XState is unique in the JavaScript ecosystem. It won’t keep your DOM synced with your application state, but it will help manage your application’s state by allowing you to model it as a finite state machine (FSM).

A deep dive into state machines and formal languages is beyond the scope of this post, but Jon Bellah does that in another CSS-Tricks article. For now, think of an FSM as a flow chart. Flow charts have a number of states, represented as bubbles, and arrows leading from one state to the next, signifying a transition from one state to the next. State machines can have more than one arrow leading out of a state, or none at all if it’s a final state, and they can even have arrows leaving a state, and pointing right back into that same state.

If that all sounds overwhelming, relax, we’ll get into all the details, nice and slow. For now, the high level view is that, when we model our application as a state machine, we’ll be creating different “states” our application can be in (get it … state machine … states?), and the events that happen and cause changes to state will be the arrows between those states. XState calls the states “states,” and the arrows between the states “actions.”

Our example

XState has a learning curve, which makes it challenging to teach. With too contrived a use case it’ll appear needlessly complex. It’s only when an application’s code gets a bit tangled that XState shines. This makes writing about it tricky. With that said, the example we’ll look at is an autocomplete widget (sometimes called autosuggest), or an input box that, when clicked, reveals a list of items to choose from, which filter as you type in the input.

For this post we’ll look at getting the animation code cleaned up. Here’s the starting point:

This is actual code from my svelte-helpers library, though with unnecessary pieces removed for this post. You can click the input and filter the items, but you won’t be able to select anything, “arrow down” through the items, hover, etc. I’ve removed all the code that’s irrelevant to this post.

We’ll be looking at the animation of the list of items. When you click the input, and the results list first renders, we want to animate it down. As you type and filter, changes to the list’s dimensions will animate larger and smaller. And when the input loses focus, or you click ESC, we animate the list’s height to zero, while fading it out, and then remove it from the DOM (and not before). To make things more interesting (and nice for the user), let’s use a different spring configuration for the opening than what we use for the closing, so the list closes a bit more quickly, or stiffly, so unneeded UX doesn’t linger on the screen too long.

If you’re wondering why I’m not using Svelte transitions to manage the animations in and out of the DOM, it’s because I’m also animating the list’s dimensions when it’s open, as the user filters, and coordinating between transition, and regular spring animations is a lot harder than simply waiting for a spring update to finish getting to zero before removing an element from the DOM. For example, what happens if the user quickly types and filters the list, as it’s animating in? As we’ll see, XState makes tricky state transitions like this easy.

Scoping the Problem

Let’s take a look at the code from the example so far. We’ve got an open variable to control when the list is open, and a resultsListVisible property to control whether it should be in the DOM. We also have a closing variable that controls whether the list is in the process of closing.

On line 28, there’s an inputEngaged method that runs when the input is clicked or focused. For now let’s just note that it sets open and resultsListVisible to true. inputChanged is called when the user types in the input, and sets open to true. This is for when the input is focused, the user clicks escape to close it, but then starts typing, so it can re-open. And, of course, the inputBlurred function runs when you’d expect, and sets closing to true, and open to false.

Let’s pick apart this tangled mess and see how the animations work. Note the slideInSpring and opacitySpring at the top. The former slides the list up and down, and adjusts the size as the user types. The latter fades the list out when hidden. We’ll focus mostly on the slideInSpring.

Take a look at the monstrosity of a function called setSpringDimensions. This updates our slide spring. Focusing on the important pieces, we take a few boolean properties. If the list is opening, we set the opening spring config, we immediately set the list’s width (I want the list to only slide down, not down and out), via the { hard: true } config, and then set the height. If we’re closing, we animate to zero, and, when the animation is complete, we set resultsListVisible to false (if the closing animation is interrupted, Svelte will be smart enough to not resolve the promise so the callback will never run). Lastly, this method is also called any time the size of the results list changes, i.e., as the user filters. We set up a ResizeObserver elsewhere to manage this.

Spaghetti galore

Let’s take stock of this code.

  • We have our open variable which tracks if the list is open.
  • We have the resultsListVisible variable which tracks if the list should be in the DOM (and set to false after the close animation is complete).
  • We have the closing variable that tracks if the list is in the process of closing, which we check for in the input focus/click handler so we can reverse the closing animation if the user quickly re-engages the widget before it’s done closing.
  • We also have setSpringDimensions that we call in four different places. It sets our springs depending on whether the list is opening, closing, or just resizing while open (i.e. if the user filters the list).
  • Lastly, we have a resultsListRendered Svelte action that runs when the results list DOM element renders. It starts up our ResizeObserver, and when the DOM node unmounts, sets closing to false.

Did you catch the bug? When the ESC button is pressed, I’m only setting open to false. I forgot to set closing to true, and call setSpringDimensions(false, true). This bug was not purposefully contrived for this blog post! That’s an actual mistake I made when I was overhauling this widget’s animations. I could just copy paste the code in inputBlured over to where the escape button is caught, or even move it to a new function and call it from both places. This bug isn’t fundamentally hard to solve, but it does increase the cognitive load of the code.

There’s a lot of things we’re keeping track of, but worst of all, this state is scattered all throughout the module. Take any piece of state described above, and use CodeSandbox’s Find feature to view all the places where that piece of state is used. You’ll see your cursor bouncing across the file. Now imagine you’re new to this code, trying to make sense of it. Think about the growing mental model of all these state pieces that you’ll have to keep track of, figuring out how it works based on all the places it exists. We’ve all been there; it sucks. XState offers a better way; let’s see how.

Introducing XState

Let’s step back a bit. Wouldn’t it be simpler to model our widget in terms of what state it’s in, with events happening as the user interacts, which cause side effects, and transitions to new states? Of course, but that’s what we were already doing; the problem is, the code is scattered everywhere. XState gives us the ability to properly model our state in this way.

Setting expectations

Don’t expect XState to magically make all of our complexity vanish. We still need to coordinate our springs, adjust the spring’s config based on opening and closing states, handle resizes, etc. What XState gives us is the ability to centralize this state management code in a way that’s easy to reason about, and adjust. In fact, our overall line count will increase a bit, as a result of our state machine setup. Let’s take a look.

Your first state machine

Let’s jump right in, and see what a bare bones state machine looks like. I’m using XState’s FSM package, which is a minimal, pared down version of XState, with a tiny 1KB bundle size, perfect for libraries (like an autosuggest widget). It doesn’t have a lot of advanced features like the full XState package, but we wouldn’t need them for our use case, and we wouldn’t want them for an introductory post like this.

The code for our state machine is below, and the interactive demo is over at Code Sandbox. There’s a lot, but we’ll go over it shortly. And to be clear, it doesn’t work yet.

const stateMachine = createMachine( { initial: "initial", context: { open: false, node: null }, states: { initial: { on: { OPEN: "open" } }, open: { on: { RENDERED: { actions: "rendered" }, RESIZE: { actions: "resize" }, CLOSE: "closing" }, entry: "opened" }, closing: { on: { OPEN: { target: "open", actions: ["resize"] }, CLOSED: "closed" }, entry: "close" }, closed: { on: { OPEN: "open" }, entry: "closed" } } }, { actions: { opened: assign(context => { return { ...context, open: true }; }), rendered: assign((context, evt) => { const { node } = evt; return { ...context, node }; }), close() {}, resize(context) {}, closed: assign(() => { return { open: false, node: null }; }) } } );

Let’s go from top to bottom. The initial property controls what the initial state is, which I’ve called “initial.” context is the data associated with our state machine. I’m storing a boolean for whether the results list is currently open, as well as a node object for that same results list. Next we see our states. Each state is a key in the states property. For most states, you can see we have an on property, and an entry property.

on configures events. For each event, we can transition to a new state; we can run side effects, called actions; or both. For example, when the OPEN event happens inside of the initial state, we move into the open state. When the RENDERED event happens in the open state, we run the rendered action. And when the OPEN event happens inside the closing state, we transition into the open state, and also run the resize action. The entry field you see on most states configures an action to run automatically whenever a state is entered. There are also exit actions, although we don’t need them here.

We still have a few more things to cover. Let’s look at how our state machine’s data, or context, can change. When we want an action to modify context, we wrap it in assign and return the new context from our action; if we don’t need any processing, we can just pass the new state directly to assign. If our action does not update context, i.e., it’s just for side effects, then we don’t wrap our action function in assign, and just perform whatever side effects we need.

Affecting change in our state machine

We have a cool model for our state machine, but how do we run it? We use the interpret function.

const stateMachineService = interpret(stateMachine).start();

Now stateMachineService is our running state machine, on which we can invoke events to force our transitions and actions. To fire an event, we call send, passing the event name, and then, optionally, the event object. For example, in our Svelte action that runs when the results list first mounts in the DOM, we have this:

stateMachineService.send({ type: "RENDERED", node });

That’s how the rendered action gets the node for the results list. If you look around the rest of the AutoComplete.svelte file, you’ll see all the ad hoc state management code replaced with single line event dispatches. In the event handler for our input click/focus, we run the OPEN event. Our ResizeObserver fires the RESIZE event. And so on.

Let’s pause for a moment and appreciate the things XState gives us for free here. Let’s look at the handler that runs when our input is clicked or focused before we added XState.

function inputEngaged(evt) { if (closing) { setSpringDimensions(); } open = true; resultsListVisible = true; }

Before, we were checking to see if we were closing, and if so, forcing a re-calculation of our sliding spring. Otherwise we opened our widget. But what happened if we clicked on the input when it was already open? The same code re-ran. Fortunately that didn’t really matter. Svelte doesn’t care if we re-set open and resultsListVisible to the values they already held. But those concerns disappear with XState. The new version looks like this:

function inputEngaged(evt) { stateMachineService.send("OPEN"); }

If our state machine is already in the open state, and we fire the OPEN event, then nothing happens, since there’s no OPEN event configured for that state. And that special handling for when the input is clicked when the results are closing? That’s also handled right in the state machine config — notice how the OPEN event tacks on the resize action when it’s run from the closing state.

And, of course, we’ve fixed the ESC key bug from before. Now, pressing the key simply fires the CLOSE event, and that’s that.

Finishing up

The ending is almost anti-climactic. We need to take all of the work we were doing before, and simply move it to the right place among our actions. XState does not remove the need for us to write code; it only provides a structured, clear place to put it.

{ actions: { opened: assign({ open: true }), rendered: assign((context, evt) => { const { node } = evt; const dimensions = getResultsListDimensions(node); itemsHeightObserver.observe(node); opacitySpring.set(1, { hard: true }); Object.assign(slideInSpring, SLIDE_OPEN); slideInSpring.update(prev => ({ ...prev, width: dimensions.width }), { hard: true }); slideInSpring.set(dimensions, { hard: false }); return { ...context, node }; }), close() { opacitySpring.set(0); Object.assign(slideInSpring, SLIDE_CLOSE); slideInSpring .update(prev => ({ ...prev, height: 0 })) .then(() => { stateMachineService.send("CLOSED"); }); }, resize(context) { opacitySpring.set(1); slideInSpring.set(getResultsListDimensions(context.node)); }, closed: assign(() => { itemsHeightObserver.unobserve(resultsList); return { open: false, node: null }; }) } } Odds and ends

Our animation state is in our state machine, but how do we get it out? We need the open state to control our results list rendering, and, while not used in this demo, the real version of this autosuggest widget needs the results list DOM node for things like scrolling the currently highlighted item into view.

It turns out our stateMachineService has a subscribe method that fires whenever there’s a state change. The callback you pass is invoked with the current state machine state, which includes a context object. But Svelte has a special trick up its sleeve: its reactive syntax of $: doesn’t only work with component variables and Svelte stores; it also works with any object with a subscribe method. That means we can sync with our state machine with something as simple as this:

$: ({ open, node: resultsList } = $stateMachineService.context);

Just a regular destructuring, with some parens to help things get parsed correctly.

One quick note here, as an area for improvement. Right now, we have some actions which both both perform a side effect, and also update state. Ideally, we should probably split these up into two actions, one just for the side effect, and the other using assign for the new state. But I decided to keep things as simple as possible for this article to help ease the introduction of XState, even if a few things wound up not being quite ideal.

Here’s the demo Parting thoughts

I hope this post has sparked some interest in XState. I’ve found it to be an incredibly useful, easy to use tool for managing complex state. Please know that we’ve only scratched the surface. We focused on the minimal fsm package, but the entire XState library is capable of a lot more than what we covered here, from nested states, to first-class support for Promises, and it even has a state visualization tool! I urge you to check it out.

Happy coding!

The post Coordinating Svelte Animations With XState appeared first on CSS-Tricks.

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

axe DevTools Pro

Css Tricks - Wed, 04/07/2021 - 3:08am

I’m going to try to show you some things I think are useful and important about axe™ DevTools and use as few words as possible.

axe DevTools includes a browser extension which you need no special expertise to use.

You install it from the extension directories like any other extension.

It’s a tab along with your other DevTools.

It might be all the way on the right. I like to click and drag it over by the Elements tab.

Now I can scan my page and find 57% of accessibility issues along with help in fixing them.

Here’s an <iframe> on CodePen that was missing a title attribute.

The information in the extension itself is very helpful in fixing the problem, but I can also click over to Deque University to get very clear, detailed information on the problem, who it affects, and how to fix it. For the problem I found above:

Screen reader users have the option to pull up a list of titles for all frames on a page. Adding descriptive, unique titles allows users to quickly find the frame they need. If no titles are present, navigating through frames can quickly become difficult and confusing.

axe DevTools Pro unlocks Intelligent Guided™ Tests, meaning we can fix 83% of all accessibility issues.

There are many accessibility issues that a static scan of the code can’t catch. For example, does your site have a modal? If so, testing it requires some step-by-step testing. What buttons open it (because focus will need to be returned there)? Can the modal be found once open? Does it trap focus? Is it closable? These are important issues that are difficult to remember on your own and impossible to statically test for. But you’re in luck, the Intelligent Guided Tests (which you get by upgrading to axe DevTools Pro) make problems easy to suss out, because it walks you through each step.

“Get yourself axe clean before pushing.” is a company culture thing I can get behind.

You don’t commit syntax errors in code. You don’t commit poorly formatted code. Don’t commit accessibility bugs either. Open axe DevTools and get yourself axe clean before pushing up new commits.

Friends don’t let friends ship inaccessible code.

Get Your 14-day Free Trial

The post axe DevTools Pro appeared first on CSS-Tricks.

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

Space Jam

Css Tricks - Tue, 04/06/2021 - 10:57am

It’s certainly worth noting that the Space Jam website, which made its way into umpteen conference talks for being fabulous evidence of the web’s strength in backward compatibility, has been replaced. We could have saw that coming. Everything is remake. The original was released in 1996, making the site, which they kept online, 25 years old.

Of course, you knew folks would pull out their measuring sticks. Here’s Max Böck:

Unsurprisingly, the new site is a lot heavier than the original: with 4.673KB vs. 120KB, the new site is about 39 times the size of the old one. That’s because the new site has a trailer video, high-res images and a lot more Javascript.

That’s funny, the 25 year old site is more than 25 times smaller.

They are both websites that exist and promote a movie, so I feel like it’s fair to call that an apples-to-apples comparison. But Max levels the playing field to the time period by comparing the old site on a 1996 56kb modem and the new site on a 3G mobile network connection, which is 30× faster. When you do that, the sites are nearly neck-and-neck, with the new one being 1.3 seconds faster.

You could say that whatever we’re given, we use, sort of like how building better protective gear for athletes only makes the athletes hit harder.

Direct Link to ArticlePermalink

The post Space Jam appeared first on CSS-Tricks.

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

Gaps? Gasp!

Css Tricks - Tue, 04/06/2021 - 3:35am

At first, there were flexboxes (the children of a display: flex container). If you wanted them to be visually separate, you had to use content justification (i.e. justify-content: space-between), margin trickery, or sometimes, both. Then along came grids (a display: grid container), and grids could have not-margin not-trickeried minimum gaps between grid cells, thanks to grid-gap. Flexboxes did not have gaps.

Now they can, thanks to the growing support of gap, the grid-gap successor that isn’t confined to grids. With gap, you can gap your grids, your flexboxes, and even your multiple columns. It’s gaptastic!

Gap with Grid

Let’s start where gap is the most robust: CSS Grid. Here’s a basic grid setup in HTML and CSS:

<section> <div>div</div> <div>div</div> <div>div</div> <div>div</div> <div>div</div> <div>div</div> <div>div</div> </section> section { display: grid; grid-template-rows: repeat(2,auto); grid-template-columns: repeat(4,auto); gap: 1em; } section div { width: 2em; } CodePen Embed Fallback

That places the grid cells at least 1em apart from each other. The separation distance can be greater than that, depending on other conditions beyond the scope of this post, but at a minimum they should be separated by 1em. (OK, let’s do one example: gap’s gaps are in addition to any margins on the grid cells, so if all the grid items have margin: 2px;, then the visual distance between grid cells would be at least 1em plus 4px.) By default, changes to the gap size causes resizing of the grid items, so that they fill their cells.

This all works because gap is actually shorthand for the properties row-gap and column-gap. The gap: 1em is interpreted as gap: 1em 1em, which is shorthand for row-gap: 1em; column-gap: 1em;. If you want different row and column gap distances, then something like gap: 0.5em 1em will do nicely.

Gap with Flexbox

Doing the same thing in a flexbox context gives you gaps, but not in quite the same way they happen in grids. Assume the same HTML as above, but this CSS instead:

section { display: flex; flex-wrap: wrap; gap: 1em; } CodePen Embed Fallback

The flexboxes are pushed apart by at least the value of gap here, and (thanks to flex-wrap) wrap to new flex lines when they run out of space inside their flex container. Changing the gap distance could lead to a change in the wrapping of the flex items, but unlike in Grid, changing gaps between flex items won’t change the sizes of the flex items. Gap changes can cause the flex wrapping to happen at different places, meaning the number of flex items per row will change, but the widths will stay the same (unless you’ve set them to grow or shrink via flex, that is).

Gap with Multi-Column

In the case of multicolumn content, there is bit of a restriction on gap: only column gaps are used. You can declare row gaps for multicolumn if you want, but they’ll be ignored.

section { columns: 2; gap: 1em; } CodePen Embed Fallback Support

Support for gap, row-gap, and column-gap is surprisingly widespread. Mozilla’s had them since version 61, Chromium since version 66, and thanks to work by Igalia’s Sergio Villar, they’re coming to Safari and Mobile Safari soon (they’re already in the technology preview builds). So if your grid, flex, or multicolumn content needs a bit more space to breathe, get ready to fall into the gap!

The post Gaps? Gasp! appeared first on CSS-Tricks.

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

Jetpack Turns 10!

Css Tricks - Tue, 04/06/2021 - 3:27am

(This is a sponsored post.)

Ten years! That’s a huge milestone for a project, especially one that had a pretty simple goal in mind from the start: give self-hosted WordPress sites many of the same features and functionality enjoyed by hosted WordPress.com sites.

It’s a great story. The Automattic team responsible for driving social activity in WordPress sees Jetpack as a way to expand and unify social activity across all WordPress sites, and winds up paving the way for a product that today helps more than 5 million sites with everything from security and performance to backups and integrations.

And what has Jetpack accomplished in those 10 years and 5 million sites? The numbers are staggering:

  • 122 billion blocked malicious login attempts.
    9,330,623 of those on CSS-Tricks, as we write.
  • 269 million site backups.
    Last backup of CSS-Tricks: 3 minutes ago, as we write.
  • 24 trillion images served by Jetpack CDN.
    Incredibly, a free feature of Jetpack.
  • 61.6 billion site searches.
    Try site search on this site, the latest release has really nice UI & UX improvements, like seeing the post thumbnail.
  • 50 billion related posts displayed. Related posts plugins are notoriously heavy on the server, but not when you let Jetpack do it!
  • 1.6 trillion tracked page views. Those are the numbers at work here with WordPress being some 40% of the web.
  • 2.6 billion shared social posts.
    The @css Twitter account runs itself thanks to Jetpack.

And you know CSS-Tricks is represented in those figures. Jetpack powers our search. We use it for real-time backups and downtime monitoring. It’s what helps us push content to Twitter and display related posts. We use its CDN. We even use a bunch of blocks it includes when we’re writing posts like this. We love Jetpack.

The Jetpack team launched a site celebrating 10 years. And, hey, for a super limited time, you can get 40% off the entire first year of any Jetpack plan.

Direct Link to ArticlePermalink

The post Jetpack Turns 10! appeared first on CSS-Tricks.

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

Definition Tag

Css Tricks - Mon, 04/05/2021 - 5:54am

It’s <dfn>. Jen Kramer and Erika Lee are doing a #30DaysofHTML email list thing-y on Substack, which is an easy subscribe. It’s only been a few days and all of them have little gems, even for someone like me who likes to think he has a pretty decent grasp on HTML. Day 4 is <dfn>.

I’m sure I could have told you that <dfn> was “definition” but I certainly don’t reach for it as often as I probably should and couldn’t give you a straight answer on the perfect time to use it.

I think Erika nails that here:

<p>He is not as enamored with <dfn id="kong">King Kong</dfn> who resembles an enormous gorilla-like ape that has appeared in various media since 1933. </p> ... <p>Complaints about <a href="#kong">Kong</a> include how he has no atomic fire breath.</p>

I really like the idea of defining a term naturally and contextually within a block of text, and pointing back up to it with an anchor link later if needed.

Direct Link to ArticlePermalink

The post Definition Tag appeared first on CSS-Tricks.

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

Creating a Smart Navbar With Vanilla JavaScript

Css Tricks - Mon, 04/05/2021 - 2:55am

Sticky, or fixed, navigation is a popular design choice because it gives users persistent access to navigate the site. On the other hand, it takes up space on the page and sometimes covers content is a way that’s less than appealing.

A possible solution? Smart navigation.

Let’s define “smart navigation” as:

  1. Visible at the top of the page
  2. Visible when the user moves up the page (wherever they may have scrolled to)
  3. Hidden when the user moves down the page

Here’s an example of how that might work:

CodePen Embed Fallback

It‘s all the convenience of sticky positioning, with an added fullscreen benefit. This sort of smart navigation is already commonly (think of the URL bar in many mobile browsers), but is sometimes a hassle to implement without a library or plugin. So, in this article, we’ll discuss how to build one using CSS and vanilla JavaScript.

Side note: People have different definitions of what scrolling down a page means (imagine how some trackpad preferences scroll the page up when you move your fingers down). For the purposes of this article, scrolling down refers to moving towards the bottom of the page.

Let’s look at the code

Here’s some example HTML. Our smart navigation will be the <nav> which sits above the <main>:

<nav> <div class="logo"> Logo </div> <div class="links"> <a href="#">Link 1</a> <a href="#">Link 2</a> <a href="#">Link 3</a> <a href="#">Link 4</a> </div> </nav> <main> <!--Place the content of your page here--> </main>

It’s important to note that elements are only sticky relative to their parent container. The parent container of <nav> should be the body tag; it shouldn’t be placed within another tag on the page.

The CSS for our smart navigation looks like this:

nav { position: sticky; top: 0; display: flex; flex-wrap: wrap; justify-content: space-between; padding: 1.5rem 2rem; background-color: #eaeaea; }

Now we need to detect when our user is scrolling the page and the direction of their scrolling. A user is scrolling down if the value of their last scroll position is less than the value of their current scroll position. Breaking the logic down, we’ll need to:

  1. Define a variable to store the previous scroll position
  2. Assign a variable to detect the current scroll position set to the scroll offset of the page

If the current scroll position is greater than the previous scroll position, then the user is scrolling downwards. Let’s call our function isScrollingDown:

let previousScrollPosition = 0; const isScrollingDown = () => { let currentScrolledPosition = window.scrollY || window.pageYOffset; let scrollingDown; if (currentScrolledPosition > previousScrollPosition) { scrollingDown = true; } else { scrollingDown = false; } previousScrollPosition = currentScrolledPosition; return scrollingDown; };

Here’s a visual representation of how this function works:

CodePen Embed Fallback

With this logic, we’re able to detect when the page is scrolling down so we can use this to toggle our nav styling:

const nav = document.querySelector('nav'); const handleNavScroll = () => { if (isScrollingDown()) { nav.classList.add('scroll-down'); nav.classList.remove('scroll-up') } else { nav.classList.add('scroll-up'); nav.classList.remove('scroll-down') } }

If the user is scrolling down, we’ll assign a .scroll-down class that contains our styling method for when the page is moving downward. We can update our <nav> CSS to this:

nav { /* default styling */ transition: top 500ms ease-in-out; } nav.scroll-up { top: 0; } nav.scroll-down { top: -100%; }

With this styling, the top property value of <nav> is set to -100% of the page height so it slides out of view. We could also choose to handle our styling with translate or by fading it out — whatever animation works best.

Performance

Whenever we’re working with scroll event listeners, performance is something that should immediately come to mind. Right now, we’re calling our function every time the user scrolls the page, but we don’t need to detect each pixel movement.

For this case, we can implement a throttle function instead. A throttle function is a higher order function that acts as a timer for the function passed into it. If we throttle a scroll event with a timer of 250ms, the event will only be called every 250ms while the user scrolls. It’s a great way to limit the number of times we call the function, helping with the performance of the page.

David Corbacho goes deeper into throttle implementations in this article.

A simple throttle implementation in JavaScript looks like this:

// initialize a throttleWait variable var throttleWait; const throttle = (callback, time) => { // if the variable is true, don't run the function if (throttleWait) return; // set the wait variable to true to pause the function throttleWait = true; // use setTimeout to run the function within the specified time setTimeout(() => { callback(); // set throttleWait to false once the timer is up to restart the throttle function throttleWait = false; }, time); }

Then we can include our handleNavScroll function inside a throttle:

window.addEventListener("scroll", () => { throttle(handleNavScroll, 250) });

With this implementation, the handleNavScroll function is only called once every 250ms.

Accessibility

Whenever implementing a custom feature in JavaScript, we must always take accessibility into concern. One such issue is ensuring that <nav> is visible when it’s in focus. Browsers tend to scroll to the part of the page that currently has focus by default, but there can be certain complications when working with scroll events.

A way to ensure that <nav> is always visible is to update the CSS to account for focus. Now our CSS looks like this:

nav.scroll-up, nav:focus-within { top: 0; }

Unfortunately, the focus-within selector isn’t fully supported across all browsers. We can include a JavaScript fallback for it:

const handleNavScroll = () => { if (isScrollingDown() && !nav.contains(document.activeElement))) { nav.classList.add('scroll-down'); nav.classList.remove('scroll-up') } else { nav.classList.add('scroll-up'); nav.classList.remove('scroll-down') } }

In this updated function, we only apply the scroll-down class if the user is scrolling down the page and the <nav> doesn’t currently have any element with focus in it.

Another aspect of accessibility is the consideration that some users may not want to have any animation on the page. That’s something we can detect and respect with the prefers-reduced-motion CSS media query. We can update this method in JavaScript and prevent our function from running at all if a user prefers reduced motion:

const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); window.addEventListener("scroll", () => { if (mediaQuery && !mediaQuery.matches) { throttle(handleNavScroll, 250) } }); Wrapping up

So, there we have it: a smart navigation implementation with plain CSS and vanilla JavaScript. Now users have persistent access to navigate the site without losing real estate in a way that blocks content.

Plus, the benefit of a custom implementation like this is that we get a delightful user experience that isn’t over-engineered or sacrifices open performance or accessibility.

CodePen Embed Fallback

The post Creating a Smart Navbar With Vanilla JavaScript appeared first on CSS-Tricks.

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

Platform News: Rounded Outlines, GPU-Accelerated SVG Animations, How CSS Variables Are Resolved

Css Tricks - Fri, 04/02/2021 - 9:35am

In the news this week, Firefox gets rounded outlines, SVG animations are now GPU-accelerated in Chrome, there are no physical units in CSS, The New York Times crossword is accessible, and CSS variables are resolved before the value is inherited.

Let’s jump in the news!

Rounded outlines are coming to Firefox

The idea to have the outline follow the border curve has existed ever since it became possible to create rounded borders via the border-radius property in the mid 2000s. It was suggested to Mozilla, WebKit, and Chromium over ten years ago, and it’s even been part of the CSS UI specification since 2015:

The parts of the outline are not required to be rectangular. To the extent that the outline follows the border edge, it should follow the border-radius curve.

Fast-forward to today in 2021 and outlines are still rectangles in every browser without exception:

CodePen Embed Fallback

But this is finally starting to change. In a few weeks, Firefox will become the first browser with rounded outlines that automatically follow the border shape. This will also apply to Firefox’s default focus outline on buttons.

Please star Chromium Issue #81556 (sign in required) to help prioritize this bug and bring rounded outlines to Chrome sooner rather than later.

SVG animations are now GPU-accelerated in Chrome

Until recently, animating an SVG element via CSS would trigger repaint on every frame (usually 60 times per second) in Chromium-based browsers. Such constant repainting can have a negative impact on the smoothness of the animation and the performance of the page itself.

The latest version of Chrome has eliminated this performance issue by enabling hardware acceleration for SVG animations. This means that SVG animations are offloaded to the GPU and no longer run on the main thread.

In this example, the SVG circle is continuously faded in and out via a CSS animation (see code)

The switch to GPU acceleration automatically made SVG animations more performant in Chromium-based browsers (Firefox does this too), which is definitely good news for the web:

Hooray for more screen reader-accessible, progressively enhanced SVG animations and less Canvas.

There cannot be real physical units in CSS

CSS defines six physical units, including in (inches) and cm (centimeters). Every physical unit is in a fixed ratio with the pixel unit, which is the canonical unit. For example, 1in is always exactly 96px. On most modern screens, this length does not correspond to 1 real-world inch.

The FAQ page of the CSS Working Group now answers the question why there can’t be real physical units in CSS. In short, the browser cannot always determine the exact size and resolution of the display (think projectors). For websites that need accurate real-world units, the Working Group recommends per-device calibration:

Have a calibration page, where you ask the user to measure the distance between two lines that are some CSS distance apart (say, 10cm), and input the value they get. Use this to find the scaling factor necessary for that screen (CSS length divided by user-provided length).

This scaling factor can then be set to a custom property and used to compute accurate lengths in CSS:

html { --unit-scale: 1.428; } .box { /* 5 real-world centimeters */ width: calc(5cm * var(--unit-scale, 1)); } The Times crossword is accessible to screen reader users

The NYT Open team wrote about some of the improvements to the New York Times website that have made it more accessible in recent years. The website uses semantic HTML (<article>, <nav>, etc.), increased contrast on important components (e.g., login and registration), and skip-to-content links that adapt to the site’s paywall.

Furthermore, the Games team made the daily crossword puzzle accessible to keyboard and screen reader users. The crossword is implemented as a grid of SVG <rect> elements. As the user navigates through the puzzle, the current square’s aria-label attribute (accessible name) is dynamically updated to provide additional context.

The screen reader announces the clue, the number of letters in the solution, and the position of the selected square

You can play the mini crossword without an account. Try solving the puzzle with the keyboard.

CSS variables are resolved before the value is inherited

Yuan Chuan recently shared a little CSS quiz that I didn’t answer correctly because I wasn’t sure if a CSS variable (the var() function) is resolved before or after the value is inherited. I’ll try to explain how this works on the following example:

html { --text-color: var(--main-color, black); } footer { --main-color: brown; } p { color: var(--text-color); }

The question: Is the color of the paragraph in the footer black or brown? There are two possibilities. Either (A) the declared values of both custom properties are inherited to the paragraph, and then the color property resolves to brown, or (B) the --text-color property resolves to black directly on the <html> element, and then this value is inherited to the paragraph and assigned to the color property.

The correct answer is option B (the color is black). CSS variables are resolved before the value is inherited. In this case, --text-color falls back to black because --main-color does not exist on the <html> element. This rule is specified in the CSS Variables module:

It is important to note that custom properties resolve any var() functions in their values at computed-value time, which occurs before the value is inherited.

The post Platform News: Rounded Outlines, GPU-Accelerated SVG Animations, How CSS Variables Are Resolved appeared first on CSS-Tricks.

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

Honeypot DEVS ANSWER

Css Tricks - Fri, 04/02/2021 - 9:34am

I did this thing for Honeypots YouTube Channel. I had heard of Honeypot through these mini documentaries they have done, like about Vue.js, GraphQL, and Ember.js. They do a great job, so I was happy to shoot them over some answers to questions for this series.

Here’s a TLDR-ish transcript:

What’s the best way to stay up to date with new technologies?

Don’t worry about it much. If you’re actively building websites and solving problems that come up day to day, you’re doing the job and that’s what’s important. If you’re really worried you’re only ever using outdated tech, go to a conference once a year and see what people are talking about.

What are your top tips for career advancement?

Writing! You don’t have to blog, even though I like that idea. You can write to your co-workers, or even keep a private log. Writing really opens doors and makes you a better thinker.

What strengths are most important for a developer?

Communication trumps raw technical skill. One developer alone can never do as much as a high functioning team, and communication is what makes a team high functioning.

What what the proudest moment of your career?

I tend to look back at things that took a long time and didn’t involve just me. CodePen will be 10 years old next year. That’s something to be proud of.

When did you realize you wanted to be a developer?

I had a good teacher in high school. I’m sure they don’t even remember me, but I was highly into the computer programming class they taught. I’d come to school early to use the computer lab. I’d stay after school. I’d take my study halls there. Working on a program, with the guidance of that teacher, was deeply fun. While I ended up abandoning computer science in college for art, my career now I find was a way to marry my love of computer nerdery and art.

The post Honeypot DEVS ANSWER appeared first on CSS-Tricks.

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

The Deno Company

Css Tricks - Fri, 04/02/2021 - 9:10am

I’m sure a lot of you are paying attention to Deno anyway, the next-gen JavaScript-on-the-sever project from Node creator Ryan Dahl, especially after dropping all these candid regrets about what happened in Node. But perhaps you’re paying more attention now that Deno has taken some seed investment and will be turning itself into a company, while staying open-source:

For Deno to grow and be maximally useful, it must remain permissively free. We don’t believe the “open core” business model is right for a programming platform like Deno. We do not want to find ourselves in the unfortunate position where we have to decide if certain features are for paid customers only. If you watch our conference talks, you will find we’ve been hinting at commercial applications of this infrastructure for years. We are bullish about the technology stack we’ve built and intend to pursue those commercial applications ourselves. Our business will build on the open source project, not attempt to monetize it directly.

I’m excited about this because other people are, to some degree. I know the “secure by default” nature of it is exciting to my extremely security-conscious co-founder, Alex.

I find features like “TypeScript out of the box” interesting. While I don’t really use TypeScript myself, I find it striking just what a big deal it is. Talking to Laurie Voss a few years back, I learned that nearly two-thirds of developers were using it, and it doesn’t seem like it has lost any steam. And you’ve got Scott Tolinski over here waxing poetic about how GraphQL is all typed and you get this dreamy fully-typed stack when TypeScript is part of the mix.

There is already a bundler (literally, Bundler) for Deno that supports TypeScript out of the box, along with JSX. Guess what else does? The big next-gen build tools, Snowpack, Vite, and wmr.

Deno is also written in Rust, which is an interesting angle to all this, partially because of the speed (it’s fast). Snowpack and Vite both use esbuild under the hood, which is written in Go (also fast). I don’t have a great sense of whether Go or Rust is faster for this type of work, but they are both a big leap forward from most of the bundlers and task runners we use today. You can even use esbuild directly, or with light abstractions over it, like Estrella. Again, TypeScript supported.

It makes me wonder about Babel. If you don’t run TypeScript through it, and don’t need it for JSX, and don’t need to compile away basic ES6/7 stuff now that support is so wide, how long does it stick around? A long time, is the answer, of course. A project as big as Babel doesn’t just disappear. I suppose all it takes is one exotic and desirable new JavaScript feature that is compilable to an existing syntax, and everybody will be putting it back into their pipelines.

The post The Deno Company appeared first on CSS-Tricks.

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

Click Outside Detector

Css Tricks - Fri, 04/02/2021 - 4:49am

It’s a reasonable UX thing that you can click-to-open something, and then not only be able to click that same thing to close it, but click outside the thing that it opened to close it. Kitty Giraudel just blogged about that. The trick is that once the thing is opened, you attach an event handler to the window with that watches for events (like another click). If that subsequent click did not happen within the newly-opened area, close it. Like, literally thing.contains(event.target). It’s a nice trick, I think.

There are lots of little things to think about though. For example:

we have to stop the propagationg of the click event on the toggle itself. Otherwise, it goes up to the window click listener, and since the toggle is not contained within the menu, it would close the latter as soon as we try to open it.

Right right. Can’t have that or it breaks the whole thing.

We have this same pattern in a lot of places on CodePen. Like Kitty, we have it implemented in React. In taking a peek at our implementation, it’s got a number of bells-and-whistles I figured were worth mentioning. For example, ours isn’t a function or hook, but a component wrapper we use like:

<ClickOutsideDetector listen onClickOutside={() => { closeTheThing(); }} > A Menu or Modal or something. </ClickOutsideDetector>

That way it is a generic wrapper that we can use for anything on a “click outside”. The bells-and-whistles being:

  • You can pass in component prop so that it doesn’t have to manifest as a <div> but whatever you want it to be semantically.
  • The listen prop allows you to toggle if it is currently actively listening to events. Like a quick way to short-circuit it.
  • An ESC keypress is the functional equivalent to clicking outside.
  • Handles touch events as well as clicks
  • Handles a case where the click outside happens into an <iframe> in which case the window has a blur event rather than a click.
  • Allows you to pass in elements to ignore, so rather than the stopPropagation trick that Kitty documented, we can be specific about elements that don’t trigger a click outside.

So many little things! To me this is the kinda perfect little example of real-world development. You just want one little behavior and ultimately there are a ton of considerations and edge cases you have to deal with and it’s never really done. I just touched our component in the last few months because of a third-party tool we used changed how they did something which affected iframes in use on the page. Ultimately I had to watch for a blur event that then check the classList of document.activeElement to see if that was the thing eating the click outside!

Annnnyway, I tossed up a only-slightly-dumbed-down version of ours here.

And I saw something from Kitty’s post that we weren’t handling, and it’s in the very first sentence:

we needed a way to close the menu when clicking outside of it or tabbing out of it.

Emphasis mine. Don’t worry, I’ve got a TODO in our code for that now.

The post Click Outside Detector appeared first on CSS-Tricks.

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

WordPress Caching: All You Need To Know

Css Tricks - Thu, 04/01/2021 - 11:21am

Here’s Ashley Rich at Delicious Brains writing about all the layers of caching that are relevant to a WordPress site. I think we all know that caching is complicated, but jeez, it’s a journey to understand all the caches at work here. The point of cache being speed and reducing burden on the worst bottlenecks and slowest/busiest parts of a web stack.

Here’s my own understanding:

  • Files can be cached by the browser. This is the fastest possible cache as no network request happens at all. Assets like images, CSS, and JavaScript are often cached this way because they don’t change terribly frequently, but you have to make sure you’re telling browsers that it’s OK to do this and have a mechanism in place to break that cache if you need to (e.g. by changing file names). You very rarely cache the HTML this way, as it changes the most and file-name-cache-busting of HTML seems more tricky than it’s worth.
  • Files can be cached at the CDN level. This is great because even though network traffic is happening, CDN servers are very fast and likely geographically closer to users than your origin server. If users get files from here, they never even trouble your origin server. You’ll need a way to break this cache as well, which again is probably through changing file names. You might cache HTML at this level even without changing file names if you have a mechanism to clear that cache globally when content changes.
  • The origin server might cache built HTML pages. On a WordPress site, the pages are built with PHP which probably triggers MySQL queries. If the server can save the result of the things that have already executed, that means it can serve a “static” file as a response, which it can do much faster than having to run the PHP and MySQL. That’ll work for logged out users, who all get the same response, but not for logged in users who have dynamic content on the page (like the WordPress admin bar).
  • The database has its own special caching. After a MySQL query is executed, the results can be saved in an Object Cache, meaning the same request can come from that cache instead of having to run the query again. You get that automatically to some degree, but ideally it gets wired up to a more persistent store, which you do not get automatically

Phew. It gets a little easier with Jamstack since your pages are prebuilt and CDN-hosted already, and in the case of Netlify, you don’t even have to worry about cache busting.

But even as complex as this is, I don’t worry about it all that much. This WordPress site uses Flywheel for hosting which deals with the database and server-level caching, I have Cloudflare in front of it with special WordPress optimization for the CDN caching, and roll-my-own file-name cache busting (I wish this part was easier). I’d certainly trust SpinupWP to get it right too, given Ashley’s great writeup I’m linking to here.

Direct Link to ArticlePermalink

The post WordPress Caching: All You Need To Know appeared first on CSS-Tricks.

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

React Authentication & Access Control

Css Tricks - Thu, 04/01/2021 - 11:20am

Authentication and access control are required for most applications, but they often distract us from building core features. In this article, I’ll cover a straightforward way to add auth and access control in React.

Instead of adding a static library that you have to keep up to date or re-research each time you build a project, we’ll use a service that stays up to date automatically and is a much simpler alternative to Auth0, Okta, and others.

React authentication

There are two main things your React application needs to do to sign on a user:

  1. Get an access token from an authentication server
  2. Send the access token to your backend server with each subsequent request

These steps are the same for pretty much all authentication, whether that’s standard email and password, magic links, or single sign on (SSO) providers like Google, Azure, or Facebook.

Ultimately, we want our React app to send an initial request to an authentication server and have that server generate an access token we can use.

JWT access tokens

There are different choices for what type of access token to use, and JSON Web Tokens (JWTs) are a great option. JWTs are compact, URL-safe tokens that your React application can use for authentication and access control.

Each JWT has a JSON object as its “payload” and is signed such that your backend server can verify that the payload is authentic. An example JWT looks like:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9.f7iKN-xi24qrQ5NQtOe0jiriotT-rve3ru6sskbQXnA

The payload for this JWT is the middle section (separated by periods):

eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9

The JWT payload can be decoded from base64 to yield the JSON object:

JSON.parse(atob("eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9")); // => { “userId”: 1, “authorization”: “admin” }

It’s important to note that this payload is readable by anyone with the JWT, including your React application or a third party. Anyone that has the JWT can read its contents.

However, only the authentication server can generate valid JWTs. Your React application, your backend server, or a malicious third party cannot generate valid JWTs, only read and verify them.

When your backend server receives a request with a JWT, it should verify the JWT as authentic by checking it against the public key for that JWT. This allows your application server to verify incoming JWTs and reject any tokens that were not created by the authentication server (or that have expired).

The flow for using a JWT in your React application looks like this:

  1. Your React app requests a JWT from the authentication server whenever the user wants to sign on.
  2. The authentication server generates a JWT using a private key and then sends the JWT back to your React app.
  3. Your React app stores this JWT and sends it to your backend server whenever your user needs to make a request.
  4. Your backend server verifies the JWT using a public key and then reads the payload to determine which user is making the request.

Each of these steps is simple to write down, but each step has its own pitfalls when you actually want to implement it and keep it secure. Especially over time, as new threat vectors emerge and new platforms need to be patched or supported, the security overhead can add up quickly.

Userfront removes auth complexity in React apps

Userfront is a framework that abstracts away auth complexity. This makes it much easier for you to work with authentication in a React application and, perhaps most importantly, it keeps all the auth protocols updated for you automatically over time.

The underlying philosophy with Userfront is that world-class auth should not take effort – it should be easy to set up, and security updates should happen for you automatically. Userfront has all the bells and whistles of authentication, Single Sign On (SSO), access control, and multi-tenancy, with a production-ready free tier up to 10,000 monthly active users.

For most modern React applications, it’s a great solution.

Setting up authentication in React

Now we’ll go through building all the main aspects of authentication in a React application. The final code for this example is available here.

Set up your React application and get your build pipeline in order however you prefer. In this tutorial, we’ll use Create React App, which does a lot of the setup work for us, and we’ll also add React Router for client-side routing. Start by installing Create React App and React Router:

npx create-react-app my-app cd my-app npm install react-router-dom --save npm start

Now our React application is available at http://localhost:3000.

Like the page says, we can now edit the src/App.js file to start working.

Replace the contents of src/App.js with the following, based on the React Router quickstart:

// src/App.js import React from "react"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; export default function App() { return ( <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/login">Login</Link> </li> <li> <Link to="/reset">Reset</Link> </li> <li> <Link to="/dashboard">Dashboard</Link> </li> </ul> </nav> <Switch> <Route path="/login"> <Login /> </Route> <Route path="/reset"> <PasswordReset /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> <Route path="/"> <Home /> </Route> </Switch> </div> </Router> ); } function Home() { return <h2>Home</h2>; } function Login() { return <h2>Login</h2>; } function PasswordReset() { return <h2>Password Reset</h2>; } function Dashboard() { return <h2>Dashboard</h2>; }

Now we have ourselves a very simple app with routing:

RouteDescription/Home page/loginLogin page/resetPassword reset page/dashboardUser dashboard, for logged in users only

This is all the structure we need to start adding authentication.

Signup, login, and password reset with Userfront

Go ahead and create a Userfront account. This will give you a signup form, login form, and password reset form you can use for the next steps.

In the Toolkit section of your Userfront dashboard, you can find the instructions for installing your signup form:

Follow the instructions by installing the Userfront React package with:

npm install @userfront/react --save npm start

Add the signup form to your home page by importing and initializing Userfront, and then updating the Home() function to render the signup form.

// src/App.js import React from "react"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; import Userfront from "@userfront/react"; Userfront.init("demo1234"); const SignupForm = Userfront.build({ toolId: "nkmbbm", }); export default function App() { return ( <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/login">Login</Link> </li> <li> <Link to="/reset">Reset</Link> </li> <li> <Link to="/dashboard">Dashboard</Link> </li> </ul> </nav> <Switch> <Route path="/login"> <Login /> </Route> <Route path="/reset"> <PasswordReset /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> <Route path="/"> <Home /> </Route> </Switch> </div> </Router> ); } function Home() { return ( <div> <h2>Home</h2> <SignupForm /> </div> ); } function Login() { return <h2>Login</h2>; } function PasswordReset() { return <h2>Password Reset</h2>; } function Dashboard() { return <h2>Dashboard</h2>; }

Now the home page has your signup form. Try signing up a user:

Your signup form is in “Test mode” by default, which will create user records in a test environment you can view separately in your Userfront dashboard:

Continue by adding your login and password reset forms in the same way that you added your signup form:

// src/App.js import React from "react"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; import Userfront from "@userfront/react"; Userfront.init("demo1234"); const SignupForm = Userfront.build({ toolId: "nkmbbm", }); const LoginForm = Userfront.build({ toolId: "alnkkd", }); const PasswordResetForm = Userfront.build({ toolId: "dkbmmo", }); export default function App() { return ( <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/login">Login</Link> </li> <li> <Link to="/reset">Reset</Link> </li> <li> <Link to="/dashboard">Dashboard</Link> </li> </ul> </nav> <Switch> <Route path="/login"> <Login /> </Route> <Route path="/reset"> <PasswordReset /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> <Route path="/"> <Home /> </Route> </Switch> </div> </Router> ); } function Home() { return ( <div> <h2>Home</h2> <SignupForm /> </div> ); } function Login() { return ( <div> <h2>Login</h2> <LoginForm /> </div> ); } function PasswordReset() { return ( <div> <h2>Password Reset</h2> <PasswordResetForm /> </div> ); } function Dashboard() { return <h2>Dashboard</h2>; }

At this point, your signup, login, and password reset should all be functional.

Your users can sign up, log in, and reset their password.

Access control in React

Usually, we don’t want users to be able to view the dashboard unless they are logged in. This is known as a protected route.

Whenever a user is not logged in but tries to visit /dashboard, we can redirect them to the login screen.

We can accomplish this by updating the Dashboard component in src/App.js to handle the conditional logic.

When a user is logged in with Userfront, they will have an access token available as Userfront.accessToken(). We can check for this token to determine if the user is logged in. If the user is logged in, we can show the dashboard page, and if the user is not logged in, we can redirect to the login page.

Add the Redirect component to the import statement for React Router, and then update the Dashboard component to redirect if no access token is present.

// src/App.js import React from "react"; import { BrowserRouter as Router, Switch, Route, Link, Redirect, // Be sure to add this import } from "react-router-dom"; // ... function Dashboard() { function renderFn({ location }) { // If the user is not logged in, redirect to login if (!Userfront.accessToken()) { return ( <Redirect to={{ pathname: "/login", state: { from: location }, }} /> ); } // If the user is logged in, show the dashboard const userData = JSON.stringify(Userfront.user, null, 2); return ( <div> <h2>Dashboard</h2> <pre>{userData}</pre> <button onClick={Userfront.logout}>Logout</button> </div> ); } return <Route render={renderFn} />; }

Notice also that we’ve added a logout button by calling Userfront.logout() directly:

<button onClick={Userfront.logout}>Logout</button>

Now, when a user is logged in, they can view the dashboard. If the user is not logged in, they will be redirected to the login page.

React authentication with an API

You’ll probably want to retrieve user-specific information from your backend. In order to protect your API endpoints, your backend server should check that incoming JWTs are valid.

There are many libraries available to read and verify JWTs across various languages; here are a few popular libraries for handling JWTs:

Node.js.NETPythonJava Your access token

While the user is logged in, their access token is available in your React application as Userfront.accessToken().

Your React application can send this as a Bearer token inside the Authorization header to your backend server. For example:

// Example of calling an endpoint with a JWT import Userfront from "@userfront/react"; Userfront.init("demo1234"); async function getInfo() { const res = await window.fetch("/your-endpoint", { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${Userfront.accessToken()}`, }, }); console.log(res); } getInfo();

To handle a request like this, your backend should read the JWT from the Authorization header and verify that it is valid using the public key found in your Userfront dashboard.

Here is an example of Node.js middleware to read and verify the JWT access token:

// Node.js example (Express.js) const jwt = require("jsonwebtoken"); function authenticateToken(req, res, next) { // Read the JWT access token from the request header const authHeader = req.headers["authorization"]; const token = authHeader && authHeader.split(" ")[1]; if (token == null) return res.sendStatus(401); // Return 401 if no token // Verify the token using the Userfront public key jwt.verify(token, process.env.USERFRONT_PUBLIC_KEY, (err, auth) => { if (err) return res.sendStatus(403); // Return 403 if there is an error verifying req.auth = auth; next(); }); }

Using this approach, any invalid or missing tokens would be rejected by your server. You can also reference the contents of the token later in the route handlers using the req.auth object:

console.log(req.auth); // => { mode: 'test', tenantId: 'demo1234', userId: 5, userUuid: 'ab53dbdc-bb1a-4d4d-9edf-683a6ca3f609', isConfirmed: false, authorization: { demo1234: { tenantId: 'demo1234', name: 'Demo project', roles: ["admin"], permissions: [] }, }, sessionId: '35d0bf4a-912c-4429-9886-cd65a4844a4f', iat: 1614114057, exp: 1616706057 }

With this information, you can perform further checks as desired, or use the userId or userUuid to look up user information to return.

For example, if you wanted to limit a route to admin users only, you could check against the authorization object from the verified access token, and reject any tokens that don’t have an admin role:

// Node.js example (Express.js) app.get("/users", (req, res) => { const authorization = req.auth.authorization["demo1234"] || {}; if (authorization.roles.includes("admin")) { // Allow access } else { // Deny access } }); React SSO (Single Sign On)

With your Toolkit forms in place, you can add social identity providers like Google, Facebook, and LinkedIn to your React application, or business identity providers like Azure AD, Office365, and more.

You do this by creating an application with the identity provider (e.g. Google), and then adding that application’s credentials to the Userfront dashboard. The result is a modified sign on experience:

No additional code is needed to implement Single Sign On using this approach: you can add and remove providers without updating your forms or the way you handle JWTs.

Final notes

React authentication and access control can be complex to do yourself, or it can be simple when using a service.

Both the setup step and, more importantly, the maintenance over time, are handled with modern platforms like Userfront.

JSON Web Tokens allow you to cleanly separate your auth token generation layer from the rest of your application, making it easier to reason about and more modular for future needs. This architecture also allows you to focus your efforts on your core application, where you are likely to create much more value for yourself or your clients.

For more details on adding auth to your React application, visit the Userfront guide, which covers everything from setting up your auth forms to API documentation, example repositories, working with different languages and frameworks, and more.

Create a free Userfront project

The post React Authentication & Access Control appeared first on CSS-Tricks.

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

Designing calculator apps

Css Tricks - Thu, 04/01/2021 - 4:23am

It is extremely weird that the calculator apps, even the default ones baked into desktop operating systems, embrace the UI and UX of those little cheap-o plastic physical calculators. I like what Florens Verschelde’s Math teacher had to say:

I had a Math teacher who would ban pocket calculators that didn’t show both your input and its result at the same time. If a calculator couldn’t show this:

38 ÷ 1.2 = 31.666666666667

You couldn’t use it.

The article ends up being in praise of Soulver, which I agree is a nice modern take on the idea of calculations.

I wish it was on Setapp, as I’d probably use it. But I don’t do enough regular mathin’ to go for the full price (today, anyway).

Direct Link to ArticlePermalink

The post Designing calculator apps appeared first on CSS-Tricks.

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

Where the World Wide Web Shines

Css Tricks - Wed, 03/31/2021 - 12:17pm

Here’s a fabulous post by Vitaly Friedman that looks at how to make accessible front-end components and what problems there are today when it comes to building them.

There’s so much great info packed into this one post that I’m going to keep it open in a tab for quite some time. But I have two thoughts here. First, just skimming through the article is enough to make anyone realize that accessibility is a complex subject and it’s so very easy to get things wrong; colors, images, text, HTML, mouse pointer vs. touch, small screens vs. large screens, charts and data viz, form components, layout and semantic ordering. The list goes on and on. It’s clear to me now (and I am late to the party) that accessibility is a full-time job.

Second, Vitaly makes note of some of the excellent work that the Government Digital Service (GDS) is doing in the UK by releasing open-source components such as accessible-autocomplete. And I have to say, I think the work that GDS is doing is so very inspiring to me as a web designer and developer.

Here’s a story: a few years ago I had to book an appointment to get a driver’s license. I hopped on the website and, immediately, I recognized that it was using the GDS design system. That gave me a great sigh of relief, but then I found myself sailing through this form at lightning speed. By the end, I realized that this is what every website should feel like; I used the site, did what I needed to do as quickly as possible, and then left.

It was one of the most shocking experiences for me as a web designer because there was no cruft, no junk, and no messing around with the experience in any way. It was fast, didn’t break down, crash the browser, or confuse me at all. The form inputs were big and clickable and the correct keyboard was selected when I viewed it on my phone. All of this accessibility work that they’ve poured into making things just work is a joyous thing.

This reminds me of something that Jeremy Keith wrote about the other day when he used another government website to get vaccinated:

[…] it’s a sequence of short forms, clearly labelled. Semantic accessible HTML, some CSS, and nothing more. If your browser doesn’t support JavaScript (or you’ve disabled it for privacy reasons), that won’t make any difference to your experience. This is the design system in action and it’s an absolute pleasure to experience.

[…] Maybe I’ll never need to visit that URL again. In the case of the NHS, I hope I won’t need to visit again. I just need to get in, accomplish my task, and get out again. This is where the World Wide Web shines.

Direct Link to ArticlePermalink

The post Where the World Wide Web Shines appeared first on CSS-Tricks.

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

Syndicate content
©2003 - Present Akamai Design & Development.