Web Standards

Your Team is Not “Them”

Css Tricks - Wed, 04/28/2021 - 9:35am

This post was written for engineering managers, but anyone is welcome to read it.

Let’s talk for a moment about how we talk about our teams. This might not seem like something that needs a whole article dedicated to it, but it’s actually quite crucial. The way that we refer to our teams sends signals: to stakeholders, to your peers, to the team itself, and even to ourselves. In addressing how we speak about our teams, we’ll also talk about accountability.

I have noticed shared similarities in those folks I consider good managers whose teams deliver well, and those who don’t. It starts with how they communicate about their teams.

Your team is “we”

There can be a perception that as a manager of an organization you are in control at all times. Part of that control can invariably be perceived as how you appear to be in charge, are competent, or how you personally perform. Due to that, some bad behaviors can arise- not due to malice, but due to fear. For this reason, it can be tempting to take credit for success and avoid credit when there is failure.

The irony is that the more that you try to hold on to these external perceptions, the more it will slip away. Why? Because the problems you are solving as a manager really aren’t about you.

Your team is “we”. You are a driving force of that team, no matter how high up the hierarchy chain. What happens on that team is your responsibility. When you speak about your org, you should include yourself in the statement.

When your team succeeds in something though, then you can praise them and leave yourself out of it. Here’s an example:

They really pulled this project over the line, despite the incredibly tight project timeline. Everyone showed up and was driven throughout the engagement. They did a fantastic job.

However, if the team failed at something, the pronoun is then I:

I didn’t recognize how tight this turnaround was and failed to prioritize the team’s time well. I need to reconvene with everyone so we can come up with a better plan.

And never, ever them:

They didn’t adhere to this tight timeline. They just weren’t able to get this project over the line.

Do you see how the last example shirks responsibility for what occurred? Too often I will hear managers relieve themselves of their duties when shit hits the fan, and that is exactly when a manager needs to step up, and dive in to the problems that are their responsibility.

Photo by Marvin Meyer on Unsplash The wider organization

There is another piece of this too, and it impacts how your team operates. It’s that your job is not to be the ambassador of who you manage and think of every other group as separate. You’re part of a larger system. A company is composed of groups, but those groups can only be successful if they’re working together, not if they are protecting their own org at all costs.

I admit I didn’t fully understand the depth of this until I read Patrick Lencioni’s great book The Advantage, thanks to Dalia Havens, a peer at Netlify. In the book, Lencioni talks about how organizational health, not “being smart”, as the biggest key to success. Plenty of smart people with good ideas build companies and see them fail. Success lies in being able to work together.

Fundamentally, other groups at the company are not separate from your group, rather that you’re all part of one whole. The Leadership Team is also a team, and should be treated as your team. How you speak about this team is equally important.

As such, when we talk about successes and failures of any groups, these should also be shared. There should be a sense that you’re all working towards a common goal together, and every group contributes to it. Within a leadership team there should be trust and vulnerability to own their part so that the whole organization can operate at its best.

And, yes, the leadership team as well

You may see where I’m going with this: when you talk about the leadership team, this is “we” too. You can’t speak to your team about decisions that were made at a table with your peers and boss and say “they decided something you don’t agree with” even if you don’t agree. You were there, ideally you took part in that decision, when you talk about that team, presenting them as “we” is important as well.

Why? Because as a manager, our job is to try as much as we can to drive balance and clarity. It’s confusing and disorienting to hear a manager talk about a leadership team they are on as though they aren’t a part of it and not take accountability for what’s happening there. Your reports themselves can’t effect change at that level, so if you don’t own your involvement in the leadership group, you can demoralize your staff and make them feel distrustful of other parts of the company. This can have an effect where folks demonize other teams and their initiatives, which as we discussed is ultimately unhealthy.

Saying “we” holds you accountable to your team for leadership decisions that you are a part of, which is how it should be. If people on your team have issues with the direction, it’s also your responsibility to own that conversation and next steps, as a liaison to the leadership team.

There are of course, some small instances when this might not be appropriate. Something that really goes against your core values that you fought strongly against can make this untenable. I would say those instances should ideally be very infrequent, or unfortunately you may need to pursue another place to work.

Speaking about the Leadership Team in Practice

Here’s how this works in practice, using an example of conveying a decision at the leadership level to the people who report to you:

The leadership team decided that we need to ship at least 3 features this quarter so I guess that’s what we have to figure out to do.


