A single-file HTML magazine

The Browser Is Already a UI Library

Native elements, selectors and APIs that replace entire component libraries. No framework required.

01

Open

Disclosure, modals and floating UI — all built into the platform.

details dialog popover command

details / summary

Native accordion. No JS required for open/close.

What is progressive enhancement?

Start with working HTML, then layer CSS and JS. The core experience never breaks.

Why prefer native controls?

Accessibility, keyboard support, and platform conventions come free.

dialog

Modal with focus trap, backdrop and Escape handling.

Native dialog

This modal is fully accessible. Focus is trapped; Escape closes it.

popover

Lightweight floating UI. Top-layer, light dismiss.

Hello from popover

Click outside or press Escape to dismiss.

command / invoker

Declarative actions via command & commandfor (emerging).

Use popovertarget today; command buttons are landing in browsers for even cleaner invokers.

02

Input

Form controls that already know about dates, colours, ranges and validation.

date colour range datalist validation output autocomplete inputmode

date · colour · range

datalist · validation · inputmode

03

Reveal

Show and hide content with pure CSS and a couple of attributes.

:target until-found inert

:target

Style the element whose ID matches the URL hash.

Highlight the box →
I light up when the fragment is #reveal-box.

Clear highlight

hidden="until-found" · inert

until-found keeps content searchable. inert removes interactivity.

Use hidden="until-found" so find-in-page can still discover the content. Apply inert to background UI when a modal is open.

05

Move

Scrolling, snapping and page transitions handled by the browser.

scroll snap view transitions scroll-driven animation

scroll-snap

Swipe or scroll horizontally — cards snap into place.

Card 01

scroll-snap-align: center

Card 02

Native momentum & physics

Card 03

No carousel library needed

Card 04

Works with keyboard too

Card 05

Accessible by default

View Transitions API

Smooth morph between states. Click to toggle.

State A

Scroll-driven animation

Animate tied to scroll progress with animation-timeline: scroll().

Progress bars, parallax and reveal effects can now be driven by scroll position with pure CSS — no IntersectionObserver required for many cases.

06

Style

Selectors and colour functions that make component CSS far simpler.

:has :is :where @scope container queries color-mix light-dark corner-shape custom properties

:has()

Parent reacts to its children. Check the box.

Container queries

This card changes layout based on its own width.

Responsive component

No media-query breakpoint needed.

color-mix · light-dark

This page uses light-dark() and color-mix() for theme-aware colours. Toggle your OS dark mode to see it adapt.

corner-shape · custom properties

Squircle-ish corners (where supported) and design tokens via custom properties.

07

Let the browser work

Stop re-implementing what the platform already does well.

lazy images native controls semantic HTML

loading="lazy"

Images below the fold load only when needed.

Semantic HTML & native controls

Prefer <button>, <a>, <input>, <dialog>, <details> over div + role soup. The browser gives you keyboard support, focus management and accessibility tree mappings for free.

08

Adapt

Light and dark is one axis. A data attribute per axis turns custom properties into full design modes — density, contrast, fidelity, entitlement — with no extra CSS architecture.

data attributes custom properties color-scheme variable modes localStorage

Mode switches

Every switch sets one attribute on <html>. The whole magazine re-renders, and your choices survive a reload.

Theme
Density
Contrast
Fidelity
Plan

One attribute per axis

Each axis overrides only the tokens it owns, so five independent switches need five small rules — not thirty-six combined classes.

:root {
  --density: 1;
  --fidelity: solid;
}

:root[data-theme="dark"]      { color-scheme: dark; }
:root[data-density="compact"] { --density: 0.65; }
:root[data-contrast="high"]   { --ink: light-dark(#000, #fff); }
:root[data-fidelity="wireframe"] { --fidelity: dashed; }
:root[data-plan="pro"]        { --accent: light-dark(#4c5fd7, #8fa0ff); }

.card {
  border: 1px var(--fidelity) var(--border);
  padding: calc(1.35rem * var(--density));
}

Themes without a theme file

The theme axis sets color-scheme rather than redefining colours — every light-dark() token in the page already knows what to do, so an explicit light/dark switch costs two declarations.

Density and contrast are the accessibility axes people actually ask for. Fidelity turns the page into a wireframe for design review. Plan is a product axis: the same tokens, gated on what the account is entitled to.

Persistence is localStorage plus a small script in <head> that applies the saved modes before the first paint — no flash of the wrong theme.

No framework required

The browser already ships a capable UI library. Reach for a framework when you need complex state, routing or a design system — not for a modal, an accordion or a colour picker.

Built as a single HTML file · Works offline · Zero dependencies