Journal
Theming to optimise for user colour scheme preference
“Dark mode” has been a buzz-phrase in web development since around 2019. It refers to the ability provided by modern operating systems to set the user interface’s appearance to either light or dark. Web browsers and technologies support this by allowing developers to detect whether or not the OS provides such settings, and if so which mode the user prefers. Developers can create alternate light and dark themes for their websites and switch between these intelligently (responsively?) to fit with the user’s system preference.
I’ve been meaning to do some work on this front for a while and finally got around to it. (You might even be reading this post with your computer’s dark colour scheme enabled and seeing the fruits of my labour.) Here’s how I set things up and the lessons I learned along the way.
Switching your computer’s appearance
On a Mac:
System Preferences > General > Appearance
On Windows:
Colors > Choose your color
On iOS:
Settings > Display & Brightness > Appearance
On Android:
Settings > Dark Theme
Emulating this in browser DevTools
In Chromium-based browsers you can open DevTools, then open the Rendering tool (which might be hidden behind More Tools). In the Emulate CSS media feature prefers-color-scheme dropdown, select prefers-color-scheme: dark or prefers-color-scheme: light.
The theme-color HTML meta tag
You can use the theme-color HTML meta tag to indicate to the browser a colour it can use to customise the display of the page and surrounding elements such as the address bar. The reason we have a meta tag solution to this rather than leaving it to CSS is performance; it allows the browser to make the relevant updates immediately.
In practice you might specify your “highlight colour”—Clearleft currently use their signature green. Alternatively you could specify both your light theme and dark theme background colours, using the media attribute to associate each with the user colour scheme preference it is optimised for. This is the approach I chose.
<!-- Set theme color to white when user prefers light mode -->
<meta name="theme-color" content="rgb(255,255,255)" media="(prefers-color-scheme: light)">
<!-- Set theme color to a custom black when user prefers dark mode -->
<meta name="theme-color" content="rgb(38,38,38)" media="(prefers-color-scheme: dark)">
The theme-color meta tag has mixed support. It works well in Safari and iOS Safari. Meanwhile Chrome only applies the colour on installed progressive web apps (PWAs).
This had me considering PWA matters again for the first time in a while. I previously definined a PWA “theme colour” for my website in my manifest.manifest file. However unlike in the theme-color meta tag approach, the manifest allows defining one theme colour only. While I don’t think there’d be any issue in having both the meta tag and manifest value in play simultaneously (my understanding is that the HTML would take priority over the manifest), I decided to keep things simple and removed theme_color from my manifest file for now.
Note: the w3c manifest spec may support multiple theme colours in future.
The color-scheme meta tag
The color-scheme meta tag is used to specify one or more color schemes/modes with which the page is compatible. Unlike theme-color you don’t provide an arbitrary colour value, but rather one of light or dark or both in combination. This tells the browser whether it can change default background, foreground and text colours to match the current system appearance. As Jim Nielsen notes in Don’t forget the color-scheme property, it gives the browser permission to automatically change standard form controls, scroll bars, and other named system colors.
I include this meta tag on each page (just after my theme-color meta tags). I indicate that my styles have been prepped to handle the browser applying either light or dark colour schemes and—via the order of values in the content attribute—that I prefer light.
<meta name="color-scheme" content="light dark">
Notes:
- I’ve also seen
<meta name="supported-color-schemes">however Thomas Steiner explained that this is an old syntax and has since been standardised as<meta name="color-scheme">; - There is also a
color-schemeCSS property, which I’ll come to later. Once again though, having an HTML meta tag helps browsers to apply things faster; - although I’m currently intimating that I prefer light, in future I might update this to dark first (assuming I don’t hear of any reasons against it). The dark colour palette is really growing on me!
Ensuring colours are accessible
While working on a new dark colour palette and tweaking my light palette I made sure to check that colour contrast met accessibility requirements.
Useful tools on this front include:
- Activating Chrome’s element inspector (the “box with arrow at bottom-right” icon) enables hovering an element to check the contrast of text against background;
- Erik Kennedy’s Accessible Color Generator provides slightly modified colour alternatives when contrast is insufficient—really handy and I used this a couple of times;
- Are my colors accessible? is another great “check contrast, digest results, then tweak” tool which provides more detailed information than the Chrome inspector, especially regarding the impact of small text on contrast.
Design Tokens
It quickly became clear that I’d need global colour settings that I could use across multiple technologies and multiple files. For example, I’d want to reuse the same custom background colour value in an HTML theme-color meta tag, in CSS, and perhaps also in a JSON-based manifest file.
I’d previously bookmarked Heydon Pickering’s article on design tokens in Eleventy and now was the time to give it a spin.
I created _data/tokens.json in which I defined raw tokens such as colorDark, colorDarker and colorLight.
Then in my Nunjucks-based HTML templates I could access that token data to define meta tag values using {{ tokens.colorLight }}. These values would be interpolated to the real CSS colour values during build.
I also created a new Nunjucks file, theme_css.njk, adding permalink: "css/theme.css" in its frontmatter so that it’d generate a CSS file. This file maps my design tokens to custom properties set on the root element, something like this:
:root {
--color-dark: {{ tokens.colorDark }};
--color-darker: {{ tokens.colorDarker }};
--color-light: {{ tokens.colorLight }};
--color-grey: {{ tokens.colorGrey }};
--color-highlight: {{ tokens.colorHighlight }};
--color-highlight-dark: {{ tokens.colorHighlightDark }};
--color-highlight-darker: {{ tokens.colorHighlightDarker }};
--color-text: {{ tokens.colorText }};
}
Custom properties FTW
Using custom properties allowed me to do the hard work of preparing my light and dark themes upfront and removed the need to constantly duplicate values and write code forks throughout my CSS.
Having previously created a base layer of reusable custom property representations of my design tokens, I began creating more specific custom properties to serve different contexts.
:root {
--color-page-bg-light: var(--color-light);
--color-page-bg-dark: var(--color-dark);
--color-ink-dark: var(--color-text);
--color-ink-light: var(--color-light);
}
CSS for default and dark-mode contexts
Now to set my light-mode defaults and fork these when the user prefers dark-mode.
Here’s my CSS:
:root {
color-scheme: light;
--color-highlight: var(--color-highlight-darker);
--color-page-bg: var(--color-page-bg-light);
--color-band: var(--color-darker);
--color-ink: var(--color-ink-dark);
--color-ink-offset: var(--color-grey);
@media (prefers-color-scheme: dark) {
color-scheme: dark;
--color-highlight: var(--color-highlight-dark);
--color-page-bg: var(--color-page-bg-dark);
--color-band: var(--color-darker);
--color-ink: var(--color-ink-light);
--color-ink-offset: var(--color-grey);
}
accent-color: var(--color-highlight);
}
body {
background-color: var(--color-page-bg);
color: var(--color-ink);
}
Let’s break that down.
The color-scheme property allows us to specify one or more colour schemes/modes with which an element is compatible. I’m defining it on the root element i.e. for the whole page. My approach was to start by specifying support for a light colour scheme only, then define background and text colour custom properties with values optimised for light colour scheme.
Using @media (prefers-color-scheme: dark) {…} we can check the user’s colour scheme preference. When the user prefers dark, I change color-scheme to dark to let the browser do its “set sensible defaults” thing, and I update the custom property values to those optimised to blend with a dark appearance.
The reusable, theme-sensitive custom properties I’ve plumped for are:
- highlight colour: really just my brand colour, although lightened a touch in dark mode for better contrast;
- page background colour;
- “band” background colour: something I’d use as a band of alternative background colour to distinguish special sections like the header or footer;
- ink colour: my default text colour;
- “offset” ink colour: a grey for little bits of meta text like dates and categories.
I’m sure I’ll evolve these over time.
Lastly, the accent-colour CSS property is used by some form controls e.g. radios and checkboxes. I set that to my highlight colour with the intention of creating a more branded experience.
SVG considerations
My SVG-based logo didn’t look right in dark mode, however the fix was pretty simple. I applied BEM-style element classes to any problematic path elements of the SVG then wrote CSS for those. The CSS tests for prefers-color-scheme: dark and updates the path’s fill or stroke colour as necessary.
What about leaving everything to the browser?
Having read Jim Nielsen’s brilliant article on CSS System Colors I tried feeding the browser color-scheme: light dark, removing my arbitrary dark theme colours, and leaving the colour palette entirely up to the browser. Along the way I learned some interesting lessons on system colours—which we can also choose to use as valid CSS colours in our custom components—such as Canvas and CanvasText.
I love this idea in principle! However in practice:
- only Safari’s colour palette looks consistent with the rest of the Mac OS. The palette applied by Chrome doesn’t; it has its own, different “black”. So for the control you’ve given away, you don’t necessarily achieve that nice “consistency with the OS” reward in return;
- as far as I know you can’t lighten or darken the system colours so it’s hard to create a multi-tone, complimentary palette, and also be confident in achieving sufficient colour contrast. That’s fine if you only need a single background colour for the entire page with no alternate bands of colour (like on Jim’s blog) however I think that’s a little restrictive. (Maybe I’m getting too hung up on control here though, and introducing additional arbitrary colours would work fine alongside a variable “system black”);
- Firefox and iOS Safari are a little problematic in their support for the technologies involved, leading to writing a few hacky workarounds.
For the combined reasons above I’m sticking with manually defined colours for now (however I have a pull request I can revisit later if the mood takes).
What could I do better?
With extra time, I might:
- think more deeply about dark theme design, perhaps following some of the tips regarding shadows, depth and colour contrast in CSS Tricks’ A complete guide to dark mode on the web;
- consider providing a theme switcher control to allow the user to choose the website theme they want regardless of the current OS appearance. (But I probably won’t; I’m not sure it’s necessary)
References
- Theming, from Jeremy Keith’s excellent Learn Responsive Design
- Design Tokens in Eleventy by Heydon Pickering
- CSS System Colors by Jim Nielsen
Building a toast component (by Adam Argyle)
Great tutorial (with accompanying video) from Adam Argyle which starts with a useful definition of what a Toast is and is not:
Toasts are non-interactive, passive, and asynchronous short messages for users. Generally they are used as an interface feedback pattern for informing the user about the results of an action. Toasts are unlike notifications, alerts and prompts because they're not interactive; they're not meant to be dismissed or persist. Notifications are for more important information, synchronous messaging that requires interaction, or system level messages (as opposed to page level). Toasts are more passive than other notice strategies.
There are some important distinctions between toasts and notifications in that definition: toasts are for less important information and are non-interactive. I remember in a previous work planning exercise regarding a toast component a few of us got temporarily bogged down in working out the best JavaScript templating solution for SVG icon-based “Dismiss” buttons… however we were probably barking up the wrong tree with the idea that toasts should be manually dismissable.
There are lots of interesting ideas and considerations in Adam’s tutorial, such as:
- using the
<output>element for each toast - some crafty use of CSS Grid and logical properties for layout
- combining
hsland percentages in custom properties to proportionately modify rather than redefine colours for dark mode - animation using
keyframesandanimation - native JavaScript modules
- inserting an element before the
<body>element (TIL that this is a viable option)
Thanks for this, Adam!
(via Adam’s tweet)
There’s some nice code in here but the demo page minifies and obfuscates everything. However the toast component source is available on GitHub.
Related links
- A toast to accessible toasts by Scott O’Hara
Web animation tips
Warning: this entry is a work-in-progress and incomplete. That said, it's still a useful reference to me which is why I've published it. I’ll flesh it out soon!
There are lots of different strands of web development. You try your best to be good at all of them, but there’s only so much time in the day! Animation is an area where I know a little but would love to know more, and from a practical perspective I’d certainly benefit from having some road-ready solutions to common challenges. As ever I want to favour web standards over libraries where possible, and take an approach that’s lean, accessible, progressively-enhanced and performance-optimised.
Here’s my attempt to break down web animation into bite-sized chunks for ocassional users like myself.
Defining animation
Animation lets us make something visually move between different states over a given period of time.
Benefits of animation
Animation is a good way of providing visual feedback, teaching users how to use a part of the interface, or adding life to a website and making it feel more “real”.
Simple animation with transition properties
CSS transition is great for simple animations triggered by an event.
We start by defining two different states for an element—for example opacity:1 and opacity:0—and then transition between those states.
The first state would be in the element’s default styles (either as explicitly-defined property values or existing implicitly based on property defaults) and the other in either its :hover or :focus styles or in a class applied by JavaScript following an event.
Without the transition the state change would still happen but would be instantaneous.
You’re not limited to only one property being animated and might, for example, transition between different opacity and transform states simultaneously.
Here’s an example “rise on hover” effect, adapted from Stephanie Eckles’s Smol CSS.
<div class="u-animate u-animate--rise">
<span>rise</span>
</div>
.u-animate > * {
--transition-property: transform;
--transition-duration: 180ms;
transition: var(--transition-property) var(--transition-duration) ease-in-out;
}
.u-animate--rise:hover > * {
transform: translateY(-25%);
}
Note that:
- using custom properties makes it really easy to transition a different property than
transformwithout writing repetitious CSS. - we have a parent and child (
<div>and<span>respectively in this example) allowing us to avoid the accidental flicker which can occur when the mouse is close to an animatable element’s border by having the child be the effect which animates when the trigger (the parent) is hovered.
Complex animations with animation properties
If an element needs to animate automatically (perhaps on page load or when added to the DOM), or is more complex than a simple A to B state change, then a CSS animation may be more appropriate than transition. Using this approach, animations can:
- run automatically (you don’t need an event to trigger a state change)
- go from an initial state through multiple intermediate steps to a final state rather than just from state A to state B
- run forwards, in reverse, or alternate directions
- loop infinitely
The required approach is:
- use
@keyframesto define a reusable “template” set of animation states (or frames); then - apply
animationproperties to an element we want to animate, including one or more@keyframesto be used.
Here’s how you do it:
@keyframes flash {
0% { opacity: 0; }
20% { opacity: 1; }
80% { opacity: 0; }
100% { opacity: 1; }
}
.animate-me {
animation: flash 5s infinite;
}
Note that you can also opt to include just one state in your @keyframes rule, usually the initial state (written as either from or 0%) or final state (written as either to or 100%). You’d tend to do that for a two-state animation where the other “state” is in the element’s default styles, and you’d either be starting from the default styles (if your single @keyframes state is to) or finishing on them (if your single @keyframes state is from).
Should I use transition or animation?
As far as I can tell there’s no major performance benefit of one over the other, so that’s not an issue.
When the animation will be triggered by pseudo-class-based events like :hover or :focus and is simple i.e. based on just two states, transition feels like the right choice.
Beyond that, the choice gets a bit less binary and seems to come down to developer preference. But here are a couple of notes that might help in making a decision.
For elements that need to “animate in” on page load such as an alert, or when newly added to the DOM such as items in a to-do list, an animation with keyframes feels the better choice. This is because transition requires the presence of two CSS rules, leading to dedicated JavaScript to grab the element and apply a class, whereas animation requires only one and can move between initial and final states automatically including inserting a delay before starting.
For animations that involve many frames; control over the number of iterations; or looping… use @keyframes and animation.
For utility classes and classes that get added by JS to existing, visible elements following an event, either approach could be used. Arguably transition is the slightly simpler and more elegant CSS to write if it covers your needs. Then again, you might want to reuse the animations applied by those classes for both existing, visible elements and new, animated-in elements, in which case you might feel that instead using @keyframes and animation covers more situations.
Performance
A smooth animation should run at 60fps (frames per second). Animations that are too computationally expensive result in frames being dropped, i.e. a reduced fps rate, making the animation appear janky.
Cheap and slick properties
The CSS properties transform and opacity are very cheap to animate. Also, browsers often optimise these types of animation using hardware acceleration. To hint to the browser that it should optimise an animation property (and to ensure it is handled by the GPU rather than passed from CPU to GPU causing a noticeable glitch) we should use the CSS will-change property.
.my-element {
will-change: transform;
}
Expensive properties
CSS properties which affect layout such as height are very expensive to animate. Animating height causes a chain reaction where sibling elements have to move too. Use transform over layout-affecting properties such as width or left if you can.
Some other CSS properties are less expensive but still not ideal, for example background-color. It doesn't affect layout but requires a repaint per frame.
Test your animations on a popular low-end device.
Timing functions
- linear goes at the same rate from start to finish. It’s not like most motion in the real world.
- ease-out starts fast then gets really slow. Good for things that come in from off-screen, like a modal dialogue.
- ease-in starts slow then gets really fast. Good for moving somethng off-screen.
- ease-in-out is the combination of the previous two. It‘s symmetrical, having an equal amount of acceleration and deceleration. Good for things that happen in a loop such as element fading in and out.
- ease is the default value and features a brief ramp-up, then a lot of deceleration. It’s a good option for most general case motion that doesn’t enter or exit the viewport.
Practical examples
You can find lots of animation inspiration in libraries such as animate.css (and be sure to check animate.css on github where you can search their source for specific @keyframe animation styles).
But here are a few specific examples of animations I or teams I’ve worked on have had to implement.
Skip to content
The anchor’s State A sees its position fixed—i.e. positioned relative to the viewport—but then moved out of sight above it via transform: translateY(-10em). However its :focus styles define a State B where the intial translate has been undone so that the link is visible (transform: translateY(0em)). If we transition the transform property then we can animate the change of state over a chosen duration, and with our preferred timing function for the acceleration curve.
HTML:
<div class="u-visually-hidden-until-focused">
<a
href="#skip-link-target"
class="u-visually-hidden-until-focused__item"
>Skip to main content</a>
</div>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/">News</a></li>
<li><a href="/">About</a></li>
<!-- …lots more nav links… -->
<li><a href="/">Contact</a></li>
</ul>
</nav>
<main id="skip-link-target">
<h1>This is the Main content</h1>
<p>Lorem ipsum <a href="/news/">dolor sit amet</a> consectetur adipisicing elit.</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</main>
CSS:
.u-visually-hidden-until-focused {
left: -100vw;
position: absolute;
&__item {
position: fixed;
top: 0;
left: 0;
transform: translateY(-10em);
transition: transform 0.2s ease-in-out;
&:focus {
transform: translateY(0em);
}
}
}
To see this in action, visit my pen Hiding: visually hidden until focused and press the tab key.
Animating in an existing element
For this requirement we want an element to animate from invisible to visible on page load. I can imagine doing this with an image or an alert, for example. This is pretty straightforward with CSS only using @keyframes, opacity and animation.
Check out my fade in and out on page load with CSS codepen.
Animating in a newly added element
Stephanie Eckles shared a great CSS-only solution for animating in a newly added element which handily includes a Codepen demo. She mentions “CSS-only” because it’s common for developers to achieve the fancy animation via transition but that means needing to “make a fake event” via a JavaScript setTimeout() so that you can transition from the newly-added, invisible and class-free element state to adding a CSS class (perhaps called show) that contains the opacity:1, fancy transforms and a transition. However Stephanie’s alternative approach combines i) hiding the element in its default styles; with ii) an automatically-running animation that includes the necessary delay and also finishes in the keyframe’s single 100% state… to get the same effect minus the JavaScript.
Avoiding reliance on JS and finding a solution lower down the stack is always good.
HTML:
<button>Add List Item</button>
<ul>
<li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Nostrum facilis perspiciatis dignissimos, et dolores pariatur.</li>
</ul>
CSS:
li {
animation: show 600ms 100ms cubic-bezier(0.38, 0.97, 0.56, 0.76) forwards;
// Prestate
opacity: 0;
// remove transform for just a fade-in
transform: rotateX(-90deg);
transform-origin: top center;
}
@keyframes show {
100% {
opacity: 1;
transform: none;
}
}
Jhey Tompkins shared another CSS-only technique for adding elements to the DOM with snazzy entrance animations. He also uses just a single @keyframes state but in his case the from state which he uses to set the element’s initial opacity:0, then in his animation he uses an animation-fill-mode of both (rather than forwards as Stephanie used).
I can’t profess to fully understand both however if you change Jhey’s example to use forwards instead, then the element being animated in will temporarily appear before the animation starts (which ain’t good) rather than being initially invisible. Changing it to backwards gets us back on track, so I guess the necessary value relates to whether you’re going for from/0% or to/100%… and both just covers you for both cases. I’d probably try to use the appropriate one rather than both just in case there’s a performance implication.
Animated disclosure
Here’s an interesting conundrum.
For disclosure (i.e. collapse and expand) widgets, I tend to either use the native HTML <details> element if possible or else a simple, accessible DIY disclosure in which activating a trigger toggles a nearby content element’s visibility. In both cases, there’s no animation; the change from hidden to revealed and back again is immediate.
To my mind it’s generally preferable to keep it simple and avoid animating a disclosure widget. For a start, it’s tricky! The <details> element can’t be (easily) animated. And if using a DIY widget it’ll likely involve animating one of the expensive properties. Animating height or max-height is also gnarly when working with variable (auto) length content and often requires developers to go beyond CSS and reach for JavaScript to calculate computed element heights. Lastly, forgetting the technical challenges, there’s often no real need to animate disclosure; it might only hinder rather than help the user experience.
But let’s just say you have to do it, perhaps because the design spec requires it (like in BBC Sounds’ expanding and collapsing tracklists when viewed on narrow screens).
Options:
- Animate the
<details>element. This is a nice, standards-oriented approach. But it might only be viable for when you don’t need to mess with<details>appearance too much. We’d struggle to apply very custom styles, or to handle a “show the first few list items but not all” requirement like in the BBC Sounds example; - Animate CSS Grid. This is a nice idea but for now the animation only works in Firefox*. It’d be great to just consider it a progressive enhancement so it just depends on whether the animation is deemed core to the experience;
- Animate from a max-height of 0 to “something sufficient” (my pen is inspired by Scott O’Hara’s disclosure example). This is workable but not ideal; you kinda need to set a max-height sweetspot otherwise your animation will be delayed and too long. You could of course add some JavaScript to get the exact necessary height then set it. BBC use
max-heightfor their tracklist animation and those tracklists likely vary in length so I expect they use some JavaScript for height calculation.
* Update 20/2/23: the “animate CSS Grid” option now has wide browser support and is probably my preferred approach. I made a codepen that demonstrates a disclosure widget with animation of grid-template-rows.
Ringing bell icon
To be written.
Pulsing “radar” effect
To be written.
Accessibility
Accessibility and animation can co-exist, as Cassie Evans explains in her CSS-Tricks article Empathetic Animation. We should consider which parts of our website are suited to animation (for example perhaps not on serious, time-sensitive tasks) and we can also respect reduced motion preferences at a global level or in a more finer-grained way per component.
Notes
transition-delaycan be useful for avoiding common annoyances, such as when a dropdown menu that appears on hover disappears when you try to move the cursor to it.
References
- Inspiration: the animate.css library
- animate.css on github (good for searching for keyframe CSS)
- CSS transitions and transforms on Thoughtbot
- CSS Transitions by Josh Comeau
- Keyframe Animations by Josh Comeau
- Transition vs animation on CSS Animation
- Keyframe animation syntax on CSS-Tricks
- CSS animation for beginners on Thoughtbot
- Using CSS Transions on auto dimensions on CSS-Tricks
- Jhey Tompkins’s Image fade with interest codepen
Limbo
Enjoyed this funny and touching film about a Syrian asylum seeker searching for happiness on a remote Scottish island.
I’ve started reading Station Eleven, by Emily St. John Mandel.
GOV.UK introduce an experimental block link component
Here’s an interesting development in the block link saga: GOV.UK have introduced one (named .chevron-card) on their Homepage, citing how it’ll improve accessibility by increasing mobile touch targets. It’s not yet been added to their Design System while they’re monitoring it to see if it is successful. They’ve chosen the approach which starts with a standard, single, non-wrapping anchor then “stretches” it across the whole card via some pseudo elements and absolute positioning magic. I’m slightly surprised at this choice because it breaks the user’s ability to select text within the link. Really interested to see how it pans out!
Lovely write-up, & great rationale re. larger mobile tap targets! I’ve wrestled with “block links” & found that each approach has issues so it’s v. interesting that you chose the route that impacts text selection. Is that the lesser of the evils? Keen to hear how it pans out!
— Laurence Hughes (@fuzzylogicx) December 13, 2021
Front-end architecture for a new website (in 2021)
Just taking a moment for some musings on which way the front-end wind is blowing (from my perspective at least) and how that might practically impact my approach on the next small-ish website that I code.
I might lean into HTTP2
Breaking CSS into small modules then concatenating everything into a single file has traditionally been one of the key reasons for using Sass, but in the HTTP2 era where multiple requests are less of a performance issue it might be acceptable to simply include a number of modular CSS files in the <head>, as follows:
<link href="/css/base.css" rel="stylesheet">
<link href="/css/component_1.css" rel="stylesheet">
<link href="/css/component_2.css" rel="stylesheet">
<link href="/css/component_3.css" rel="stylesheet">
The same goes for browser-native JavaScript modules.
This isn’t something I’ve tried yet and it’d feel like a pretty radical departure from the conventions of recent years… but it‘s an option!
I’ll combine ES modules and classes
It’s great that JavaScript modules are natively supported in modern browsers. They allow me to remove build tools, work with web standards, and they perform well. They can also serve as a mustard cut that allows me to use other syntax and features such as async/await, arrow functions, template literals, the spread operator etc with confidence and without transpilation or polyfilling.
In the <head>:
<script type="module" src="/js/main.js"></script>
In main.js
import { Modal } from '/components/modal.js';
const Modal = new Modal();
modal.init();
In modal.js
export class Modal {
init() {
// modal functionality here
}
}
I’ll create Web Components
I’ve done a lot of preparatory reading and learning about web components in the last year. I’ll admit that I’ve found the concepts (including Shadow DOM) occasionally tough to wrap my head around, and I’ve also found it confusing that everyone seems to implement web components in different ways. However Dave Rupert’s HTML with Superpowers presentation really helped make things click.
I’m now keen to create my own custom elements for javascript-enhanced UI elements; to give LitElement a spin; to progressively enhance a Light DOM baseline into Shadow DOM fanciness; and to check out how well the lifecycle callbacks perform.
I’ll go deeper with custom properties
I’ve been using custom properties for a few years now, but at first it was just as a native replacement for Sass variables, which isn’t really exploiting their full potential. However at work we’ve recently been using them as the special sauce powering component variations (--gap, --mode etc).
In our server-rendered components we’ve been using inline style attributes to apply variations via those properties, and this brings the advantage of no longer needing to create a CSS class per variation (e.g. one CSS class for each padding variation based on a spacing scale), which in turn keeps code and specificity simpler. However as I start using web components, custom properties will prove really handy here too. Not only can they be updated by JavaScript, but furthermore they provide a bridge between your global CSS and your web component because they can “pierce the Shadow Boundary”, make styling Shadow DOM HTML in custom elements easier.
I’ll use BEM, but loosely
Naming and structuring CSS can be hard, and is a topic which really divides opinion. Historically I liked to keep it simple using the cascade, element and contextual selectors, plus a handful of custom classes. I avoided “object-oriented” CSS methodologies because I found them verbose and, if I’m honest, slightly “anti-CSS”. However it’s fair to say that in larger applications and on projects with many developers, this approach lacked a degree of structure, modularisation and predictability, so I gravitated toward BEM.
BEM’s approach is a pretty sensible one and, compared to the likes of SUIT, provides flexibility and good documentation. And while I’ve been keeping a watchful eye on new methodologies like CUBE CSS and can see that they’re choc-full of ideas, my feeling is that BEM remains the more robust choice.
It’s also important to me that BEM has the concept of a mix because this allows you to place multiple block classes on the same element so as to (for example) apply an abstract layout in combination with a more implementation-specific component class.
<div class="l-stack c-news-feed">
Where I’ll happily deviate from BEM is to favour use of certain ARIA attributes as selectors (for example [aria-current=page] or [aria-expanded=true] because this enforces good accessibility practice and helps create equivalence between the visual and non-visual experience. I’m also happy to use the universal selector (*) which is great for owl selectors and I’m fine with adjacent sibling (and related) selectors.
Essentially I’m glad of the structure and maintainability that BEM provides but I don’t want a straitjacket that stops me from using my brain and applying CSS properly.
Enhance! by Jeremy Keith—An Event Apart video (on Vimeo)
A classic talk by Jeremy Keith on progressive enhancement and the nature of the web and its technologies.
Learn Responsive Design (on web.dev)
Jeremy Keith’s new course for Google’s web.dev learning platform is fantastic and covers a variety of aspects of responsive design including layout (macro and micro), images, icons and typography.
Life lessons from Larry David and Adam Buxton
Recently, during a period when pandemic woes were taking their toll and likely making me a little cranky at home, I found some funny and helpful words of wisdom on Adam Buxton’s podcast and when re-watching Ricky Gervais’s interview with Larry David.
On sustaining a happy marriage, Buxton said:
Every now and then you think “they’re not doing what I want them to do and I think it’s important that they do do this thing that I want them to do! (But) you have to think really hard: is this thing actually important? Is it worth getting bent out of shape about?
It’s good advice. He mentioned that he’s slowly getting better at that, and I’m trying my best to, too.
Meanwhile, on the reasons why he got into acting, Larry David said:
it gets you out of the house!
So simple but true—it’s good to get out of the house! We’re now emerging from a period of sustained isolation and I should probably avoid being completely remote (and going stir crazy) and get back into the office occasionally.