One of the key OKRs this quarter is that we as a company need to double the signups to our platform. We’ve done some calculations that show we can almost certainly get there by shipping 3 features, so let’s all talk about what we can do within our group to make that possible. If you’re curious, we can chat through what initiatives other groups are doing to support this as well.

The first is not just passive, but demotivating. I have made the mistake of using this approach when I want to be liked by my employees and for them to think of me as a peer. But we’re not peers, I have a responsibility to them.

You’ll note in the second approach, we also explained the reasoning behind the decision. I’ve noticed personally that when I have to hold myself accountable to the decision, I care a bit more that people understand the reasoning behind it. This is a very good thing for the morale on your team! Which is arguably one of your most important jobs.

The last line in the second approach also opens up discussion- since you’re taking ownership of the decision, you’re also owning that you know about other pieces of the puzzle, and show a willingness to dive in with your team.

What if you make a mistake?

We all do! Management can be difficult and it’s impossible to be perfect all the time. Try not to beat yourself up, but perhaps show a bit more thoughtfulness next time. I’ve made lots of mistakes as well. It’s not a stick to beat up yourself or others, but a lesson learned to be as mindful as possible and promote a better working environment.

We communicate to our teams, peers, and stakeholders whether or not we’re taking responsibility as a true leader in these moments. We communicate whether we’ll approach a problem with humility, and a desire to collaborate and improve. This may seem to be a detail, but it’s a powerful piece of leading an organization.

The post Your Team is Not “Them” appeared first on CSS-Tricks.

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

Eliminating five top compatibility pain points on the web

Css Tricks - Fri, 04/23/2021 - 10:55am

Robert Nyman and Philip Jägenstedt:

Google is working with other browser vendors and industry partners to fix the top five browser compatibility pain points for web developers. The areas of focus are CSS Flexbox, CSS Grid, position: sticky, aspect-ratio, and CSS transforms.

[…] The goal in 2021 is to eliminate browser compatibility problems in five key focus areas so developers can confidently build on them as reliable foundations.

I’d say slow clap, but I don’t want to sound sarcastic. Full on, regular clapping.

Ten, fifteen years ago, the job of a web designer and developer was heavily thinking about, planning for, and dealing with cross-browser compatibility. These days, it’s still a thing, but it’s not about dealing with bugs, quirks, and frustrating implementation differences like it was then. It’s more edge-case stuff with more obvious work-arounds. And when we’re thinking about the browser and device landscape, we’re doing it through the lens of meeting our users where they are and embracing that landscape. Doing our best, anyway.

If the powers that be can keep chipping away at compatibility problems, that further cements the web as the correct place to build.

I vote for some kind of proper stab at reliable viewport units in 2022, that somehow sensibly handle scrollbars and other browser chrome.

Direct Link to ArticlePermalink

The post Eliminating five top compatibility pain points on the web appeared first on CSS-Tricks.

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

The Almost-Complete Guide to Cumulative Layout Shift

Css Tricks - Thu, 04/22/2021 - 9:04am

Here’s Jess B. Peck writing all about Google’s Core Web Vitals:

Let’s step back one. CLS is when you’re about to click on a link, and the whole page shifts and you click on a different link instead. It’s when you’re halfway through a blogpost and an ad loads and you lose your place. It is when… the layout shifts. At least, that’s what it’s trying to measure– both those shifts, how often they happen, and the irritation that causes the user.

I didn’t quite understand just how complex Cumulative Layout Shift is before reading Jess’s piece. As Jess explains:

CLS is a measure for a robot to approximate the user perception of instability. This means we’re getting a unit of change over time. It’s a three dimensional equation, and there are tons of things that can affect it. […] The idea is more to alert devs to a problem area, rather than be a perfect measurement of how annoying a page is.

I had this problem on, of all places, Google dot com. I kept tapping an element just as it appeared on screen and this sent me to the wrong page.

Jess notes that these metrics are sometimes more of an art than a science, and so we shouldn’t be obsessed with making sure that just these Core Web Vital metrics are okay. Chris mentioned a while ago that he worries folks might begin to game these metrics for improving their SEO:

This feels like the start of a weird new era of web performance where the metrics of web performance have shifted to user-centric measurements, but people are implementing tricky strategies to game those numbers with methods that, if anything, slightly harm user experience.

