This project uses the Next 13 App Router. Pages can be found in /app
and all other project code is found in /src
.
Jotai helps prevent unnecessary re-renders by using read-only and write-only state. The global store is found in src/store
.
The utility function atomWithLifecycle
creates a jotai atom that has onMount
and onUpdate
callbacks.
Using my mode
state as an example:
onMount
uses theprefers-color-scheme
media query to set the initial state valueonUpdate
updates thetheme-color
meta tag
I use OKLCH to create a consistent color scheme across hues. This allows me to use the same lightness and chroma values while rotating the hue.
type LightnessAndChroma = [number, number];
const lightnessAndChromaValues: Record<
Mode,
Record<Color, readonly LightnessAndChroma>
> = {
light: {
primary: [0.56, 0.15],
"primary-contrast": [0.99, 0.005],
container: [0.27, 0.06],
"container-contrast": [0.93, 0.03],
tint: [0.97, 0.011],
"tint-contrast": [1, 0],
},
dark: {
primary: [0.88, 0.064],
"primary-contrast": [0.35, 0.1],
container: [0.93, 0.03],
"container-contrast": [0.45, 0.1],
tint: [0.28, 0.02],
"tint-contrast": [0.16, 0.02],
},
};
These values are combined with a hue like so:
const [lightness, chroma] = lightnessAndChromaValues[mode][color];
const oklch = `oklch(${lightness} ${chroma} ${hue})`;
Components are styled using the data-hue
attribute and individual style tags injected into <head>
. These tags are created on both the client and server using the PostCSS OKLAB Function.
On mount, a MutationObserver
is used to watch for data-hue
attribute changes. When a change is detected, a new style tag is created and injected into <head>
. This allows the dynamic color system to stay up to date when the DOM is manipulated directly (bypassing the UI).
This code is licensed under MIT. The fonts used in this project are licensed by NaN Type Foundry to Andrew Wiggin and may not be used outside wiggin.dev. See the license here.