If you use VS Code, you might have enabled the setting for re-opening a previously open file next time the app launches. I do. I like that.
Hey, thanks for remembering, buddy! 🤗
But sometimes you really, really don’t want that to happen.
I recently ran into one of those times! I had to reinstall my local copy of this site and, with it, the 3GB+ database that accompanies it. Being a WordPress site and all, I needed to open up the SQL database file to search-and-replace some stuff.
If you’ve ever tried to open a super duper large file in VS Code, then you know you might need to jiggle a few settings that increase the memory limit and all that. The app is super flexible like that. There’s even a nice extension that’ll both increase the memory and perform a search-and-replace on open.
Anyway, that big ol’ database file crashed VS Code several times and I wound up finding another way to go about things. However, VS Code keeps trying to open that file and inevitably crashes even though I nuked the file. And that means I wait for the MacOS beachball of fun to spin around before the app crashes and I can reopen it again for reals.
Well, I finally decided to fix that today and spent a little time searching around. One Stack Overflow thread suggests disabling extensions and increasing the memory limit via the command line. I’m glad that worked for some folks, but I had to keep looking.
Another thread suggests clearing the app’s cache from the command palette.
Nice, but no dice. 🎲
I wound up going with a scorched earth strategy shared by Jie Jenn in a helpful YouTube video. You’ve gotta manually trash the cached files from VS Code. The video walks through it in Windows, but it’s pretty darn similar in MacOS. The VS Code cache is located in your user folder.
Notice that I have the Backups folder highlighted there. Jie removed the files from the CachedData folder, but all that did was trigger a prompt for me to re-install the app. So, I took a risk and deleted what appeared to be a 3GB+ file in Backups. I showed that file the door and VS Code has been happy ever since.
Ask me again in a week and maybe I’ll find out that I really screwed something up. But so far, so good!
Let’s face it: building an AA or AAA-accessible product can be quite daunting. Luckily, having an accessible product isn’t all-or-nothing. Even seemingly small improvements can have nice quality of life benefits for many people.
In that spirit, here are five accessibility quick wins you can implement today.
Quick Win 1: Indicate the Current Page
It’s probably safe to assume that a different style is the most common way to communicate the current page of a site or app. However, even if those styles are clear and with great contrast ratios, they’re still only a visual cue.
So what happens if a person with limited vision cannot see that separation? How will they know what page they’re on?
Creating an accessible product is to ensure its markup communicates as clearly as its design.
Adding aria-current="page" to the active navigation element is one way to ensure markup and design communicate the same information with or without assistive technologies.
<a aria-current="page" href="/">Home</a>
🎉 Bonus
Use CSS attribute selectors to style the aria-current="page" element to keep the visual and markup cues linked.
[aria-current="page"] { /* Active element styles */ }
Quick Win 2: Document Language
While some people can visit a website and determine the language or locale of its content, not all people have that luxury. Again, markup must communicate the same information as the visual design — even if that information may seem implied.
Add the lang attribute to the <html> tag to communicate not only the document’s language, but its locale. This will help assistive technologies like screen readers understand and communicate the content. Even if the app only supports one language, this can be a nice quality of life improvement for many people.
<html lang="en-US">
For apps which support multiple languages, the <html> element is likely not the only one to need its lang value defined. Use the lang attribute on specific elements whose language differs from the rest of the document, like links within a language toggle menu. In this case, pair the use of lang with the hreflang attribute to not only communicate the language of the link itself, but also of its destination.
Whether drawing attention to actions or updates, or creating a sense of life and charm, adding motion to an app can really elevate its experience. However, some people may find that experience disorienting.
Windows and MacOS both offer a setting at the OS level for people to greatly reduce the amount of motion when using their systems. The prefers-reduced-motion setting can greatly improve the experience on a computer, but it does not extends beyond the UI of the operating system. So wouldn’t it be nice if our apps could respect that same system setting and provide a more static experience for those who prefer it?
Well, with CSS media queries, they can.
The prefers-reduced-motion media query can be used to greatly reduce or remove all motion from an app whenever the system setting is enabled.
The blanket approach shown here prevents all motion, but it can leave little room for nuance. It’d be best to review the needs of those using the product, but consider these other options as well.
One approach could be to only animate one property at a time in prefers-reduced-motion settings. So consider a <Modal /> that fades and scales into view with opacity and transform. In reduced motion environments, only the opacity would transition. The scaling effect would be removed as they are more commonly problematic than fading.
Another option could be to look at the prefers-reduced-motion environment a bit more literally and remove all motion. This would do away with our scaling modals, sliding drawers, and bouncing notifications, but would still leave room for color transitions on links and buttons.
Quick Win 4: Indicate Data Sorting State
A common theme across all of these tips is to ensure that an app’s visual design and markup communicate the same things. So, when the design uses an arrow to indicate the sort direction of a table column, how can that also be communicated in the markup?
Setting the aria-sort attribute to ascending /descending on the header of the actively-sorted column allows the markup to communicate the same content structure as a visual indicator in the UI.
This will help ensure that people using assistive technologies and those who aren’t can understand the content in the same way.
Whether scrolling through an endless stream of tweets or through an impossible-to-decide list of products, the web has fully embraced lazy loading long lists of data (and alliteration, apparently).
This is when the aria-setsize and aria-posinset attributes become very valuable. While a person’s progression through the list can be communicated visually in many different ways, these attributes are used to communicate that same progression to many assistive technologies.
As developers, we likely have access to the length of an entire list as well as the index of the current items being displayed. With that, the aria-setsize attribute would define the total length of the list, while the aria-posinset attribute would define an item’s specific position (or index) within that list.
If the total length of the list is not known, then aria-setsize should be set to -1.
With these attributes, assistive technologies can better interpret a list and a person can better understand their position within it.
Take a listen to how these attributes are announced using MacOS VoiceOver.
🎉 Bonus Win: Axe-DevTools Extension
Implementing those five accessibility quick wins is a great start, but that’s exactly what it is —a start. There’s a sprawling landscape of assistive technologies and sets of abilities a person can posses, and navigating it all alone can feel overwhelming.
Fortunately, there are plenty of tools to help with auditing a product’s accessibility that make the journey much more manageable. My personal favorite — my trusty accessibility compass — is the Axe-DevTools browser extension.
Running the Axe-DevTools accessibility scanner can return tons of valuable information. Not only will it display all issues and warnings found on the page, but it groups them by approximate severity. It can also highlight the element on the page or in the Elements tab and provide links to learn more about the specific issue.
However, most importantly, it will offer clear and concise approaches to fix the specific issue.
Wrapping Up
A product isn’t made accessible overnight; nor is a product’s accessibility work ever really complete. Like anything else on the web, accessibility evolves and requires maintenance. However, even seemingly small additions can have an impact on a product’s accessibility and a person’s overall experience.
After stepping into a new codebase, these are often some of the first few things I look into — some “low-hanging fruit” of accessibility, if you will.
Reaching AAA or even AA conformance can feel like scaling an 8,000 meter peak. These steps won’t carry you to the summit, but an expedition is never completed in a single stride.
Oh, Bootstrap, that old standard web library that either you hate or you spend all your time defending as “it’s fine, it’s not that bad.” Regardless of what side you fall on, it’s a powerful UI framework that’s everywhere, most people know the basics of it, and it gives you extremely predictable results.
For better or worse, Bootstrap is opinionated. It wants you to construct your HTML a certain way, it wants you to override styles a certain way, it wants to be built from core files a certain way, and it wants to be included in websites a certain way. Most of the time, unless you have a coworker who writes Bootstrap badly, this is fine, but it doesn’t cover all use cases.
Bootstrap wants to be generated server-side and it does not like having its styles overridden at runtime. If you’re in a situation where you want some sort of visual theme feature in your application, what Bootstrap wants you to do is generate separate stylesheets for each theme and swap out stylesheets as you need. This is a great way to do it if you have pre-defined themes you’re offering to users. But what if you want user-defined themes? You could set up your app to run Sass and compile new stylesheets and save them to the server, but that’s a lot of work—plus you have to go talk to the back-end guys and DevOps which is a bunch of hassle if you only want to, say, swap out primary and secondary colors, for example.
So this is where I was.
I’m building a multi-user SaaS app using Django and Vue with a fixed layout, but also a requirement to be able to change the branding colors for each user account with an automatic default color theme. There is another requirement that we don’t re-deploy the app every time a new user is added. And, finally, every single back-end and DevOps dev is currently swamped with other projects, so I have to solve this problem on my own.
Since I really don’t want to compile Sass at runtime, I could just create stylesheets and inject them into pages, but this is a bad solution since we’re focusing on colors. Compiled Bootstrap stylesheets render out the color values as explicit hex values, and (I just checked) there are 23 different instances of primary blue in my stylesheet. I would need to override every instance of that just for primary colors, then do it again for secondary, warning, danger, and all the other conventions and color standardizations we want to change. It’s complicated and a lot of work. I don’t want to do that.
Luckily, this new app doesn’t have a requirement to support Internet Explorer 11, so that means I have CSS variables at my disposal. They’re great, too, and they can be defined after loading a stylesheet, flowing in every direction and changing all the colors I want, right? And Bootstrap generates that big list of variables in the :root element, so this should be simple.
This is when I learned that Bootstrap only renders some of its values as variables in the stylesheet, and that this list of variables is intended entirely for end-user consumption. Most of the variables in that list ate not referenced in the rest of the stylesheet, so redefining them does nothing. (However, it’s worth a note that better variable support at runtime may be coming in the future.)
So what I want is my Bootstrap stylesheet to render with CSS variables that I can manipulate on the server side instead of static color values, and strictly speaking, that’s not possible. Sass won’t compile if you set color variables as CSS variables. There are a couple of clever tricks available to make Sass do this (here’s one, and another), but they require branching Bootstrap, and branching away from the upgrade path introduces a bit of brittleness to my app that I’m unwilling to add. And if I’m perfectly honest, the real reason I didn’t implement those solutions was that I couldn’t figure out how to make any of them work with my Sass compiler. But you might have better luck.
This is where I think it’s worth explaining my preferred workflow. I prefer to run Sass locally on my dev machine to build stylesheets and commit the compiled stylesheets to the repo. Best practices would suggest the stylesheets should be compiled during deployment, and that’s correct, but I work for a growing, perpetually understaffed startup. I work with Sass because I like it, but in what is clearly a theme for my job, I don’t have the time, power or spiritual fortitude to integrate my Sass build with our various deployment pipelines.
It’s also a bit of lawful evil self-defense: I don’t want our full-stack developers to get their mitts on my finely-crafted styles and start writing whatever they want; and I’ve discovered that for some reason they have a terrible time getting Node installed on their laptops. Alas! They just are stuck asking me to do it, and that’s exactly how I want things.
All of which is to say: if I can’t get the stylesheets to render with the variables in it, there’s nothing stopping me from injecting the variables into the stylesheet after it’s been compiled.
Behold the power of find and replace!
What we do is go into Bootstrap and find the colors we want to replace, conveniently found at the top of your compiled stylesheet in the :root style:
Grab the value for, say, --bs-primary, the good ol’ Bootstrap blue. I use Gulp to compile my stylesheets, so let’s take a look at the Sass task function for that in the gulpfile.js:
var gulp = require('gulp'); var sass = require('gulp-sass')(require('sass')); var sourcemaps = require('gulp-sourcemaps'); function sassCompile() { return gulp.src('static/sass/project.scss') .pipe(sourcemaps.init()) .pipe(sass({outputStyle: 'expanded'})) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('/static/css/')); } exports.sass = sassCompile;
I want to copy and replace this color throughout my entire stylesheet with a CSS variable, so I installed gulp-replace to do that. We want our find-and-replace to happen at the very end of the process, after the stylesheet is compiled but before it’s saved. That means we ought to put the pipe at the end of the sequence, like so:
var gulp = require('gulp'); var sass = require('gulp-sass')(require('sass')); var sourcemaps = require('gulp-sourcemaps'); var gulpreplace = require('gulp-replace'); function sassCompile() { return gulp.src('static/sass/project.scss') .pipe(sourcemaps.init()) .pipe(sass({outputStyle: 'expanded'})) .pipe(sourcemaps.write('.')) .pipe(gulpreplace(/#002E6D/ig, 'var(--ct-primary)')) .pipe(gulp.dest('static/css/')); } exports.sass = sassCompile;
Cool, OK, we now have an entire stylesheet that wants a variable value for blue. Notice it changed both the primary color and the “blue” color. This isn’t a subtle technique. I call it quick-and-dirty for a reason, but it’s fairly easy to get more fine-grained control of your color replacements if you need them. For instance, if you want to keep “blue” and “primary” as separate values, go into your Sass and redefine the $ blue and $ primary Sass variables into different values, and then you can separately find-and-replace them as needed.
Next, we need to define our new default variable value in the app. It’s as simple as doing this in the HTML head:
Run that and everything shows up. Everything that needs to be blue is blue. Repeat this process a few times, and you suddenly have lots of control over the colors in your Bootstrap stylesheet. These are the variables I’ve chosen to make available to users, along with their default color values:
Now the fun begins! From here, you can directly manipulate these defaults if you like, or add a second :root style below the defaults to override only the colors you want. Or do what I do, and put a text field in the user profile that outputs a :root style into your header overriding whatever you need. Voilà, you can now override Bootstrap at runtime without recompiling the stylesheet or losing your mind.
This isn’t an elegant solution, certainly, but it solves a very specific use case that developers have been trying to solve for years now. And until Bootstrap decides it wants to let us easily override variables at runtime, this has proven to be a very effective solution for me.
localStorage can be an incredibly useful tool in creating experiences for applications, extensions, documentation, and a variety of use cases. I’ve personally used it in each! In cases where you’re storing something small for the user that doesn’t need to be kept permanently, localStorage is our friend. Let’s pair localStorage with Vue, which I personally find to be a great, and easy-to-read developer experience.
Simplified example
I recently taught a Frontend Masters course where we built an application from start to finish with Nuxt. I was looking for a way that we might be able to break down the way we were building it into smaller sections and check them off as we go, as we had a lot to cover. localStorage was a gsolition, as everyone was really tracking their own progress personally, and I didn’t necessarily need to store all of that information in something like AWS or Azure.
Here’s the final thing we’re building, which is a simple todo list:
Storing the data
We start by establishing the data we need for all the elements we might want to check, as well as an empty array for anything that will be checked by the user.
We’ll also output it to the page in the template tag:
<div id="app"> <fieldset> <legend> What we're building </legend> <div v-for="todo in todos" :key="todo"> <input type="checkbox" name="todo" :id="todo" :value="todo" v-model="checked" /> <label :for="todo">{{ todo }}</label> </div> </fieldset> </div>
Mounting and watching
Currently, we’re responding to the changes in the UI, but we’re not yet storing them anywhere. In order to store them, we need to tell localStorage, “hey, we’re interested in working with you.” Then we also need to hook into Vue’s reactivity to update those changes. Once the component is mounted, we’ll use the mounted hook to select checked items in the todo list then parse them into JSON so we can store the data in localStorage:
That’s actually all we need for this example. This just shows one small possible use case, but you can imagine how we could use localStorage for so many performant and personal experiences on the web!
Sarah Higley has some CSS tricks up her sleeve for dealing with High Contrast Mode on Windows, which I learned is referred to as WHCM.
Here’s the first trick:
[…] if the default CSS outline property doesn’t give you the visual effect you want [in WHCM] for focus states, there’s a very simple fix. Instead of overriding default browser focus styles with outline: none, make it transparent instead: outline 3px solid transparent.
That will essentially do nothing outside of WHCM, but in WHCM, it will be a thick white border, which is a strong, good visual focus style.
You should for sure be setting far-out cache headers on your assets like CSS and JavaScript (and images and fonts and whatever else). That tells the browser “hang on to this file basically forever.” That way, when navigating from page to page on a site — or revisiting it, or refreshing the page — the browser doesn’t have to download it again which produces way faster page loads. It’s massively important for web performance, so do it!
But how do you force the browser to get a fresh version of the file? Well, there are a bunch of ways. Check out that blog post for more. But here’s one that I’ve used just recently that I wanted to document.
The trick is to change the query string
There was an era where the prevailing wisdom was that changing the query string wasn’t enough, but even then it, the reasons it wouldn’t work were pretty much edge cases. These days, changing the query string is fine (assuming you don’t change the default behavior, services like Cloudflare let you do it).
So, one day you ship it like this in your HTML:
<link rel="stylesheet" href="style.css?v=1" />
Then you change that query string to break the cache when you need to:
<link rel="stylesheet" href="style.css?v=2" />
The HTML, by the way, is either not cached or cached for a much shorter amount of time, so changes to HTML will be seen.
I sometimes do it by hand
For many years, I busted cache on this very site by setting a PHP variable and using it to break assets, like…
Hey that works, but it was me hand-manipulating that variable. I would sometimes forget to do it, and even if I did remember, I sort of resented having to do it.
Automating version busting with Gulp
I’m using a Gulp-powered build process at the moment on this site, which does all the classic stuff: Sass, Babel, file concatenation, live reloading…
It occurred to me I might as well have Gulp do the query-string changing whenever changes are made to CSS or JavaScript. JavaScript has a .replace() method, and that’s available in Node/Gulp easily with the gulp-replace plugin.
I make a task. When I call it, it looks in my header filer for the string cache_bust= plus some value, and replaces it with a new randomized string based on the date and time.
I do the same thing in a separate task when JavaScript files are editing and compiled.
It’s still a little dumb
Notice that I’m changing the query string on all the CSS assets every time any of them changes. That’s not as efficient as it could be. I should probably be changing the query string only on the changed files.
I’ll get to it eventually. Sometimes you just gotta baby-step your way to better solutions over time.
This is just one way! There are other Gulp plugins just for this. Other build systems have different approaches. This approached happened to work well for me and my exact needs at the time. Feel free to share your strategy!
We’ve been talking a lot about Dark Mode around here ever since Apple released it as a system setting in MacOS 10.14 and subsequently as part of Safari. It’s interesting because of both what it opens up as as far as design opportunities as well as tailoring user experience based on actual user preferences.
This week, we got an Editor’s Draft for the Color Adjust Module Level 1 specification and the First Public Working Draft of it. All of this is a work-in-progress, but the progression of it has been interesting to track. The spec introduces three new CSS properties that help inform how much control the user agent should have when determining the visual appearance of a rendered page based on user preferences.
color-scheme is the first property defined in the spec and perhaps the centerpiece of it. It accepts light and dark values which — as you may have guessed — correspond to Light Mode and Dark Mode preferences for operating systems that support them. And, for what it’s worth, we could be dealing with labels other than “Light” and “Dark” (e.g. “Day” and “Night”) but what we’re dealing with boils down to a light color scheme versus a dark one.
This single property carries some important implications. For one, the idea is that it allows us to set styles based on a user’s system preferences which gives us fine-grained control over that experience.
Another possible implication is that declaring the property at all enables the user agent to take some responsibility for determining an element’s colors, where declaring light or dark informs the user agent that an element is “aware” of color schemes and should be styled according to a preference setting matching the value. On the other hand, we can give the browser full control to determine what color scheme to use based on the user’s system preferences by using the auto value. That tells the browser that an element is “unaware” of color schemes and that the browser can determine how to proceed using the user preferences and a systems’s default styling as a guide.
It’s worth noting at this point that we may also have a prefers-color-scheme media feature (currently in the Editor’s Draft for the Media Queries Level 5 specification) that also serves to let us detect a user’s preference and help gives us greater control of the user experience based on system preferences. Robin has a nice overview of it. The Color Adjust Module Level 1 Working Draft also makes mention of possibly using a color scheme value in a <meta> element to indicate color scheme support.
There’s more to the property, of course, including an only keyword, chaining values to indicate an order of preference, and even an open-ended custom ident keyword. So definitely dig in there because there’s a lot to take in.
Pretty interesting, right? Hopefully you’re starting to see how this draft could open up new possibilities and even impacts how we make design decisions. And that’s only the start because there are two more properties!
forced-color-adjust: This is used when we want to support color schemes but override the user agent’s default stylesheet with our own CSS. This includes a note about possibly merging this into color-adjust.
color-adjust: Unlike forcing CSS overrides onto the user agent, this property provides a hint to browsers that they can change color values based on the both the user’s preferences and other factors, such as screen quality, bandwidth, or whatever is “deem[ed] necessary and prudent for the output device.” Eric Bailey wrote up the possibilities this property could open up as far as use cases, enhanced accessibility, and general implementations.
The current draft is sure to expand but, hey, this is where we get to be aware of the awesome work that W3C authors are doing, gain context for the challenges they face, and even contribute to the work. (See Rachel Andrew’s advice on making contributions.)
Let’s go rapid fire and try to answer this question with quick points rather than long explanations. There are a lot of similarities between flexbox and grid, starting with the fact that they are used for layout and much more powerful than any layout technique that came before them. They can stretch and shrink, they can center things, they can re-order things, they can align things… There are plenty of layout situations in which you could use either one to do what we need to do, and plenty of situations where one is more well-suited than the other. Let’s focus on the differences rather than the similarities:
Flexbox can optionallywrap. If we allow a flex container to wrap, they will wrap down onto another row when the flex items fill a row. Where they line up on the next row is independent of what happenned on the first row, allowing for a masonry-like look.
Grid can also optionally wrap (if we allow auto filling) in the sense that items can fill a row and move to the new row (or auto place themselves), but as they do, they will fall along the same grid lines all the other elements do.
Flexbox on top, Grid on bottom
You could think of flexbox as “one dimensional.” While flexbox can make rows and columns in the sense that it allows elements to wrap, there’s no way to declaratively control where elements end up since the elements merely push along a single axis and then wrap or not wrap accordingly. They do as they do, if you will, along a one-dimensional plane and it’s because of that single dimension that we can optionally do things, like align elements along a baseline — which is something grid is unable to do.
.parent { display: flex; flex-flow: row wrap; /* OK elements, go as far as you can on one line, then wrap as you see fit */ }
You could think of grid as “two dimensional“ in that we can (if we want to) declare the sizing of rows and columns and then explicitly place things into both rows and columns as we choose.
.parent { display: grid; grid-template-columns: 3fr 1fr; /* Two columns, one three times as wide as the other */ grid-template-rows: 200px auto 100px; /* Three columns, two with explicit widths */ grid-template-areas: "header header header" "main . sidebar" "footer footer footer"; } /* Now, we can explicitly place items in the defined rows and columns. */ .child-1 { grid-area: header; } .child-2 { grid-area: main; } .child-3 { grid-area: sidebar; } .child-4 { grid-area: footer; }
Flexbox on top, Grid on bottom
I’m not the world’s biggest fan of the “1D” vs. “2D” differentiation of grid vs. flexbox, only because I find most of my day-to-day usage of grid is “1D” and it’s great for that. I wouldn’t want someone to think they have to use flexbox and not grid because grid is only when you need 2D. It is a strong distinction though that 2D layout is possible with grid though in ways it is not in flexbox.
Grid is mostly defined on the parent element. In flexbox, most of the layout (beyond the very basics) happen on the children.
/* The flex children do most of the work */ .flexbox { display: flex; > div { &:nth-child(1) { // logo flex: 0 0 100px; } &:nth-child(2) { // search flex: 1; max-width: 500px; } &:nth-child(3) { // avatar flex: 0 0 50px; margin-left: auto; } } } /* The grid parent does most of the work */ .grid { display: grid; grid-template-columns: 1fr auto minmax(100px, 1fr) 1fr; grid-template-rows: 100px repeat(3, auto) 100px; grid-gap: 10px; }
Grid is better at overlapping. Getting elements to overlap in flexbox requires looking at traditional stuff, like negative margins, transforms, or absolute positioning in order to break out of the flex behavior. With grid, we can place items on overlapping grid lines, or even right within the same exact grid cells.
Flexbox on top, Grid on bottom
Grid is sturdier. While the flexing of flexbox is sometimes it’s strength, the way a flex item is sized gets rather complicated. It’s a combination of width, min-width, max-width, flex-basis, flex-grow, and flex-shrink, not to mention the content inside and things like white-space, as well as the other items in the same row. Grid has interesting space-occupying features, like fractional units, and the ability for content to break grids, though, generally speaking, we’re setting up grid lines and placing items within them that plop right into place.
Flexbox can push things away. It’s a rather unique feature of flexbox that you can, for example, put margin-right: auto; on an element and, if there is room, that element will push everything else as far away as it can go can.
Here are some of my favorite tweets on the subject:
flexbox looks like it does what you want but grid is usually what you want
Grid makes actual columns and rows. Content will line up from one to the other, as you ask it to. Flexbox doesn’t. Not only in the second dimension (which is easiest to talk about), but also in the first dimension. Flexbox isn’t for most of the things we’ve been using it for.
How about this:#Flexbox is for alignment. #CSSGrid is for layout.
This is almost always how I wind up using them. It allows them to preserve their relationships to one another. It also allows each to be used for its strength, even though each can do the other thing.
For me Grid is there to great full layout..grids…with more control over how the whole are/page comes together, whereas flexbox helps me to position and align (whether it’s in a grid or not).
The distinction between the two is often blurry, especially now that we also have `gap` for flexbox. Grid is best suited for a few specific use cases (2D obviously, but also things like overlapping elements) while flexbox usually shines in simpler yet common layout requirements.
Here’s a possibility! Perhaps you are testing your JavaScript with a framework like Jasmine. That’s nice because you can write lots of tests to cover your application, get a nice little UI to see the output, and even integrate it with build and deploy tools to make your ongoing development work safer.
Now, perhaps there is this zany developer on your team who keeps changing API endpoints on you — quite literally breaking things in the process. You decide to write a test that hits those endpoints and makes sure you’re getting back from it what you expect. Straightforward enough. The only slightly tricky part is that API requests are async. To really test it, the test needs to have some way to wait for the results before testing the expectations.
That can be handled in Jasmine through a beforeEach(), which can wait to complete until you call a done() function. Here’s the whole thing:
I’ve been auditing a ton of CSS lately and thought it would be neat to jot down how I’m going about doing that. I’m sure there are a million different ways to do this depending on the size and scale of your app and how your CSS works under the hood, so please take all this with a grain of salt.
First a few disclaimers: at Gusto, the company I work for today, our engineers and designers all write in Sass and use webpack to compile those files into CSS. Our production environment minifies all that code into a single CSS file. However, our CSS is made up of three separate domains. so I downloaded them all to my desktop because I wanted to test them individually.
Here’s are those files and what they do:
manifest.css: a file that’s generated from all our Sass functions, mixins and contains all of our default HTML styles and utility classes.
components.css: a file that consists of our React components such as Button.scss, Card.scss, etc. This and manifest.css both come from our Component Library repo and are imported into our main app.
app.css: a collection of styles that override our components and manifest. Today, it exists in our main application repo.
After I downloaded everything, I threw them into an S3 bucket and ran them through CSS Stats. (I couldn’t find a command line tool that I liked, so I decided stuck with this tool.) The coolest thing about CSS Stats is that it provides a ton of clarity about the health and quality of a site’s CSS, and in turn, a design system. It does this by showing the number of unique font-size and unique background-color CSS declarations there are, as well as a specificity graph for that particular CSS file.
I wanted to better understand our manifest.css file first. As I mentioned, this file contains all our utility classes (such as padding-top-10px and c-salt-500) as well as our normalize and reset CSS files, so it’s pretty foundational for everything else. I started digging through the results:
There are some obvious issues here, like the fact that there are 101 unique colors and 115 unique background colors. Why is this a big deal? Well, it’s a little striking to me because our team had already made a collection of Sass functions to output a very specific number of colors. In our Figma UI Kit and variables_color.scss (which gets compiled into our manifest file, we declare a total of 68 unique colors:
So, where are all these extra colors coming from? Well, I assume that they’re coming from Bootstrap. Back when we started building the application, we hastily built on top of Bootstrap’s styles without refactoring things as we went. There was a certain moment when this started to hurt as we found visual inconsistencies across our application and hundreds of lines of code being written that simply overrode Bootstrap. In a rather gallant CSS refactor, I removed Bootstrap’s CSS from our main application and archived it inside manifest.css, waiting for the day when we could return to it and refactor it all.
These extra colors are likely come from that old Bootstrap file, but it’s probably worth investigating some more. Anyway, the real issue with this for me is that my understanding of the design system is different from what’s in the front-end. That’s a big problem! If my understanding of the design system is different from how the CSS works, then there’s enormous potential for engineers and designers to pick up on the wrong patterns and for confusion to disseminate across our organization. Think about the extra bloat and lack of maintainability, not to mention other implications.
I was reading Who Are Design Systems For? by Matthew Ström and perked up when he quotes a talk by Julie Ann-Horvath where she’s noted as saying, “a design system doesn’t exist until it’s in production.” Following the logic, it’s clear the design system I thought we had didn’t actually exist.
Going back to manifest.css though: the specificity graph for this file should be perfectly gradual and yet there are some clear spikes that show there’s probably a bit more CSS that needs to be refactored in there:
Anyway, next up is our components.css. Remember that’s the file that our styles for our components come from so I thought beforehand that it’s bound to be a little messier than our manifest file. Throwing it into CSS Stats returns the following:
CSS-Stats shows some of the same problems — like too many font sizes (what the heck is going on with that giant font size anyway?) — but there are also way too many custom colors and background-colors. I already had a hunch about what the biggest issue with this CSS file was before I started and I don’t think the problem is not shown in this data here at all.
Let me explain.
A large number of our components used to be Bootstrap files of one kind or another. Take our Accordion.jsx React component, for instance. That imports an accordion.css file which is then compiled with all the other component’s CSS into a components.css file. The problem with this is that some Accordion styles affect a lot more than just that component. CSS from this this file bleeds into other patterns and classes that aren’t tied to just one component. It’s sort of like a poison in our system and that impacts our team because it makes it difficult to reliably make changes to a single component. It also leads to a very fragile codebase.
So I guess what I’m saying here is that tools like CSS Stats are wondrous things to help us check core vital signs for CSS health, but I don’t think they’ll ever really capture the full picture.
Anyway, next up is the app.css file:
This is the “monolith” — the codebase that our design systems team is currently trying to better understand and hopefully refactor into a series of flexible and maintainable React components that others can reuse again and again.
What worries me about this codebase is the specificity of it all what happens when something changes in the manifest.css or in our components.css? Will those styles be overridden in the monolith? What will happen to the nice and tidy component styles that we import into a new project?
Subsequently, I don’t know where I stole this, but I’ve been saying it an awful lot lately — you should always be able to predict what your CSS is going to do, whether that’s a single line of code or a giant codebase of intermingled styles. That’s what design systems are all about — designing and building predictable interfaces for the future. And if our compiled CSS has all these unpredictable and unknowable parts to it, then we need to gather everyone together to fix it.
Anywho, I threw some of the data into a Dropbox Paper doc after all this to make sure we start tackling these issues and see gradual improvements over time. That looks something like this today:
How have you gone about auditing your CSS? Does your team code review CSS? Are there any tricks and tips you’d recommend? Leave a comment below!