Harry Roberts mentioned something similar:

A new and unusual phenomenon: clients reluctant (even refusing) to fix performance issues unless they directly improve Vitals.

— Harry Roberts (@csswizardry) March 5, 2021

I feel like this is our responsibility as web developers, to explain that what we want to do here is reduce user misery on our websites. That’s not to say it’s easy, though, and there’s certainly not much we can do to avoid the shady folks who’ll game these metrics only to improve SEO.

As Jeremy wrote just the other day:

The map is not the territory. The numbers are a proxy for user experience, but it’s notoriously difficult to measure intangible ideas like pain and frustration.

Direct Link to ArticlePermalink

The post The Almost-Complete Guide to Cumulative Layout Shift appeared first on CSS-Tricks.

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

Tools to Improve UX and Win Over Your Customers

Css Tricks - Thu, 04/22/2021 - 9:03am

Try Hotjar for free today!

An enjoyable user experience and high conversion rates go hand-in-hand. It makes sense then, that if you want to improve conversion rates, your first task is to improve user experience.

To improve UX, deeply understanding your users is non-negotiable. But speaking with customers one by one to figure out their pain points isn’t a feasible or fast strategy.

Which is why today, we’re showing you different tools you can use to learn more about your customers so you can make quick and impactful changes that improve UX and increase conversions!

Why your visitors aren’t converting

Before we talk about the different tools to help you understand your users, let’s look at five main reasons why you don’t see as many conversions as you’d like:

Your website is confusing to navigate: If navigating your way through a cornfield maze is easier than trying to get to the checkout page on your website, you have a problem. Users like it when they can get from point A to point B with no roadblocks or confusion.

You’re guessing what your visitors want (but don’t actually know): As someone who knows your product inside and out, it can be hard to take a step back to try and get into the mind of your visitors. Unless you’re gathering voice of customer data, any changes you make will be based on guesswork.

Your visitors are distracted: If your visitors are distracted by unimportant elements on your page, they’ll miss your CTA and won’t convert. Even worse, they’ll get frustrated by a lack of flow and leave your website (and maybe even head over to your competitor).

There are roadblocks you aren’t aware of: Using your website might be easy for you and your team—you helped build it, after all—but that bias makes it challenging to see roadblocks and other issues that could prevent your users from having an enjoyable experience.

Your users don’t trust your website: Things like poor design, spelling issues in your copy, and low-quality imagery can turn potential customers away.

Simply put, if your website is clunky, you’ll irritate your users, and they won’t convert. But when you understand what your visitors want—and why they want it—you’ll be able to build an experience they love.

4 tools to improve UX and conversions

At Hotjar, we want to help you make your users happy and avoid the above problems! Here are four tools you can use to improve your user experience, make your customers smile from ear to ear, and as a result, skyrocket your conversions!


Heatmaps are visual representations of your analytical data, organized so you can easily spot popular (and unpopular) areas of your website. With heatmaps, you can figure out which areas of your website contribute to a poor user experience.

You can use three types of heatmaps: scroll maps, click maps, and move maps.

Scroll maps A scroll map

Scroll maps show you how far users scroll down your page. Red areas mean more visitors went to that part of your page, whereas the blue areas signal low activity. They can help you understand if users see key information.

Click maps A click map

Click maps show an aggregate of where users click their mouse or tap the screen on desktop and mobile devices. Click maps help you understand if your CTAs are in the right place, if people are clicking on clickable items, and whether or not users are ‘rage clicking’ on your site.

Move maps A move map

Move maps show where users move their mouse as they go through your page. Research suggests that mouse movement correlates with eye movement, helping you understand what people look at on your website.

You can use Hotjar’s Heatmaps to:

  • See whether important information is within the “hottest” areas of your heatmap (or if it’s being missed because it’s in areas where visitors aren’t scrolling to)
  • Decide where to move essential information based on where your users focus their attention
  • Spot where your visitors’ attention drops
  • A/B test to see how user behavior changes
  • Make sure clicks and taps happen on “clickable” elements
  • See how behavior changes on different devices (i.e., desktop, tablet, and mobile)

It’s easy to improve UX when you know how your users are really using your website. Recordings let you watch live playbacks of each user on your site so you can see exactly how your visitors navigate through your website, identify roadblocks, and make sense of your web analytics.

Recordings let you see mouse movement, scrolling, clicks, and keyboard strokes across multiple pages on your site.

