Andromeda Pro · Foundation
The primitives
Every Andromeda Pro component is built from the values on this page, read through three layers of tokens. This page is the what. The AI Brain teaches when and how to use each value.
Three layers
- 100200300400500
Layer 1
Primitives
The neutral ladder and four hue families. A theme retunes these; a component never reads them.
- status.info.textbrand 300
Layer 2
Semantic
Named roles such as
status.danger.text. Every component reads this layer and only this one. - Live
Layer 3
Component wires
A
--component-rolevariable in the one file that owns it, pointing at exactly one role. The wiring never leaves that file.
The neutral ladder
Thirteen grays, numbered by depth: 100 is the page ground, 1300 the strongest ink. Surfaces, borders and text are all roles pointing into this one ladder.
light
dark
Four hue families
Five stops each, 100 through 500, plus two alphas and a guaranteed-contrast on ink. The ramp itself is the same in both themes; what flips is which stop a role points at, shown further down. Color is never the only channel: a tone always rides with a glyph, a position or a label.
Brand
the live series, the primary action
light
dark
Success
this moved the good way
light
dark
Warning
caution, degraded, restricted
light
dark
Danger
loss, fault, threshold breach
light
dark
Semantic roles
Components never read a primitive. They say what they mean, in eight role groups:
textthe four inks: primary, secondary, muted, faintsurfacethe grounds and the interaction steps a fill climbsborderthe hairline ladder, subtle to strong, plus the floating framestatusfour tones × six roles: surface, border, text, on, onMuted, fillactionwhat a control does: primary, secondary, danger, and their statesfocusthe ring, the glow, and their invalid formsselectionthe mark, the track, the selected edgechartbaseline, live, context, threshold, grid, and the stacked rampHow a family crosses the themes
When the theme flips, a role’s stop mirrors around the middle of its family: 100 and 500 swap, 200 and 400 swap, and 300 is the pivot. Fills stop at 200 rather than 100, because 100 barely stands off the light page. The one thing that overrules the mirror is WCAG. Where the mirrored value misses its contrast minimum on the light page ground it steps toward the deep end until it clears. That is why brand and success text reads 300 → 500 rather than 300 → 400: on the light ground their 400 stop measures 3.97 and 4.21, under the 4.5 that normal text requires, so they step again. Danger reaches 4.86 at 400 and stops there. Warning’s 400 measures 3.12, and its 500 clears 4.5 but reads as brown, so its words take an authored deep ochre, marked ink below, the most saturated amber that clears 4.5 (4.88). The mark row lands on 400 everywhere because a mark is a non-text object and needs 3.0, not 4.5. Both columns are resolved from the same functions the site paints with, so they cannot drift from what you see.
Text
a status word, a value, a chart label
Secondary text
the quieter second line beside it
Foreground and borders
icons, dots, chart lines, frames
Background fill
a rail, a filled chip, a track
Every color, both themes
The whole set, dark on the left of each pair and light on the right. Enumerated from the live token set rather than listed by hand, so a color added to the system shows up here on its own. A pair marked same is deliberately theme invariant: a scrim darkens what is behind it whatever the theme, and a solid fill that carries pale text has to stay deep in both.
Neutrals
numbered by depth in the stack, not by lightness, so 100 is the page ground in both themes and the ladder never folds
neutral-100neutral-200neutral-300neutral-400neutral-500neutral-600neutral-700neutral-800neutral-900neutral-1000neutral-1100neutral-1200neutral-1300neutral-scrimsameText
the four neutral inks
text-primarytext-secondarytext-mutedtext-faintSurfaces
grounds and the steps a fill climbs under the pointer
surface-floatingsurface-scrimsamesurface-basesurface-raisedsurface-overlaysurface-hoversurface-activesurface-alphaBorders
the hairline ladder
border-floatingborder-subtleborder-baseborder-brightborder-strongBrand ramp
the primitive stops every brand role points at
brand-100samebrand-200samebrand-300samebrand-400samebrand-500samebrand-alphasamebrand-alpha-strongsamebrand-onsamebrand-on-brightsameStatus
four tones, six roles each
status-success-surfacesamestatus-success-borderstatus-success-onstatus-success-on-mutedstatus-success-textstatus-success-fillstatus-success-markstatus-success-areasamestatus-warning-surfacesamestatus-warning-borderstatus-warning-onstatus-warning-on-mutedstatus-warning-textstatus-warning-fillstatus-warning-markstatus-warning-areasamestatus-danger-surfacesamestatus-danger-borderstatus-danger-onstatus-danger-on-mutedstatus-danger-textstatus-danger-fillstatus-danger-markstatus-danger-areasamestatus-info-surfacesamestatus-info-borderstatus-info-onstatus-info-on-mutedstatus-info-textstatus-info-fillstatus-info-markstatus-info-areasameActions
what a control does
action-primarysameaction-primary-hoversameaction-primary-pressedsameaction-primary-bordersameaction-primary-border-hoversameaction-on-primarysameaction-secondaryaction-secondary-hoveraction-secondary-pressedaction-secondary-borderaction-secondary-border-hoveraction-on-secondaryaction-ghost-hoveraction-ghost-pressedaction-on-ghostaction-on-ghost-hoveraction-dangersameaction-danger-hoversameaction-danger-bordersameaction-danger-border-hoversameaction-on-dangersameaction-danger-text-hoveraction-active-textaction-active-glowFocus
the ring and its glow
focus-ringfocus-ring-invalidsameSelection
checked, selected, current
selection-borderselection-border-hoverselection-surfacesameselection-markselection-tracksameChart ink
roles, not series numbers
chart-baselinechart-livechart-contextchart-thresholdsamechart-comparechart-gridchart-cursorchart-axischart-stack1chart-stack2samechart-stack3chart-stack4Gradients
composed values, swapped stop by stop
gradient-brand-fadegradient-brand-sweepgradient-surface-softAccessibility
Contrast is checked by a script in both themes, not judged by eye, and it is the one thing here that overrules a design decision, including the family pivot above. The rest is written into the system’s rules, so every component follows it the same way.
4.5 : 1
Normal text
Anything under 24px, or under 18.66px bold. Almost all of our text. WCAG 1.4.3.
3.0 : 1
Large text and non-text
Display sizes, and the status icons, dots and borders, the chart lines and the focus ring. WCAG 1.4.3 and 1.4.11.
10 : 1
Measured headroom
The script checks the 4.5 floor. Measured, the primary, secondary and muted text inks sit above 10:1 in both themes, past the 7:1 of AAA.
Focus you can see
Reached by keyboard, every control shows one 1px ring from the focus token, in place of the browser default. An invalid field gets a red ring instead.
Full keyboard use
Menus move with the arrow keys, Home and End. The drawer keeps focus inside while open and hands it back when it closes. The date picker moves by day, week and month. Escape closes what opened.
Reduced motion
When a visitor asks their system for less motion, entrances and cascades turn off, state feedback stays, and animated objects hold a still frame.
Color is never the only signal
Every state a reader has to tell apart also carries an icon, a dot, a label, an arrow or a dash. Success and warning look almost the same to some color-blind readers; the second signal is what they read.
Touch targets
On touch screens the tappable area grows to 40px while the control keeps its size on screen. In tight clusters it stops at 32px, so neighbors never overlap.
Rules per component
Every component lists its own accessibility behavior in its rules: the roles and labels it uses, its keys and its focus handling. An icon-only button, for one, must carry a label.
Typeface
One free, open-source face from Google Fonts carries every label, value and heading in the system.
Aa
Manrope
A modern geometric sans with open, even shapes that stay clear at small interface sizes and hold together at display sizes.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
The type ramp
A step names three values, not one: a size, its leading, and its tracking. Taking a step means taking all three. Tracking is a function of size here, open at the small end where letterforms crowd and pulled in at the display end where large type sets loose. Weight is the second axis and stays a component’s choice.
textXsAa10px / 0.625rem14px / 0.875rem0.14em / 14%textSmAa12px / 0.75rem18px / 1.125rem0.08em / 8%textMdAa14px / 0.875rem20px / 1.25rem0.02em / 2%textLgAa16px / 1rem24px / 1.5rem0.02em / 2%textXlAa18px / 1.125rem28px / 1.75rem0text2xlAa20px / 1.25rem30px / 1.875rem0displayXsAa24px / 1.5rem32px / 2rem-0.02em / -2%displaySmAa30px / 1.875rem38px / 2.375rem-0.02em / -2%displayMdAa36px / 2.25rem44px / 2.75rem-0.02em / -2%displayLgAa48px / 3rem60px / 3.75rem-0.02em / -2%displayXlAa60px / 3.75rem72px / 4.5rem-0.02em / -2%display2xlAa72px / 4.5rem90px / 5.625rem-0.02em / -2%Spacing
A 4px grid with one half-step. Control heights ride their own three-rung ladder (28 / 32 / 40) so fields, buttons and toggles in a row always agree.
14px1.56px28px312px416px520px624px832px1040px1248px