Skip to content
Lantern

Foundation

Principles

Four rules that decide the arguments. Dark by default, one codebase for two breakpoints, depth from steps not shadows, and meaning before decoration.

Four rules

In priority order. Where two conflict, the lower number wins.

01

Dark is the system, not a mode

There is no light theme and there is no plan for one. The tokens live on :root, the .dark class is intentionally a no-op, and the page background is true black.

This is why a component that hard-codes a light value has no fallback to hide behind — it is simply wrong.

02

One codebase, two breakpoints

Desktop and mobile ship from the same components. 63 of them carry a load-bearing max-md: / md: pair, so nothing is done until it has been seen at both.

The full rule.

03

Depth comes from steps, not shadows

Elevation is a move up the surface scale plus a hairline border — 0% to 4% to 7.5% to 10%. Cards rest on border-subtle and move to border on hover.

The one exception is the convex button treatment, which earns its shadows by being the single strongest affordance in the product.

04

Colour means something or it is absent

The system has no brand hue. Primary is white. Saturation is reserved for status — success, warning, destructive — and a chart series scale.

If a colour is doing decoration rather than communication, it should be a surface step instead.

What follows from these

The rules are short because most of the work is in what they rule out.

No component invents a value

Every colour, radius and duration comes from the Tailwind preset, which comes from the stylesheet. A component that needs a value the system does not have is a request to add a token, not a licence to hard-code one.

Examples are static and deterministic

No Math.random, no Date.now-relative labels, no live intervals, no network. State that a component would normally hold internally gets lifted to a prop so each state is a fixed, reviewable thing rather than a click-path.

App concerns stay in the app

No data fetching, no router, no contexts, no persistence. A component that needs to know where it is gets told through props — which is also what makes it documentable here.