You can use Hotjar Recordings to:

  • Make sense of your bounce rate by analyzing why your visitors are leaving your page(s)
  • Empathize with your visitors by understanding their roadblocks and frustrations on your website
  • Uncover what’s preventing your visitors from converting
  • Find bugs and see if something’s not working as planned (like pages that load differently on mobile versus desktop)
  • See how long it really takes for users to convert (and identify what’s preventing them from converting sooner)
Incoming Feedback

With Hotjar’s Incoming Feedback tool, you can eliminate the guesswork and get into your user’s mind by placing feedback widgets right on your website. Your visitors can tell you exactly what they like and dislike, and you can use that information to fix issues and provide more of what your users love.

Incoming Feedback widget

Users can also highlight certain elements of your site, so you don’t need to guess what they’re referring to—you’ll know precisely what they’re talking about!

You can use Hotjar’s Incoming Feedback to:

  • Get feedback on specific elements on your website and understand why your users like and dislike certain aspects of your site
  • Gather the emotions of your website visitors
  • Pinpoint exactly which areas are causing trouble for your users
  • Track changes over time to see if user experience is improving

Gathering voice of customer (VOC) data is easy with Hotjar Surveys. Hotjar has two types of surveys: on-site and off-site.

On-site surveys let you ask your users questions while on specific pages of your website. By asking open- or closed-ended questions (or a mix of both), you can get into the mind of your visitor and get valuable feedback to improve your website and increase conversions.

You can also use Hotjar’s on-site surveys to follow up on specific questions. For example, if you asked, “did you find what you were looking for today?” and someone clicked “no,” you can have a follow-up question asking them to explain why.

You can use Hotjar’s Surveys to:

  • Validate new product ideas
  • Understand why your visitors like or dislike aspects of your site
  • Find areas of your site that need fixing (for example, place an on-site survey on pages with high bounce rates to figure out why users are leaving)
  • Gather valuable insight from your users
  • Improve conversions through post-purchase surveys
  • how your customers you care about their input
Try Hotjar for free today!

All of Hotjar’s tools collect powerful data in ways everyone on your team will be able to understand. Using data to drive your decision-making process will steer you in the right direction, keep users happy, and improve your conversion rates!

Click to sign up and see how easy it is to understand your users with Hotjar!

Try Hotjar for free today!

P.S. To get you up to speed, we’ve put together checklists to help you improve user experience and increase conversions during your free Hotjar trial 💯

The post Tools to Improve UX and Win Over Your Customers appeared first on CSS-Tricks.

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

Still Hoping for Better Native Page Transitions

Css Tricks - Wed, 04/21/2021 - 12:57pm

It would be nice to be able to animate the transition between pages if we want to on the web without resorting to hacks or full-blown architecture choices to achieve it. I could imagine an API that would run stuff, perhaps integrating with WAAPI, before the page is unloaded, and other stuff after the next page loads in. This, with otherwise regular ol’ anchor links and page loads.

We do have an onbeforeunload API, but I’m not sure what kind of baggage that has. We can technically build page transitions now, even without single-page-app architecture, but what I want are purpose-built APIs that help us do it cleanly (understandable functions) and with both performance (working as quickly as clicking links normally does) and accessibility (like focus handling) in mind.

If you’re building a single-page app anyway, you get the freedom to animate between views because the page never reloads. The danger here is that you might pick a single-page app just for this ability, which is what I mean by having to buy into a site architecture just to achieve this. That feels like an unfortunate trade-off, as single-page apps bring a ton of overhead, like tooling and accessibility concerns, that you wouldn’t have otherwise needed.

Without a single-page app, you could use something like Turbo and animate.css like this. Or, Adam’s new transition.style, a clip-path() based homage to Daniel Edan’s masterpiece. Maybe if that approach was paired with instant.page it would be as fast as any other internal link click.

There are other players trying to figure this out, like smoothState.js and Swup. The trick being: intercept the action to move to the next page, run the animation first, then load the next page, and animate the new page in. Technically, it slows things down a bit, but you can do it pretty efficiently and the movement adds enough distraction that the perceived performance might even be better.

Ideally, we wouldn’t have to animate the entire page but we could have total control to make more interesting transitions. Heck, I was doing that a decade ago with a page for a musician where clicking around the site just moved things around so that the audio would keep playing (and it was fun).

