 m-cli – Swiss Army Knife for macOS

m-cli is a macOS command line tool that lets you interact with utilities and applications entirely in Terminal.

Gives you a bunch of shorthand CLI commands that you can use, such as m finder showdesktop YES, m battery status, m dns flush, etc. These replace a bunch of custom aliases you might have set up in your dotfiles

 m-cli – Swiss Army Knife for macOS →

A Previous Sibling Selector

Jim Nielsen set out to style a bunch of links that appeared before hr elements. As the element tree – generated from a Markdown file – was entirely flat, there are no enclosing section elements to hook onto in order to select those links (using something like section p:last-child a:only-child).

The solution? The :has() selector:

p:has(+ hr) a:only-child {

This, again, proves that the :has() selector is way more than just a parent selector.

A Previous Sibling Selector →

Fun CSS-only scrolling effects for Matterday

In this post, Lynn Fisher walks us through the parallax scrolling CSS effects featured on the Matterday project. Does not use Scroll-Linked Animations (which is undergoing a spec rewrite) but transform hacks that rely on translateZ() to create a stack and scale() to correct the size again.

Fun CSS-only scrolling effects for Matterday →

The CSS Cascade: A Deep Dive (2022.06.09 @ CSS Day)

Me, speaking at CSS Day

On June 9 & 10, I was in Amsterdam for CSS Day – a conference I’ve been attending since it’s very first edition in 2013. This time – after a two year hiatus due to youknowwhat – I was very glad to be back! This time not as an attendee though, but as a speaker. In my talk I covered the details of the CSS Cascade, with a huge focus on CSS Cascade Layers.

CSS is short for Cascading Style Sheets. But what exactly is this Cascade, and how does it work? What are Origins? How do you calculate Specificity? And where do those new Cascade Layers you might have heard of fit in? And oh, what exactly happens when you use an !important somewhere?

In this insightful talk, we’ll take a look under the hood of browsers, and detail how they determine which CSS declarations to apply and which not.


# Slides

The slides are up on slidr.io, and also embedded below:


# Video

The talk was recorded and will premiere on the CSS Day YouTube Channel on July 7 at 13:55. The recording might be of more help, as the slides themselves lack some explanation and supporting animations to tell the whole story.

☝️ If you’re looking a full content-recap of CSS Day, I would recommend the recaps by Grrr:


# Personal Reflection

Elad, Amit, Adam, Hui Jing, and me
Elad, Amit, Adam, Hui Jing, and me

I had planned this speaking engagement a long time ago – even from before I joined Google – and was really looking forward to it. Initially I planned on talking about Scroll-Linked Animations with @scroll-timeline, but given that the syntax is undergoing a rewrite I switched subjects to the Cascade and Cascade Layers.

The long wait has been worth the while: speaking to a real crowd again on a real stage felt great. There’s a great vibe going on during the conference, speaker dinner, and hallway track – one that simply cannot be matched by any virtual event.

Meeting some CSS friends who I only know through Twitter in real life for the very first time, as well as meeting new people, and hanging out with some of my colleagues (who I, as a remote worker, normally only get to see through a screen), is an enriching experience.

I’m honored to have been part of this edition with its stellar line-up, and hope I was able to meet the same level of quality as my fellow speakers.


# Thanks!

Me, speaking at CSS Day

I would like to thank organizers Krijn and PPK for having me. Having organised events for almost a decade now, they know how to do it – even a totally new venue was no hurdle to them. A big thanks to the other speakers as well for giving their talks – the quality, as per usual, was very high level.

I hope you all had fun attending my talk — I know I had making it (and whilst bringing it forward) — and perhaps you even learned something from it along the way 🙂


💁‍♂️ If you are a conference or meetup organiser, don't hesitate to contact me to come speak at your event.

Re-evaluating technology

Jeremy underlines importance of revisiting past decisions:

In recent years in particular it feels like the web has come on in leaps and bounds: service workers, native JavaScript APIs, and an astonishing boost in what you can do with CSS. Most important of all, the interoperability between browsers is getting better and better. Universal support for new web standards arrives at a faster rate than ever before.

But developers remain suspicious, still prefering to trust third-party libraries over native browser features. They made a decision about those libraries in the past. They evaluated the state of browser support in the past. I wish they would re-evaluate those decisions.

I’ve said it before: The web catches up.

Re-evaluating technology →

Star Wars Scene Transition Effects in CSS

You know those wipe transitions between scenes in Star Wars movies? Have you ever thought it would be awesome to recreate them with CSS? Probably not, but, well, here we are. Let’s do it.

Uses gradients set as a mask-image which transition on hover. For the iris and clock wipte effects, @property is used.

Star Wars Scene Transition Effects in CSS →

Dark Mode Toggles Should be a Browser Feature

Mockup of a browser in Dark Mode + a Light Mode override button (🌕) which the website on display takes into account

When implementing Dark Mode and ways to toggle it, developers currently need to duplicate code and roll their own toggle implementation.

What if, instead, browsers would take care of all that?


Table of Contents


# Dark Mode 101

Thanks to Media Queries Level 5 we can implement Dark Mode using only CSS. By also using CSS Custom Properties in the mix, it becomes very manageable:

html {
    color-scheme: light dark; /* This site supports both light and dark mode */

:root { /* Default Light Mode colors */
    --primary-color: black;
    --primary-background: white;    

@media(prefers-color-scheme: dark) {
    :root { /* Dark Mode colors */
        --primary-color: white;
        --primary-background: black;    

body {
    color: var(--primary-color);
    background: var(--primary-background);

Depending on the System/OS Color Theme setting, the site will either use black text on a white background (Light Mode) or white text on a black background (Dark Mode).


# The problem with Dark Mode Toggles

To offer users an easy way to switch between Light and Dark Mode, a lot of developers are offering toggle buttons their website. Instead of having the user go to the System Preferences, they can switch between Light/Dark Mode without ever leaving the site.

Developers who ever did implement a Light/Dark Mode toggle, might have noticed these side-effects/quirks:

  1. To make this work, developers need to rely on either the Checkbox Hack or JavaScript to alter something – mostly a class or attribute – on the document:

    <select name="color-scheme-">
         <option value="system">System</option>
         <option value="light">Forced Light</option>
         <option value="dark">Forced Dark</option>
    document.querySelector('color-scheme').addEventListener('change', (e) => {
         document.documentElement.setAttribute('data-force-color-mode', e.target.value);
  2. Because of the way it is implemented (see step 1), duplication of the CSS code is required:

     :root[data-force-color-mode="light"] { /* Default Light Mode colors + Forced Light Mode */
         --primary-color: black;
         --primary-background: white;    
     /* Dark Color Scheme (System Preference) */
     @media (prefers-color-scheme: dark) {
         :root {
             --primary-color: white;
             --primary-background: black;    
     /* Dark Color Scheme (Override) */
     :root[data-force-color-mode="dark"] {
         --primary-color: white;
         --primary-background: black;    
  3. To persist the setting across pages and reloads, JavaScript is required:

    • Write the selected value after changing it (Most likely a cookie or Local Storage)
    • Read the value on page load, to make sure the proper override class is set on the document
  4. You'll need some more JavaScript as well to have the page respond to changes of the System Preference.

    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
     mediaQuery.addListener(() => {
         // Make sure the dropdown is up-to-date based on mediaQuery.matches

    But then again: do you still respond if the override has been set?

Above that many implementations I have seen don't take the "System" value into account. By omitting this option, the sites will never be able to respond to the system preference again, as they always have an override applied.


# Code Duplication as a red flag

One the things that immediately stands out as a red flag is the Code Duplication part. It's been bothering me before, and it is still bothering me today. The other listed issues are also issues, but are somewhat acceptable.

Thinking of ways to solve the Code Duplication part, I see these ideas floating around:

  • If we had a :media() pseudo-class available (see this proposal by Lea Verou), they could query it inside :is()

    :root:is(.dark, :media(prefers-color-scheme: dark)) {
          --primary-color: white;
          --primary-background: black;    
  • In similar fashion, if Media Queries could also be used as selectors (see this suggestion), you can also group the selectors together

    @media (prefers-color-scheme: dark) p,
    .dark-mode p {
          --primary-color: white;
          --primary-background: black;    

    (Sidenote: I’m not a fan of this suggestion to be honest)

  • If we were able to programmatically change the value for prefers-color-scheme, the duplicated CSS part could be omitted. Something like:

    document.querySelector('color-scheme').addEventListener('change', (e) => {
          window.setMedia('prefers-color-scheme', e.target.value);

    UPDATE: Also see this CSSWG issue

These solutions, however, do not tackle any of the other listed issues. What about the JS requirement to respond to toggle changes, persist the value, read the value on load, …?


# A better way of toggling

What if, instead of having developers try and implement their own Dark Mode Toggle with all its intricacies, the browser would offer this functionality to the user?

Think of a button that is part of the browser's UI that you could click. Something like the button Chrome DevTools already sports, but then part of the browser UI, available for all users

Video of the feature as it appears in Chrome DevTools

With such a button in place, the user can easily toggle the preference, and developers don't need to do anything to offer easy switching.

Above that it would allow users to have their OS set to Dark Mode, while reading websites in Light Mode (or vice versa)


# Inherited Settings

The way I see it, the chosen Dark/Light value would be persisted on a per-site basis – i.e. altering it would store the chosen preference for only that site.

To not force users to set the value for every new site they visit, there would also be a setting at the browser level.

Combined, you'd get a value that inherits down from the system level down to the site level:

  1. Dark/Light Setting at the OS level
  2. Dark/Light Setting at the Browser level (default value: inherit from OS)
  3. Dark/Light Setting at the Site level (default value: inherit from Browser)

With these in place, users get the freedom to have the sites they visit use a different color scheme from their OS (if they want) and to also make exceptions for certain websites.

UPDATE: Also see this Chromium issue


# Demo

You can play around with the CodePen below to see how altering these settings at the various levels would propagate onto a dummy browser UI + two webpages:

See the Pen Browser Level Dark Mode Toggle by Bramus (@bramus) on CodePen.

Be sure to try out the values to OS:Light+Browser:Dark+Site:Light for bram.us, and then switch tabs to other.site:

  • The site bram.us will be Light, due to the Site setting being Light
  • The site other.site will be Dark, due to the Browser setting being Dark and its specific Site setting being inherit

👏 Shout out to Firefox for shipping a Dark/Light Setting at the Browser level in their Firefox 100 release. They also have an extra option, where the chosen browser theme can decide whether to apply Light or Dark Mode.

Now let's hope we can also get a setting at the Site Level?


# In Summary

In summary, I think there should be easier ways to switch Dark Mode on a per-site basis. Ideally, switching/overriding should be a simple button (or the like) at the browser level. That way we can get rid of all those custom light/dark mode buttons, and the browsers can sync the preference values to all the devices a user uses.

Should authors want to roll their own custom button, then a way to programmatically change the prefers-color-scheme value from within JavaScript would be a welcome addition (csswg-drafts/issues#6517). That way the duplicated CSS code block can be removed and there would be less JS involved to achieve the act of switching:

window.setMedia('prefers-color-scheme', 'dark');

In this scenario, calls to window.setMedia() would bubble up to the browser (so they can keep their UI in sync) and the browser itself would persist the chosen preference for that site.

Let me know what you think in the comments, or hit me up on Twitter.


# Spread the word

To help spread the contents of this post, feel free to retweet the announcement tweet:


🔥 Like what you see? Want to stay in the loop? Here's how: