16 CSS Portfolio Layouts08 / 16
Dark Mode Portfolio Website Layout CSS Variables
One layout, two skins, five lines of real logic: every color in the page routes through custom properties declared once for light and re-declared once under [data-theme='dark'], so the toggle only flips an attribute. It respects prefers-color-scheme on first paint, the sun morphs into a moon with a pure-CSS mask trick, and color-scheme keeps native scrollbars and form controls in sync with the theme.
Published
The code
<section class="cpl-08" aria-label="Dark mode portfolio layout demo">
<div class="cpl-08__page" id="cpl08-page">
<header class="cpl-08__bar">
<span class="cpl-08__brand"><i aria-hidden="true"></i>ren.dev</span>
<nav class="cpl-08__nav" aria-label="Site">
<a href="#work" aria-current="true">Work</a><a href="#notes">Notes</a><a href="#about">About</a>
</nav>
<button class="cpl-08__toggle" type="button" aria-pressed="false" aria-label="Switch to dark theme">
<span class="cpl-08__orb" aria-hidden="true"></span>
</button>
</header>
<main class="cpl-08__body">
<div class="cpl-08__hero">
<h2>Ren Okafor — front-end engineer who sweats the last 4%.</h2>
<p>Design systems, performance budgets and interfaces that work at 2am. Previously at two startups you've heard of.</p>
</div>
<div class="cpl-08__cards">
<a class="cpl-08__card" href="#tokens"><h3>Tokens at scale</h3><p>A theming architecture serving 6 products from one palette.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#perf"><h3>The 90KB storefront</h3><p>Shipping a full e-commerce front end under a hard weight budget.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#a11y"><h3>Keyboard-first checkout</h3><p>Rebuilding a payment flow to WCAG 2.2 AA without redesigning it.</p><span>Case study →</span></a>
</div>
</main>
</div>
</section><section class="cpl-08" aria-label="Dark mode portfolio layout demo">
<div class="cpl-08__page" id="cpl08-page">
<header class="cpl-08__bar">
<span class="cpl-08__brand"><i aria-hidden="true"></i>ren.dev</span>
<nav class="cpl-08__nav" aria-label="Site">
<a href="#work" aria-current="true">Work</a><a href="#notes">Notes</a><a href="#about">About</a>
</nav>
<button class="cpl-08__toggle" type="button" aria-pressed="false" aria-label="Switch to dark theme">
<span class="cpl-08__orb" aria-hidden="true"></span>
</button>
</header>
<main class="cpl-08__body">
<div class="cpl-08__hero">
<h2>Ren Okafor — front-end engineer who sweats the last 4%.</h2>
<p>Design systems, performance budgets and interfaces that work at 2am. Previously at two startups you've heard of.</p>
</div>
<div class="cpl-08__cards">
<a class="cpl-08__card" href="#tokens"><h3>Tokens at scale</h3><p>A theming architecture serving 6 products from one palette.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#perf"><h3>The 90KB storefront</h3><p>Shipping a full e-commerce front end under a hard weight budget.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#a11y"><h3>Keyboard-first checkout</h3><p>Rebuilding a payment flow to WCAG 2.2 AA without redesigning it.</p><span>Case study →</span></a>
</div>
</main>
</div>
</section>.cpl-08,
.cpl-08 *,
.cpl-08 *::before,
.cpl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cpl-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
container-type: inline-size;
}
.cpl-08__page {
color-scheme: light;
--bg: oklch(0.97 0.005 250);
--card: oklch(1 0 0);
--ink: oklch(0.23 0.02 260);
--mut: oklch(0.53 0.015 260);
--line: oklch(0.89 0.008 260);
--acc: oklch(0.55 0.19 265);
width: 100%;
container: cpl08/inline-size;
color: var(--ink);
background: var(--bg);
overflow: hidden;
transition: background-color .4s,border-color .4s;
min-height: 100vh;
min-height: 100svh;
display: flex;
flex-direction: column;
}
.cpl-08__page[data-theme="dark"] {
color-scheme: dark;
--bg: oklch(0.19 0.02 265);
--card: oklch(0.24 0.02 265);
--ink: oklch(0.94 0.008 250);
--mut: oklch(0.66 0.015 260);
--line: oklch(0.31 0.02 265);
--acc: oklch(0.75 0.14 85);
}
.cpl-08__bar {
display: flex;
align-items: center;
gap: 22px;
padding: 16px 24px;
border-bottom: 1px solid var(--line);
transition: border-color .4s;
}
.cpl-08__brand {
display: flex;
align-items: center;
gap: 9px;
font-weight: 800;
font-size: 15px;
letter-spacing: -.01em;
}
.cpl-08__brand i {
width: 10px;
height: 10px;
border-radius: 3px;
background: var(--acc);
transition: background .4s;
rotate: 45deg;
}
.cpl-08__nav {
display: flex;
gap: 4px;
margin-left: auto;
}
.cpl-08__nav a {
display: grid;
place-items: center;
min-height: 36px;
padding: 0 13px;
border-radius: 9px;
font-size: 13px;
font-weight: 600;
text-decoration: none;
color: var(--mut);
transition: color .2s,background .2s;
}
.cpl-08__nav a[aria-current],
.cpl-08__nav a:hover,
.cpl-08__nav a:focus-visible {
color: var(--ink);
background: color-mix(in oklch,var(--ink) 8%,transparent);
}
.cpl-08__nav a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cpl-08__toggle {
position: relative;
width: 46px;
height: 46px;
border: 1px solid var(--line);
border-radius: 50%;
background: var(--card);
cursor: pointer;
display: grid;
place-items: center;
transition: background-color .4s,border-color .4s,scale .2s;
}
.cpl-08__toggle:hover {
scale: 1.07;
}
.cpl-08__toggle:active {
scale: .93;
}
.cpl-08__toggle:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__orb {
position: relative;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--acc);
transition: background .4s;
mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
-webkit-mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
transition: background .4s,-webkit-mask-position .45s cubic-bezier(.4,0,.2,1),mask-position .45s cubic-bezier(.4,0,.2,1);
}
.cpl-08__page[data-theme="dark"] .cpl-08__orb {
mask-position: -14px 8px;
-webkit-mask-position: -14px 8px;
}
.cpl-08__body {
flex: 1;
align-content: center;
padding: clamp(20px,4cqi,34px);
display: grid;
gap: 26px;
}
.cpl-08__hero h2 {
font-size: clamp(21px,3.4cqi,29px);
line-height: 1.18;
letter-spacing: -.025em;
max-width: 22em;
text-wrap: balance;
}
.cpl-08__hero p {
margin-top: 10px;
font-size: 14px;
line-height: 1.6;
color: var(--mut);
max-width: 52ch;
transition: color .4s;
}
.cpl-08__cards {
display: grid;
gap: 14px;
grid-template-columns: repeat(auto-fill,minmax(min(220px,100%),1fr));
}
.cpl-08__card {
display: grid;
gap: 8px;
align-content: start;
padding: 20px;
border-radius: 16px;
background: var(--card);
border: 1px solid var(--line);
text-decoration: none;
color: inherit;
transition: background-color .4s,border-color .3s,translate .25s cubic-bezier(.2,.7,.2,1);
}
.cpl-08__card:hover,
.cpl-08__card:focus-visible {
translate: 0 -3px;
border-color: color-mix(in oklch,var(--acc) 45%,var(--line));
}
.cpl-08__card:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__card h3 {
font-size: 15.5px;
letter-spacing: -.01em;
}
.cpl-08__card p {
font-size: 13px;
line-height: 1.55;
color: var(--mut);
transition: color .4s;
}
.cpl-08__card span {
margin-top: 6px;
font-size: 12.5px;
font-weight: 700;
color: var(--acc);
transition: color .4s;
}
@media (prefers-reduced-motion: reduce) {
.cpl-08 * {
transition-duration: .01ms !important;
}
}.cpl-08,
.cpl-08 *,
.cpl-08 *::before,
.cpl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cpl-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
container-type: inline-size;
}
.cpl-08__page {
color-scheme: light;
--bg: oklch(0.97 0.005 250);
--card: oklch(1 0 0);
--ink: oklch(0.23 0.02 260);
--mut: oklch(0.53 0.015 260);
--line: oklch(0.89 0.008 260);
--acc: oklch(0.55 0.19 265);
width: 100%;
container: cpl08/inline-size;
color: var(--ink);
background: var(--bg);
overflow: hidden;
transition: background-color .4s,border-color .4s;
min-height: 100vh;
min-height: 100svh;
display: flex;
flex-direction: column;
}
.cpl-08__page[data-theme="dark"] {
color-scheme: dark;
--bg: oklch(0.19 0.02 265);
--card: oklch(0.24 0.02 265);
--ink: oklch(0.94 0.008 250);
--mut: oklch(0.66 0.015 260);
--line: oklch(0.31 0.02 265);
--acc: oklch(0.75 0.14 85);
}
.cpl-08__bar {
display: flex;
align-items: center;
gap: 22px;
padding: 16px 24px;
border-bottom: 1px solid var(--line);
transition: border-color .4s;
}
.cpl-08__brand {
display: flex;
align-items: center;
gap: 9px;
font-weight: 800;
font-size: 15px;
letter-spacing: -.01em;
}
.cpl-08__brand i {
width: 10px;
height: 10px;
border-radius: 3px;
background: var(--acc);
transition: background .4s;
rotate: 45deg;
}
.cpl-08__nav {
display: flex;
gap: 4px;
margin-left: auto;
}
.cpl-08__nav a {
display: grid;
place-items: center;
min-height: 36px;
padding: 0 13px;
border-radius: 9px;
font-size: 13px;
font-weight: 600;
text-decoration: none;
color: var(--mut);
transition: color .2s,background .2s;
}
.cpl-08__nav a[aria-current],
.cpl-08__nav a:hover,
.cpl-08__nav a:focus-visible {
color: var(--ink);
background: color-mix(in oklch,var(--ink) 8%,transparent);
}
.cpl-08__nav a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cpl-08__toggle {
position: relative;
width: 46px;
height: 46px;
border: 1px solid var(--line);
border-radius: 50%;
background: var(--card);
cursor: pointer;
display: grid;
place-items: center;
transition: background-color .4s,border-color .4s,scale .2s;
}
.cpl-08__toggle:hover {
scale: 1.07;
}
.cpl-08__toggle:active {
scale: .93;
}
.cpl-08__toggle:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__orb {
position: relative;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--acc);
transition: background .4s;
mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
-webkit-mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
transition: background .4s,-webkit-mask-position .45s cubic-bezier(.4,0,.2,1),mask-position .45s cubic-bezier(.4,0,.2,1);
}
.cpl-08__page[data-theme="dark"] .cpl-08__orb {
mask-position: -14px 8px;
-webkit-mask-position: -14px 8px;
}
.cpl-08__body {
flex: 1;
align-content: center;
padding: clamp(20px,4cqi,34px);
display: grid;
gap: 26px;
}
.cpl-08__hero h2 {
font-size: clamp(21px,3.4cqi,29px);
line-height: 1.18;
letter-spacing: -.025em;
max-width: 22em;
text-wrap: balance;
}
.cpl-08__hero p {
margin-top: 10px;
font-size: 14px;
line-height: 1.6;
color: var(--mut);
max-width: 52ch;
transition: color .4s;
}
.cpl-08__cards {
display: grid;
gap: 14px;
grid-template-columns: repeat(auto-fill,minmax(min(220px,100%),1fr));
}
.cpl-08__card {
display: grid;
gap: 8px;
align-content: start;
padding: 20px;
border-radius: 16px;
background: var(--card);
border: 1px solid var(--line);
text-decoration: none;
color: inherit;
transition: background-color .4s,border-color .3s,translate .25s cubic-bezier(.2,.7,.2,1);
}
.cpl-08__card:hover,
.cpl-08__card:focus-visible {
translate: 0 -3px;
border-color: color-mix(in oklch,var(--acc) 45%,var(--line));
}
.cpl-08__card:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__card h3 {
font-size: 15.5px;
letter-spacing: -.01em;
}
.cpl-08__card p {
font-size: 13px;
line-height: 1.55;
color: var(--mut);
transition: color .4s;
}
.cpl-08__card span {
margin-top: 6px;
font-size: 12.5px;
font-weight: 700;
color: var(--acc);
transition: color .4s;
}
@media (prefers-reduced-motion: reduce) {
.cpl-08 * {
transition-duration: .01ms !important;
}
}(function () {
document.querySelectorAll('.cpl-08__page').forEach(function (page) {
if (page.dataset.bound) return; page.dataset.bound = '1';
var btn = page.querySelector('.cpl-08__toggle');
function apply(dark) {
page.toggleAttribute('data-theme', false);
if (dark) page.setAttribute('data-theme', 'dark'); else page.removeAttribute('data-theme');
btn.setAttribute('aria-pressed', String(dark));
btn.setAttribute('aria-label', dark ? 'Switch to light theme' : 'Switch to dark theme');
}
apply(window.matchMedia('(prefers-color-scheme: dark)').matches);
btn.addEventListener('click', function () { apply(!page.hasAttribute('data-theme')); });
});
})();(function () {
document.querySelectorAll('.cpl-08__page').forEach(function (page) {
if (page.dataset.bound) return; page.dataset.bound = '1';
var btn = page.querySelector('.cpl-08__toggle');
function apply(dark) {
page.toggleAttribute('data-theme', false);
if (dark) page.setAttribute('data-theme', 'dark'); else page.removeAttribute('data-theme');
btn.setAttribute('aria-pressed', String(dark));
btn.setAttribute('aria-label', dark ? 'Switch to light theme' : 'Switch to dark theme');
}
apply(window.matchMedia('(prefers-color-scheme: dark)').matches);
btn.addEventListener('click', function () { apply(!page.hasAttribute('data-theme')); });
});
})();Here's a working CSS Portfolio Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Dark Mode Portfolio Website Layout CSS Variables
Source: https://codefronts.com/layouts/css-portfolio-layouts/dark-mode-portfolio-website-layout-css-variables/
One layout, two skins, five lines of real logic: every color in the page routes through custom properties declared once for light and re-declared once under [data-theme='dark'], so the toggle only flips an attribute. It respects prefers-color-scheme on first paint, the sun morphs into a moon with a pure-CSS mask trick, and color-scheme keeps native scrollbars and form controls in sync with the theme.
## HTML
```html
<section class="cpl-08" aria-label="Dark mode portfolio layout demo">
<div class="cpl-08__page" id="cpl08-page">
<header class="cpl-08__bar">
<span class="cpl-08__brand"><i aria-hidden="true"></i>ren.dev</span>
<nav class="cpl-08__nav" aria-label="Site">
<a href="#work" aria-current="true">Work</a><a href="#notes">Notes</a><a href="#about">About</a>
</nav>
<button class="cpl-08__toggle" type="button" aria-pressed="false" aria-label="Switch to dark theme">
<span class="cpl-08__orb" aria-hidden="true"></span>
</button>
</header>
<main class="cpl-08__body">
<div class="cpl-08__hero">
<h2>Ren Okafor — front-end engineer who sweats the last 4%.</h2>
<p>Design systems, performance budgets and interfaces that work at 2am. Previously at two startups you've heard of.</p>
</div>
<div class="cpl-08__cards">
<a class="cpl-08__card" href="#tokens"><h3>Tokens at scale</h3><p>A theming architecture serving 6 products from one palette.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#perf"><h3>The 90KB storefront</h3><p>Shipping a full e-commerce front end under a hard weight budget.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#a11y"><h3>Keyboard-first checkout</h3><p>Rebuilding a payment flow to WCAG 2.2 AA without redesigning it.</p><span>Case study →</span></a>
</div>
</main>
</div>
</section>
```
## CSS
```css
.cpl-08,
.cpl-08 *,
.cpl-08 *::before,
.cpl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cpl-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
container-type: inline-size;
}
.cpl-08__page {
color-scheme: light;
--bg: oklch(0.97 0.005 250);
--card: oklch(1 0 0);
--ink: oklch(0.23 0.02 260);
--mut: oklch(0.53 0.015 260);
--line: oklch(0.89 0.008 260);
--acc: oklch(0.55 0.19 265);
width: 100%;
container: cpl08/inline-size;
color: var(--ink);
background: var(--bg);
overflow: hidden;
transition: background-color .4s,border-color .4s;
min-height: 100vh;
min-height: 100svh;
display: flex;
flex-direction: column;
}
.cpl-08__page[data-theme="dark"] {
color-scheme: dark;
--bg: oklch(0.19 0.02 265);
--card: oklch(0.24 0.02 265);
--ink: oklch(0.94 0.008 250);
--mut: oklch(0.66 0.015 260);
--line: oklch(0.31 0.02 265);
--acc: oklch(0.75 0.14 85);
}
.cpl-08__bar {
display: flex;
align-items: center;
gap: 22px;
padding: 16px 24px;
border-bottom: 1px solid var(--line);
transition: border-color .4s;
}
.cpl-08__brand {
display: flex;
align-items: center;
gap: 9px;
font-weight: 800;
font-size: 15px;
letter-spacing: -.01em;
}
.cpl-08__brand i {
width: 10px;
height: 10px;
border-radius: 3px;
background: var(--acc);
transition: background .4s;
rotate: 45deg;
}
.cpl-08__nav {
display: flex;
gap: 4px;
margin-left: auto;
}
.cpl-08__nav a {
display: grid;
place-items: center;
min-height: 36px;
padding: 0 13px;
border-radius: 9px;
font-size: 13px;
font-weight: 600;
text-decoration: none;
color: var(--mut);
transition: color .2s,background .2s;
}
.cpl-08__nav a[aria-current],
.cpl-08__nav a:hover,
.cpl-08__nav a:focus-visible {
color: var(--ink);
background: color-mix(in oklch,var(--ink) 8%,transparent);
}
.cpl-08__nav a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cpl-08__toggle {
position: relative;
width: 46px;
height: 46px;
border: 1px solid var(--line);
border-radius: 50%;
background: var(--card);
cursor: pointer;
display: grid;
place-items: center;
transition: background-color .4s,border-color .4s,scale .2s;
}
.cpl-08__toggle:hover {
scale: 1.07;
}
.cpl-08__toggle:active {
scale: .93;
}
.cpl-08__toggle:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__orb {
position: relative;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--acc);
transition: background .4s;
mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
-webkit-mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
transition: background .4s,-webkit-mask-position .45s cubic-bezier(.4,0,.2,1),mask-position .45s cubic-bezier(.4,0,.2,1);
}
.cpl-08__page[data-theme="dark"] .cpl-08__orb {
mask-position: -14px 8px;
-webkit-mask-position: -14px 8px;
}
.cpl-08__body {
flex: 1;
align-content: center;
padding: clamp(20px,4cqi,34px);
display: grid;
gap: 26px;
}
.cpl-08__hero h2 {
font-size: clamp(21px,3.4cqi,29px);
line-height: 1.18;
letter-spacing: -.025em;
max-width: 22em;
text-wrap: balance;
}
.cpl-08__hero p {
margin-top: 10px;
font-size: 14px;
line-height: 1.6;
color: var(--mut);
max-width: 52ch;
transition: color .4s;
}
.cpl-08__cards {
display: grid;
gap: 14px;
grid-template-columns: repeat(auto-fill,minmax(min(220px,100%),1fr));
}
.cpl-08__card {
display: grid;
gap: 8px;
align-content: start;
padding: 20px;
border-radius: 16px;
background: var(--card);
border: 1px solid var(--line);
text-decoration: none;
color: inherit;
transition: background-color .4s,border-color .3s,translate .25s cubic-bezier(.2,.7,.2,1);
}
.cpl-08__card:hover,
.cpl-08__card:focus-visible {
translate: 0 -3px;
border-color: color-mix(in oklch,var(--acc) 45%,var(--line));
}
.cpl-08__card:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__card h3 {
font-size: 15.5px;
letter-spacing: -.01em;
}
.cpl-08__card p {
font-size: 13px;
line-height: 1.55;
color: var(--mut);
transition: color .4s;
}
.cpl-08__card span {
margin-top: 6px;
font-size: 12.5px;
font-weight: 700;
color: var(--acc);
transition: color .4s;
}
@media (prefers-reduced-motion: reduce) {
.cpl-08 * {
transition-duration: .01ms !important;
}
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.cpl-08__page').forEach(function (page) {
if (page.dataset.bound) return; page.dataset.bound = '1';
var btn = page.querySelector('.cpl-08__toggle');
function apply(dark) {
page.toggleAttribute('data-theme', false);
if (dark) page.setAttribute('data-theme', 'dark'); else page.removeAttribute('data-theme');
btn.setAttribute('aria-pressed', String(dark));
btn.setAttribute('aria-label', dark ? 'Switch to light theme' : 'Switch to dark theme');
}
apply(window.matchMedia('(prefers-color-scheme: dark)').matches);
btn.addEventListener('click', function () { apply(!page.hasAttribute('data-theme')); });
});
})();
```Here's a working CSS Portfolio Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Dark Mode Portfolio Website Layout CSS Variables
Source: https://codefronts.com/layouts/css-portfolio-layouts/dark-mode-portfolio-website-layout-css-variables/
One layout, two skins, five lines of real logic: every color in the page routes through custom properties declared once for light and re-declared once under [data-theme='dark'], so the toggle only flips an attribute. It respects prefers-color-scheme on first paint, the sun morphs into a moon with a pure-CSS mask trick, and color-scheme keeps native scrollbars and form controls in sync with the theme.
## HTML
```html
<section class="cpl-08" aria-label="Dark mode portfolio layout demo">
<div class="cpl-08__page" id="cpl08-page">
<header class="cpl-08__bar">
<span class="cpl-08__brand"><i aria-hidden="true"></i>ren.dev</span>
<nav class="cpl-08__nav" aria-label="Site">
<a href="#work" aria-current="true">Work</a><a href="#notes">Notes</a><a href="#about">About</a>
</nav>
<button class="cpl-08__toggle" type="button" aria-pressed="false" aria-label="Switch to dark theme">
<span class="cpl-08__orb" aria-hidden="true"></span>
</button>
</header>
<main class="cpl-08__body">
<div class="cpl-08__hero">
<h2>Ren Okafor — front-end engineer who sweats the last 4%.</h2>
<p>Design systems, performance budgets and interfaces that work at 2am. Previously at two startups you've heard of.</p>
</div>
<div class="cpl-08__cards">
<a class="cpl-08__card" href="#tokens"><h3>Tokens at scale</h3><p>A theming architecture serving 6 products from one palette.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#perf"><h3>The 90KB storefront</h3><p>Shipping a full e-commerce front end under a hard weight budget.</p><span>Case study →</span></a>
<a class="cpl-08__card" href="#a11y"><h3>Keyboard-first checkout</h3><p>Rebuilding a payment flow to WCAG 2.2 AA without redesigning it.</p><span>Case study →</span></a>
</div>
</main>
</div>
</section>
```
## CSS
```css
.cpl-08,
.cpl-08 *,
.cpl-08 *::before,
.cpl-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cpl-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
container-type: inline-size;
}
.cpl-08__page {
color-scheme: light;
--bg: oklch(0.97 0.005 250);
--card: oklch(1 0 0);
--ink: oklch(0.23 0.02 260);
--mut: oklch(0.53 0.015 260);
--line: oklch(0.89 0.008 260);
--acc: oklch(0.55 0.19 265);
width: 100%;
container: cpl08/inline-size;
color: var(--ink);
background: var(--bg);
overflow: hidden;
transition: background-color .4s,border-color .4s;
min-height: 100vh;
min-height: 100svh;
display: flex;
flex-direction: column;
}
.cpl-08__page[data-theme="dark"] {
color-scheme: dark;
--bg: oklch(0.19 0.02 265);
--card: oklch(0.24 0.02 265);
--ink: oklch(0.94 0.008 250);
--mut: oklch(0.66 0.015 260);
--line: oklch(0.31 0.02 265);
--acc: oklch(0.75 0.14 85);
}
.cpl-08__bar {
display: flex;
align-items: center;
gap: 22px;
padding: 16px 24px;
border-bottom: 1px solid var(--line);
transition: border-color .4s;
}
.cpl-08__brand {
display: flex;
align-items: center;
gap: 9px;
font-weight: 800;
font-size: 15px;
letter-spacing: -.01em;
}
.cpl-08__brand i {
width: 10px;
height: 10px;
border-radius: 3px;
background: var(--acc);
transition: background .4s;
rotate: 45deg;
}
.cpl-08__nav {
display: flex;
gap: 4px;
margin-left: auto;
}
.cpl-08__nav a {
display: grid;
place-items: center;
min-height: 36px;
padding: 0 13px;
border-radius: 9px;
font-size: 13px;
font-weight: 600;
text-decoration: none;
color: var(--mut);
transition: color .2s,background .2s;
}
.cpl-08__nav a[aria-current],
.cpl-08__nav a:hover,
.cpl-08__nav a:focus-visible {
color: var(--ink);
background: color-mix(in oklch,var(--ink) 8%,transparent);
}
.cpl-08__nav a:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 2px;
}
.cpl-08__toggle {
position: relative;
width: 46px;
height: 46px;
border: 1px solid var(--line);
border-radius: 50%;
background: var(--card);
cursor: pointer;
display: grid;
place-items: center;
transition: background-color .4s,border-color .4s,scale .2s;
}
.cpl-08__toggle:hover {
scale: 1.07;
}
.cpl-08__toggle:active {
scale: .93;
}
.cpl-08__toggle:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__orb {
position: relative;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--acc);
transition: background .4s;
mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
-webkit-mask: radial-gradient(circle 14px at 26px 4px,transparent 98%,#000);
transition: background .4s,-webkit-mask-position .45s cubic-bezier(.4,0,.2,1),mask-position .45s cubic-bezier(.4,0,.2,1);
}
.cpl-08__page[data-theme="dark"] .cpl-08__orb {
mask-position: -14px 8px;
-webkit-mask-position: -14px 8px;
}
.cpl-08__body {
flex: 1;
align-content: center;
padding: clamp(20px,4cqi,34px);
display: grid;
gap: 26px;
}
.cpl-08__hero h2 {
font-size: clamp(21px,3.4cqi,29px);
line-height: 1.18;
letter-spacing: -.025em;
max-width: 22em;
text-wrap: balance;
}
.cpl-08__hero p {
margin-top: 10px;
font-size: 14px;
line-height: 1.6;
color: var(--mut);
max-width: 52ch;
transition: color .4s;
}
.cpl-08__cards {
display: grid;
gap: 14px;
grid-template-columns: repeat(auto-fill,minmax(min(220px,100%),1fr));
}
.cpl-08__card {
display: grid;
gap: 8px;
align-content: start;
padding: 20px;
border-radius: 16px;
background: var(--card);
border: 1px solid var(--line);
text-decoration: none;
color: inherit;
transition: background-color .4s,border-color .3s,translate .25s cubic-bezier(.2,.7,.2,1);
}
.cpl-08__card:hover,
.cpl-08__card:focus-visible {
translate: 0 -3px;
border-color: color-mix(in oklch,var(--acc) 45%,var(--line));
}
.cpl-08__card:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.cpl-08__card h3 {
font-size: 15.5px;
letter-spacing: -.01em;
}
.cpl-08__card p {
font-size: 13px;
line-height: 1.55;
color: var(--mut);
transition: color .4s;
}
.cpl-08__card span {
margin-top: 6px;
font-size: 12.5px;
font-weight: 700;
color: var(--acc);
transition: color .4s;
}
@media (prefers-reduced-motion: reduce) {
.cpl-08 * {
transition-duration: .01ms !important;
}
}
```
## JavaScript
```js
(function () {
document.querySelectorAll('.cpl-08__page').forEach(function (page) {
if (page.dataset.bound) return; page.dataset.bound = '1';
var btn = page.querySelector('.cpl-08__toggle');
function apply(dark) {
page.toggleAttribute('data-theme', false);
if (dark) page.setAttribute('data-theme', 'dark'); else page.removeAttribute('data-theme');
btn.setAttribute('aria-pressed', String(dark));
btn.setAttribute('aria-label', dark ? 'Switch to light theme' : 'Switch to dark theme');
}
apply(window.matchMedia('(prefers-color-scheme: dark)').matches);
btn.addEventListener('click', function () { apply(!page.hasAttribute('data-theme')); });
});
})();
```How this works
The architecture rule that makes dark mode a 10-minute job instead of a rewrite: components never name colors, they name roles. Both palettes assign the same role tokens:
.page{ color-scheme: light;
--bg:oklch(0.97 0.005 250); --card:oklch(1 0 0);
--ink:oklch(0.23 0.02 260); --mut:oklch(0.53 0.015 260);
--line:oklch(0.89 0.008 260);--acc:oklch(0.55 0.19 265); }
.page[data-theme='dark']{ color-scheme: dark;
--bg:oklch(0.19 0.02 265); --card:oklch(0.24 0.02 265);
--ink:oklch(0.94 0.008 250); --mut:oklch(0.66 0.015 260);
--line:oklch(0.31 0.02 265); --acc:oklch(0.75 0.14 85); }Note the accent isn't just lightened — it changes hue (blue → warm gold), because saturated dark-mode accents need to move toward higher lightness to hold contrast, and oklch makes that a principled edit (raise L, keep C sane). color-scheme is the forgotten line: it switches the browser's own UI — scrollbars, inputs, selection — so nothing native glows white inside your dark page.
The JS is intentionally boring: read matchMedia('(prefers-color-scheme: dark)') for the initial value, flip data-theme on click, mirror the state onto aria-pressed. A one-shot transition: background-color .4s, color .4s on themed surfaces makes the swap feel intentional rather than glitchy. The icon is one element: a circle whose moon crescent is carved by a mask that slides in on theme change — transform and mask only, no icon font, no SVG swap.
Make it yours
- Palette surgery: re-skin the whole demo by editing the twelve token lines — nothing inside components ever needs touching. That's the entire point of the pattern.
- Persistence: add
localStorage.setItem('theme', next)in the click handler and read it before matchMedia on load — kept out of the demo so embeds never write storage you didn't ask for. - System-change reactivity: add a
matchMedia(...).addEventListener('change')that re-applies system preference until the user makes an explicit choice. - Modern alternative: for simple sites,
light-dark(colorA, colorB)(Baseline 2024) can replace the attribute swap entirely — keep the attribute approach when you need a user override, which portfolios do.
Gotchas — read before shipping
- Don't transition
*for the theme swap — transition only surfaces (page, cards, text). A global transition makes every hover in the UI feel like syrup afterwards. - Dark mode is not invert(): borders must get LIGHTER relative to surfaces, shadows become nearly useless (use borders/elevation-tint instead), and saturated brand colors usually fail contrast until you raise L in oklch.
- A theme toggle is state, not decoration — it must be a real
<button>witharia-pressedand a label; an icon-only div is invisible to assistive tech. - Test BOTH themes at 4.5:1 for body text; muted-text tokens are where dark themes quietly fail WCAG (oklch L ≥ ~0.64 on an L 0.19 surface is the safe zone here).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
Custom-property theming works everywhere back to 2017; oklch and the mask-based icon set the stated floor. color-scheme is Baseline since 2022.