This would be a great place for the web platform to step in. I remember Jake pushed for this years ago, but I’m not sure if that went anywhere. Then we got portals which are… ok? Those are like if you load an iframe on the page and then animate it to take over the whole page (and update the URL). Not much animation nuance possible there, but you could certainly swipe some pages around or fade them in and out (hey here’s another one: Highway), like jQuery Mobile did back in ancient times.

The post Still Hoping for Better Native Page Transitions appeared first on CSS-Tricks.

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

Sticky Headers: 5 Ways to Make Them Better

Css Tricks - Wed, 04/21/2021 - 10:53am

Page Laubheimer says that if you’re going to do a sticky header…

  1. Keep it small.
  2. Visually contrast it with the rest of the page.
  3. If it’s going to move, keep it minimal. (I’d say, respect prefers-reduced-motion.)
  4. Consider “partially persistent headers.” (Jemima Abu calls it a Smart Navbar.)
  5. Actually, maybe don’t even do it.

I generally like the term “sticky” header, because it implies you should use position: sticky for them, which I think you should. It used to be done with position: fixed, but that was trickier to pull off since the header would move in-and-out of flow of the document. Using sticky positioning helps reserve that space automatically without JavaScript or magic numbers.

Direct Link to ArticlePermalink

The post Sticky Headers: 5 Ways to Make Them Better appeared first on CSS-Tricks.

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

How to Improve CSS Performance

Css Tricks - Mon, 04/19/2021 - 11:33am

There is no doubt that CSS plays a huge role in web performance. Milica Mihajlija puts a point on exactly why:

When there is CSS available for a page, whether it’s inline or an external stylesheet, the browser delays rendering until the CSS is parsed. This is because pages without CSS are often unusable.

The browser has to wait until the CSS is both downloaded and parsed to show us that first rendering of the page, otherwise browsing the web would be a terribly visually jerky to browse. We’d probably write JavaScript to delay page rendering on purpose if that’s how the native web worked.

So how do you improve it? The classics like caching, minification, and compression help. But also, shipping less of it, and only loading the bit you need and the rest after the first render.

It’s entirely about how and how much CSS you load, and has very little to do with the contents of the the CSS.

Direct Link to ArticlePermalink

The post How to Improve CSS Performance appeared first on CSS-Tricks.

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

Float an Element to the Bottom Corner

Css Tricks - Mon, 04/19/2021 - 4:17am

Need to lay out an element to the right or the left, such that text wraps around it? That’s an easy task for the float property. But what about if you also want to push that element (let’s call it an image) to one of the bottom corners while we’re at it? Sounds a bit tricky, right? We probably need JavaScript?

Nope, few lines of (tricky) CSS can do it! Here’s the CSS-only solution that will make your image to stick to the bottom corner, regardless of the size and content.

Resize the wrapper element and see the magic at work:

CodePen Embed Fallback

Let’s dissect the code.

Markup and layout

We’ll need a wrapper element to contain everything, and we’ll be using flexbox on it. Flexbox allows us to rely on the default stretch alignment to be able to later use height: 100%.

<div class="wrapper"> <div class="box"> <div class="float"><img></div> Lorem ipsum dolor ... </div> </div> .wrapper { display: flex; } .float { float: right; height: 100%; display: flex; align-items: flex-end; shape-outside: inset(calc(100% - 100px) 0 0); }

The .box within the .wrapper is our flex item. We don’t need any particular CSS applied to the box. It defines the height of the wrapper and, at the same time, is stretched to the same height. This behavior will give us a “reference height” that can be used by the child elements.

From the specification:

If the flex item has align-self: stretch, redo layout for its contents, treating this used size as its definite cross size so that percentage-sized children can be resolved.

The keyword is the definite which allows us to safely use a percentage (%) height inside the box element.

Now for the floated element

Our .float element will take the entire height next to the text content, thanks to the height calculation we detailed above. Inside this element we push the image to the bottom using flexbox alignment.

The image is floated to the right but the free space above it prevents the content from wrapping around it.

Now for the real trickery, using the shape-outside property. Here’s how MDN defines it:

The shape-outside CSS property defines a shape—which may be non-rectangular—around which adjacent inline content should wrap. By default, inline content wraps around its margin box; shape-outside provides a way to customize this wrapping, making it possible to wrap text around complex objects rather than simple boxes.

In other words, shape-outside sets the way content flows around an element’s bounding box.

