The systemLumos 0.0.1
Tokens
Every color, size and space in one place, and why they are fluid rather than fixed at breakpoints.
A token is a named value: --space-4Token20px → 24px (fluid), --brand-500Token#c6fb50The brand color. Used as an accent and as the primary button fill., --text-mainToken16px → 18px (fluid). Everything in Lumos (every component, every utility, every pattern) reads its values from tokens instead of hardcoding them.
The practical effect is that the system has one place to change. Make --brand-500Token#c6fb50The brand color. Used as an accent and as the primary button fill. teal and every button, accent, focus ring and highlight on the site is teal, without a find-and-replace and without missing one.
Where they live
:root {
/* Swatches */
--light-100: #ffffff;
--light-200: #ebebeb;
--dark-800: #2f2b2d;
--dark-900: #1f1d1e;
--brand-500: #c6fb50;
--brand-text: var(--dark-900);
/* … */
}Change the values. Keep the names: the names are the contract every component relies on. The full list is on the Tokens reference, generated straight from this file.
Fluid, not breakpointed
Most tokens are not one value. They interpolate smoothly between a minimum and a maximum as the viewport grows from 320px to 1440px, so type and spacing never jump at an arbitrary width.
--space-4-min: 20; /* 20px at a 320px viewport */
--space-4-max: 24; /* 24px at 1440px and wider */Between those two widths the value slides. There is no tablet breakpoint where the padding suddenly changes, because there is no breakpoint at all, which also means one less set of numbers to design and maintain.
The two ends of the range are set by --viewport-minToken320The viewport width, in pixels, where every fluid value sits at its minimum. and --viewport-maxToken1440The viewport width where every fluid value reaches its maximum.. Every fluid token uses the same pair, so the whole system scales together.
The families
- Swatches: the raw colors. Referenced by the themes, never used directly in a component.
- Spacing:
--space-1Token6px → 8px (fluid) through--space-8Token40px → 64px (fluid), plus--site-marginToken16px → 48px (fluid)The space between the page edge and its content.,--site-gutterToken16px → 32px (fluid)The space between grid and flex children. and the three--section-space-*steps. - Text styles:
--displayToken64px → 112px (fluid),--h1Token48px → 80px (fluid) …--h6Token16px → 18px (fluid),--text-largeToken18px → 20px (fluid),--text-mainToken16px → 18px (fluid),--text-smallToken14px → 16px (fluid). Each carries its own line height, letter spacing, weight, margins and trim as sibling tokens. - Layout:
--max-width-mainTokencalc(var(--viewport-max) / 16 * 1rem)The widest the main container gets, matching --viewport-max.,--max-width-smallToken70remA comfortable reading measure for long text.,--site-widthTokencalc(100% - var(--site-margin) * 2)The content width once the site margins are taken off.,--nav-heightToken5remThe height the fixed nav reserves.. - Borders, focus and radius:
--border-widthToken0.0625rem,--focus-widthToken0.125remThickness of the focus ring. Never set this to zero.,--focus-offsetToken0.1827remHow far the focus ring sits outside the element.,--radius-smallToken0.5rem,--radius-mainToken1rem,--radius-roundToken100vw.
Using a token
In your own CSS, read it with var():
.hero_panel {
padding: var(--space-6);
border-radius: var(--radius-main);
background-color: var(--background-2);
}Names that start with an underscore
Reading the class tables you will meet names like --_gap-size, --_margin-top and --_mw. The leading underscore means private: the value is set and read inside one block, so a utility can hand a number to the pattern on the same element without inventing a token for it. .gap-4Utilitygrid-column-gap: var(--_gap-size)grid-row-gap: var(--_gap-size)--_gap-size: var(--space-4) sets --_gap-size, and the grid pattern reads it back — nobody else is meant to.
So they are not part of the contract the other names are. A token is a promise that survives an upgrade; a --_ name can be renamed in any release, and nothing outside the block that set it should read one. Use the same prefix in your own components for the values that are your component's own business, and you get the same freedom to change them later.
Text styles come as sets
A text style is not just a size. --h2Token40px → 64px (fluid) has --h2-line-heightTokenvar(--line-height-small), --h2-letter-spacingTokenvar(--letter-spacing-tight), --h2-font-weightTokenvar(--primary-medium), --h2-margin-topTokenvar(--space-7), --h2-margin-bottomTokenvar(--space-5) and a pair of leading-trim values beside it. The .text-style-h2 class applies all of them at once, which is why a heading looks right without any adjustment.
The leading-trim values are worth knowing about: they remove the invisible space a font leaves above its caps and below its baseline, so a heading sits flush against whatever is beside it. It is the difference between spacing that looks right and spacing that measures right.