Tutorials

A dark theme that does not flash

The theme has to be applied before the first paint, which a deferred module cannot do.

IntermédiaireArticle4 steps

Theme flashing has a single cause: the theme is applied after the browser has already painted the page. Everything else follows from that.

1. Put the tokens on the root

The theme is carried by an attribute on <html>, and every colour resolves through a CSS variable. Changing the attribute then re-themes the whole page at once, without any component knowing the current theme.

2. Resolve the theme in the <head>

The script has to be inline and blocking. A type="module" script is deferred by definition: it will run after paint, and the visitor will watch the page change.

3. Let an explicit choice win

With nothing stored, follow prefers-color-scheme. With a stored choice, the choice wins — including when the system preference changes afterwards.

4. Test the right thing

A test reading the attribute after load passes even when the script is deferred. Read it at the first frame instead, which is the closest a test gets to what the eye saw.

Act

Dark themePin the site's appearancesetTheme({theme:"dark"})
Light themePin the site's appearancesetTheme({theme:"light"})
System themeFollow the system preference againsetTheme({theme:"system"})
Switch the site to FrenchFrançaissetLocale({locale:"fr"})
Labs — ProductFilter Labs by typefilterLabs({type:"Produit"})
Labs — PrototypeFilter Labs by typefilterLabs({type:"Prototype"})
Labs — ResearchFilter Labs by typefilterLabs({type:"Recherche"})
Labs — Reset filtersClear every Labs filterfilterLabs({reset:true})
Journal — EngineeringFilter the Journal by categoryfilterJournal({category:"Ingénierie"})
Journal — SecurityFilter the Journal by categoryfilterJournal({category:"Sécurité"})
Journal — Agent-NativeFilter the Journal by categoryfilterJournal({category:"Agent-Native"})
Journal — StudioFilter the Journal by categoryfilterJournal({category:"Studio"})
Journal — Reset filtersClear every Journal filterfilterJournal({reset:true})

Services

DiscoveryFrame the product before the first line of code, so the budget builds instead of correcting.openService({service:"discovery"})
Product designAn interface that holds together, carried by a design system your developers can keep alive without us.openService({service:"design"})
Web developmentA fast web application that holds up over time, with the tests and delivery chain to match.openService({service:"web"})
Mobile developmentAn iOS and Android app from a single codebase, published on both stores.openService({service:"mobile"})
Infrastructure & securityInfrastructure described as code, and Zero Trust access that does not rest on a text message.openService({service:"infrastructure"})
Maintenance & evolutionA product kept current, watched and fixed, with someone who answers when it breaks.openService({service:"maintenance"})
Agent-Native Architecture AdvisoryMake an existing product agent-operable without rewriting it — and without opening more than it should.openService({service:"agent-native"})

Go to

YorroProduct engineering studio in Abidjan and Paris. We design, we build, we ship.navigate({page:"home"})
ServicesBuild a product end to end, or advise a team on its agent-native architecture.navigate({page:"services"})
LabsWhat the studio has built: products, prototypes and research, with where each one actually stands.navigate({page:"labs"})
TutorialsStep-by-step guides drawn from real projects, grouped into tracks by topic.navigate({page:"tutorials"})
JournalThe studio's notes and positions.navigate({page:"journal"})
ContactDescribe your project; you get a framing back, not a boilerplate quote.navigate({page:"contact"})
AboutWho runs the studio, how it works, and what it turns down.navigate({page:"about"})
↑↓ move↵ runesc closeOne Actions layer