It takes a number of values. One of those is the inset() function which, again, according to MDN:

Defines an inset rectangle. When all of the first four arguments are supplied they represent the top, right, bottom and left offsets from the reference box inward that define the positions of the edges of the inset rectangle.

So, with shape-outside: inset(calc(100% - X) 0 0) we can create an inset rectangle that starts exactly at the top of the image. And the top is equal to 100% - X, where X is the image height and 100% is the height of the .float element. This allows the text to wrap within the free space on the top of the image. This is responsive, plus we can easily switch between left and right (by adjusting the float property)

That’s it! The only major caveat is that you need to know the image height.

Want more?

We can extend this concept a little further to account for fancier situations. For example, we can float the image to the right, but pin it to the middle of the box with justify-content: center: and also adjust our inset rectangle to the middle by changing the shape-outside from inset(calc(100% - X) 0 0) to inset(calc(50% - X/2) 0 0)

CodePen Embed Fallback

We can also float two images at both bottom corners:

CodePen Embed Fallback

Nothing complex here. I am simply using the same floating element twice, once on the right, and again on the left. And why stop at two corners when we can place images at all four corners:

CodePen Embed Fallback

The same basic idea is at play here, but we’re are also relying on the common float feature for the top images. However, you’ll notice that this is where the concept starts to break down a bit, and we get some unwanted overflow depending on the size of the containing box. We can make the height of the .float element greater than 100% and apply somewhat “magic numbers” that smooth things out by adjusting the padding and margin of the images.

Did you know that shape-outside accepts radial-gradient() as a value? We can use that to place rounded images like below:

CodePen Embed Fallback

The transparent part of the gradient is the free space where the text can go. You may have noticed that we applied a border-radius to the image as well. The shape-outside property will simply affect the .float element and we need to manually adjust the shape of the image to follow the shape defined by shape-outside.

While we’re at it, let’s combine this with our earlier example that pins the image to the vertical center of the box using justify-content: center:

CodePen Embed Fallback

Another radial-gradient() and also another border-radius configuration.

We could have used a linear-gradient() instead to make a triangular shape for the wrapping area:

CodePen Embed Fallback

This is the same idea that we used for the radial-gradient(). The big difference is that we’re using clip-path instead of border-radius to cut our image.

And, since we did it for the others, let’s use the justify-content: center idea to pin the image to the vertical center of the box’s right edge:

CodePen Embed Fallback

We used a conic-gradient() in the above demo with shape-outside to define the triangular shape and clip-path to get a similar shape on the image

All of these examples can still be optimized using less of code in the case that the image is decorative (when it’s not needed inside the HTML for SEO purposes). Let’s replace the .float element with a pseudo-element and apply the image as background instead:

CodePen Embed Fallback

We’re using mask to show just the portion of the image that we need and, guess what, it uses the same value as shape-outside! So, all we had to do is define one value for the shape.

That’s it!

There are a lot of possibilities here to place not just rectangles in corners, but any kind of shape at any position, using largely the same code structure. We only need to:

  • Adjust the shape-outside property to define the shape
  • Apply some styling to the image to follow the previously defined shape or apply the same value to mask in case we are using the pseudo element version

Then everything holds it place, even in responsive designs.

The post Float an Element to the Bottom Corner appeared first on CSS-Tricks.

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

Platform News: Using :focus-visible, BBC’s New Typeface, Declarative Shadow DOMs, A11Y and Placeholders

Css Tricks - Fri, 04/16/2021 - 4:33am

There’s a whole lot of accessibility in this week’s news, from the nuances of using :focus-visible and input placeholders, to accessible typefaces and a Safari bug with :display: contents. Plus, a snippet for a bare-bones web component that supports style encapsulation.

Now may be a good time to start using :focus-visible

The CSS :focus-visible pseudo-class replaces :focus as the new way to create custom focus indicators for keyboard users. Chrome recently switched from :focus to :focus-visible in the user agent stylesheet and, as a result of that change, the default focus ring is no longer shown when the user clicks or taps a button.

When switching from :focus to :focus-visible, consider backwards compatibility. Your keyboard focus indicators should be clearly visible in all browsers, not just the ones that support :focus-visible. If you only style :focus-visible, non-supporting browsers will show the default focus ring which, depending on your design, “may not be sufficiently clear or visible at all.”

