CSS Color Scheme Queries (“Dark Mode CSS”)

Next to Safari 12.1 earlier this month, Firefox 67 now also supports “CSS Color Scheme Queries”.

The prefers-color-scheme media feature allows sites to adapt their styles to match a user’s preference for dark or light color schemes, a choice that’s begun to appear in operating systems like Windows, macOS and Android.

Chrome will support + enable it by default in Chrome 76 (the current Canary build at the time of writing)

Combine prefers-color-scheme with CSS Custom Properties (“CSS Variables”) for easy theming.

root {
    color-scheme: light dark;
    --special-text-color: hsla(60, 100%, 50%, 0.5);
    --border-color: black;

@media (prefers-color-scheme: dark) {
    :root {
        --special-text-color: hsla(60, 50%, 70%, 0.75);
        --border-color: white;

.special {
    color: var(--special-text-color);
    border: 1px solid var(--border-color);

If you’re too lazy, then you can somewhat fake it by abusing mix-blend-mode: difference;, although it’s not perfect. Here’s an adjusted snippet, which injects the hack on the body using ::before:

@media (prefers-color-scheme: dark) {
    body::before {
        content: '';
        display: block;
        width: 100vw;
        height: 100vh;
        position: fixed;
        top: 0;
        left: 0;
        background: white;
        mix-blend-mode: difference;
        z-index: 1;
        pointer-events: none;

A nice touch of Safari is that its DevTools also change when Dark Mode is enabled:

WebKit: Dark Mode Support in WebKit →
WebKit: Dark Mode Support in Web Inspector →Firefox 67: Dark Mode CSS, WebRender, and more →

Getting started with Event Sourcing (in Laravel)

I’ve seen Freek give his talk on Event Sourcing in Laravel at a Full Stack Ghent meetup recently. Glad to see the talk evolved a bit more and he now has made a recording of it.

Don’t let the “in Laravel” part scare you, as the knowledge is applicable across different frameworks and languages. For Laravel based projects you use Spatie’s own Laravel Event Projector package, but for others you can go with EventSauce.

More links – including source code! – in Freek’s post.

Application State Management with React

Kent C. Dodds on how he uses React itself – and not something like Redux – for his Application State Management.

Here’s the real kicker, if you’re building an application with React, you already have a state management library installed in your application. You don’t even need to npm install (or yarn add) it. It costs no extra bytes for your users, it integrates with all React packages on npm, and it’s already well documented by the React team. It’s React itself.

React is a state management library.

The core React features driving his method is React’s revised context and Hooks.

// src/count/count-context.js
import React from 'react'
const CountContext = React.createContext();

function useCount() {
  const context = React.useContext(CountContext)
  if (!context) {
    throw new Error(`useCount must be used within a CountProvider`);
  return context;

function CountProvider(props) {
  const [count, setCount] = React.useState(0);
  const value = React.useMemo(() => [count, setCount], [count]);
  return <CountContext.Provider value={value} {...props} />

export {CountProvider, useCount}
// src/count/page.js
import React from 'react'
import {CountProvider, useCount} from './count-context'

function Counter() {
  const [count, setCount] = useCount();
  const increment = () => setCount(c => c + 1);
  return <button onClick={increment}>{count}</button>

function CountDisplay() {
  const [count] = useCount();
  return <div>The current counter count is {count}</div>

function CountPage() {
  return (
        <CountDisplay />
        <Counter />

Application State Management with React →

The Most Expensive Lesson Of My Life: Details of SIM port hack

Sean Coone got hacked last week. Even with 2FA enabled, hackers got in … because his phone number got transferred to a rogue device:

My personal identity was hacked last week. The attacker was able to steal $100k+ in a sweep of my Coinbase account. I’m equal parts embarrassed, hurt, and deeply remorseful.

In an effort to raise awareness about the attack, I wrote about it.

Reading this one would almost get a second, private, phone number for services that support 2FA only using phone numbers.

The Most Expensive Lesson Of My Life: Details of SIM port hack →

Symfony Form Validation: Validating a date range

One of the (Symfony based) PHP projects I’m working on contains a form which allows the user to generate video clips from CCTV footage. To do this the user can enter a start and stop DateTime. For this to work the submitted input data is then checked: both start and stop must be dates, and the stop date must be set to a point in time after the start date.

Symfony’s DateTime Constraint can make sure both entries are DateTime instances. To check whether that the end date is after the begin date, one can use the Callback Constraint. Injected into that callback is a ExecutionContextInterface by which you can access the form, and thus other form params.

Here’s an example with the inputs start and stop:

use Symfony\Component\Validator\Constraints;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

// …

    ->add('start', 'datetime', 
        'constraints' => [
            new Constraints\NotBlank(),
            new Constraints\DateTime(),
    ->add('stop', 'datetime', [
        'constraints' => [
            new Constraints\NotBlank(),
            new Constraints\DateTime(),
            new Constraints\Callback(function($object, ExecutionContextInterface $context) {
                $start = $context->getRoot()->getData()['start'];
                $stop = $object;

                if (is_a($start, \DateTime::class) && is_a($stop, \DateTime::class)) {
                    if ($stop->format('U') - $start->format('U') < 0) {
                            ->buildViolation('Stop must be after start')

If you want to have a minimum duration between both start and stop, you can change the number 0 in the snippet above to any number of seconds.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

Game of Thrones: An Ending

George R.R. Martin, on his blog “Not a Blog”, now that the final episode of Game of Thrones has aired:

I’m writing. Winter is coming, I told you, long ago… and so it is. THE WINDS OF WINTER is very late, I know, I know, but it will be done. I won’t say when, I’ve tried that before, only to burn you all and jinx myself… but I will finish it, and then will come A DREAM OF SPRING.

How will it all end? I hear people asking. The same ending as the show? Different?

Well… yes. And no. And yes. And no. And yes. And no. And yes.

And oh, I especially like GRRM’s closing paragraph in his post:

Book or show, which will be the “real” ending? […] How about this? I’ll write it. You read it. Then everyone can make up their own mind, and argue about it on the internet.


An Ending – Not a Blog →

Really looking forward to the books, as the final season on TV was quite the disappointment. Next to some very poor dialogues this season was packed with inconsistencies, character development that got thrown out of the window, lots of loose ends … the number of WTFs per episode was rising way too fast imho.

See, for example, this narrated version of Episode 3 to see what I’m talking about:

(Same thing can be said for all other episode in the season)

Winging back to Episode 3, a few smart people suggested some really good changes, such as an improved plan for “The Battle of Winterfell” itself or even a rewrite of some parts to make it more tensive.

Be sure to also read this article on why the writing in GoT S08 feels off.

Microsoft Edge preview builds for macOS

Speaking of IE in the previous post: Microsoft just (officially) released the first preview builds for Microsoft Edge for macOS, which uses the Chromium rendering engine internally.

Although I don’t use my Mac’s Touch Bar – I’ve got set it to always show the full control strip – I really like how they’ve linked it to the browser’s tabs:

🤔 I’m wondering if that would place nice with tab hoarder like me though … I rarely have less than 250 tabs open (spread across several windows) … so much to read and watch.

Introducing the first Microsoft Edge preview builds for macOS →
Download Microsoft Edge preview builds →

A Conspiracy To Kill IE6 — How YouTube got rid of IE6 for us

Great read on how a few YouTube engineers bypassed the internal Google politics in order to abolish IE6 from their list of supported browsers:

One idea rose to the surface that quickly captured everyone’s attention. Instead of outright dropping IE6 support, what if we just threatened to? How would users react? Would they revolt against YouTube? Would they mail death threats to our team like had happened in the past? Or would they suddenly become loud advocates of modern browsers?

A Conspiracy To Kill IE6 →

Selling Composer Packages through “Private Packagist for Vendors”

Nice new addition by Packagist:

If you’re selling PHP packages, the easiest way to offer Composer package installation to your customers is now “Private Packagist for Vendors”. You get a unique URL and authentication token for each customer and they can use these in their composer.json file to install your packages. Especially if you’re still sending zip files to your customers, there is really no reason anymore not to to offer Composer installations.

You can use their our API to integrate “Private Packagist for Vendors” with your existing PHP package shop: Create a customer, grant the customer access to the package, and then get the info needed to send to the customer — all using their API.

// 1. Create Customer
$customer = $client
    ->create('Acme Web Inc.');

// 2. Grant access to package for customer
        'name' => 'my-vendor/cool-package',
        'versionConstraint' => '^1.0',
        'expirationDate' => strtotime('+1 year'),

// 3. Get info to send to user
$info = $client->customers()->show($customer['id']);

// …
//    'composerRepository' => [
//        'url' => 'https://my-vendor.repo.packagist.com',
//        'user' => 'token',
//        'token' => 'a6addb89a67b2822d352d113',
// …

As you can see in the code above, customers their access can be locked to specific versions and also limited in time.

Packagist Blog: Introducing Private Packagist for Vendors →
Private Packagist for Vendors →