button { background: white; } button:focus-visible { outline: none; background: #ffdd00; /* gold */ }

A good way to start using :focus-visible today is to define the focus styles in a :focus rule and then immediately undo these same styles in a :focus:not(:focus-visible) rule. This is admittedly not the most elegant and intuitive pattern, but it works well in all browsers:

  • Browsers that don’t support :focus-visible use the focus styles defined in the :focus rule and ignore the second style rule completely (because :focus-visible is unknown to them).
  • In browsers that do support :focus-visible, the second style rule reverts the focus styles defined in the :focus rule if the :focus-visible state isn’t active as well. In other words, the focus styles defined in the :focus rule are only in effect when :focus-visible is also active.
button:focus { outline: none; background: #ffdd00; /* gold */ } button:focus:not(:focus-visible) { background: white; /* undo gold */ } The BBC created a more accessible typeface

The BBC created their own custom typeface called Reith (named after the BBC’s founder Sir John Reith). Their goal was to create a font that supports multiple languages and is easier to read, especially on small devices. The font was tested with mixed-ability user groups (dyslexia and vision impairment) and across different screen sizes.

We [the BBC] were using Helvetica or Arial. We also had Gill Sans as the corporate typeface. These typefaces were designed a hundred years ago for the printed page [and] don’t perform well on today’s modern digital screens.

Reith Sans can bee seen in use on BBC Sport

Note: If you’d like to inspect Reith Sans and Reith Serif in Wakamai Fondue, you can quickly access the URLs of the WOFF2 files in the “All fonts on page” section of the Fonts pane in Firefox’s DOM inspector on BBC’s website.

display: contents is still not accessible in Safari

The CSS display: contents value has been supported in browsers since 2018. An element with this value “does not generate any boxes” and is effectively replaced by its children. This is especially useful in flex and grid layouts, where the contents value can be used to “promote” more deeply nested elements to flex/grid items while retaining a semantic document structure.

Source: Manuel Rego Casasnovas

Unfortunately, this feature originally shipped with an implementation bug that removed the element from the browser’s accessibility tree. For example, applying display: contents to a <ul> element resulted in that element no longer mentioned by screen readers. Since then, this bug has been fixed in Firefox and Chrome (in the latest version).

View on CodePen

In Chrome and Firefox, the screen reader informs the user that the “Main, navigation” contains a “list, 2 items.” In Safari, the latter part is missing because the <ul> and <li> elements are not present in the accessibility tree. Until Apple fixes this bug in Safari, be careful when using the contents value on semantic elements and test in screen readers to confirm that your pages are accessible in Safari as well.

Set opacity when overriding the color of placeholder text

Accessibility experts recommend avoiding placeholders if possible because they can be confused for pre-populated text and disappear when the user starts entering a value. However, many websites (including Wikipedia and GOV.UK) use placeholders in simple web forms that contain only a single input field, such as a search field.

The subscription form for the CSS-Tricks newsletter uses a placeholder in the email field

Placeholders can be styled via the widely supported ::placeholder pseudo-element. If your design calls for a custom color for placeholder text, make sure to specify both color and opacity. The latter is needed for Firefox, which applies opacity: 0.54 to ::placeholder by default. If you don’t override this value, your placeholder text may have insufficient contrast in Firefox.

.search-field::placeholder { color: #727272; opacity: 1; /* needed for Firefox */ } The placeholder text on eBay’s website is lighter in Firefox and doesn’t meet the minimum contrast requirement of 4.5:1 Declarative shadow DOM could help popularize style encapsulation

One of the key features of shadow DOM is style encapsulation, wherein the outer page’s style rules don’t match elements inside the shadow tree, and vice versa. In order to use this feature, you need to attach a shadow DOM tree to an element (usually a custom element, like <my-element>) and copy the element’s template (usually from a <template> element in the DOM) to the element’s newly created shadow DOM.

These steps can only be performed in JavaScript. If you’re only interested in style encapsulation and don’t need any dynamic functionality for your element, here is the minimum amount of JavaScript required to create a custom element with a shadow DOM:

customElements.define( "my-element", class extends HTMLElement { constructor() { super(); // find <template id="my-template"> in the DOM let template = document.getElementById("my-template"); // make a copy of the template contents… let content = template.content.cloneNode(true); // …and inject it into <my-element>’s shadow DOM this.attachShadow({ mode: "open" }).appendChild(content); } } );

For an example of style encapsulation, see Miriam Suzanne’s <media-object> element on CodePen. The scoped styles are located in the <template> element in the HTML pane. Notice how this CSS code can use simple selectors, such as article, that only match elements inside <media-object>’s shadow DOM.

JavaScript may soon no longer be required to create this type of style encapsulation in modern browsers. Earlier this week, Chrome became the first browser to ship Google’s Declarative Shadow DOM proposal. If it becomes a standard, this feature will also make it possible to use Shadow DOM in combination with server-side rendering.

The post Platform News: Using :focus-visible, BBC’s New Typeface, Declarative Shadow DOMs, A11Y and Placeholders appeared first on CSS-Tricks.

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

Not Your Typical Horizontal Rules

Css Tricks - Fri, 04/16/2021 - 4:31am

The default browser style for <hr> is so weird. It’s basically:

border-style: inset; border-width: 1px;

The default border-color is black, but the border doesn’t actually look black, because the inset border “adds a split tone to the line that makes the element appear slightly depressed.”

If I kick up the border-width to 40px you can see it more clearly:

I often reset an <hr> to be “just a line” and it always gets me because I’ll try something, like height: 1px with a background at first, but that’s not right. The easier way to clear it is to turn off all the borders then only use border-top or border-bottom. Or, turn off all the borders, set a height, and use a background.

Annnyway… Sara has some of the nicest horizontal rules in town on the current design of her site, and she’s written it all up. Guess what? They aren’t even <hr> elements! It turns out the only styling hook you have is CSS, which wasn’t as adaptive as Sara needed, so she ended up with a <div role="separator"> (TIL!) and inline SVG.

The best way to get the full flexibility of an SVG is by inlining it. But the <hr> element is content-less — it has no opening and closing tags within which you can place other elements.

The only way to work around the limitations of <hr> while preserving semantics for screen reader users is to use a div and provide the semantics of an hr using ARIA.

Direct Link to ArticlePermalink

The post Not Your Typical Horizontal Rules appeared first on CSS-Tricks.

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

Flash of inAccurate coloR Theme (FART)

Css Tricks - Thu, 04/15/2021 - 12:13pm

There is a lot to think about when implementing a dark mode theme on a website. We have a huge guide on it. There are some very clever quick wins out there, but there are also some quite tricky things to pull off. One of those tricky things is how it’s not a dark mode “toggle” between dark and light, but really three modes you need to support: dark, light, and use system preference. That’s similar to how audio preferences work in many apps, which allow you to very specifically choose which audio input or output you want, or default to the system preference.

CSS and JavaScript can handle the system preference angle, via the prefers-color-scheme API, but if the user preference has changed, and that preference is now different than the user preference, you’re in the territory of “Flash of inAccurate coloR Theme” or FART. Ok ok, it’s a tounge-in-cheek acronym, but it’s potentially quite a visually obnoxious problem so I’m keeping it. It’s in the same vein that FOUT (Flash of Unstyled Text) is for font loading.

Storing a user preference means something like a cookie, localStorage, or some kind of database. If access to that data means running JavaScript, e.g. localStorage.getItem('color-mode-preference');, then you’re in FART territory, because your JavaScript is very likely running after a page’s first render, lest you’re otherwise unnecessarily delaying page render.

User preference is “dark” mode, but the system preference is “light” mode (or unset), so when the page refreshes, you get FART.

You can access a cookie with a server-side language before page-render, meaning you could use it to output something like <html class="user-setting-dark-mode"> and style accordingly, which deftly avoids FART, but that means a site that even has access to a server-side language (Jamstack sites do not, for example).

Allllll that to say that I appreciated Rob Morieson’s article about dark mode because it didn’t punt on this important issue. It’s very specifically about doing this in Next.js, and uses localStorage, but because Next.js is JavaScript-rendered, you can force it to check the user-saved preference as the very first thing it does. That means it will render correctly the the first time (no flash). You do have to turn off server-side rendering for this to work, which is a gnarly trade-off though.

I’m not convinced there is a good way to avoid FART without a server-side language or force-delayed page renders.

The post Flash of inAccurate coloR Theme (FART) appeared first on CSS-Tricks.

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

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! &#x1f9e1; 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…


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: &#x1f44f;&#x1f44f;&#x1f44f;. 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.


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).


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.

Syndicate content
©2003 - Present Akamai Design & Development.