Files
airship/packages/editor-tokens/EDITOR.md
T
Nayan 8d9079e13b feat(editor-tokens): generate the editor's --ap-* tokens from EDITOR.md
EDITOR.md is the source of truth and src/generated/editor.ts is built from it, so the
document describing the editor's palette and the code applying it cannot
disagree. The postbuild step emits the stylesheet the overlay ships.
2026-07-26 21:34:00 +05:30

11 KiB
Raw Blame History

name, description, tokens
name description tokens
Airship Editor Airship's visual-editor token system — a dark editor palette: dense neutral surfaces, hairline borders, a single Blue #0D99FF selection voltage, and a restrained 3–5% luminance surface progression. This front-matter is the canonical token source for the editor chrome (`@airship/editor-tokens`, emitted under the `--ap-*` namespace). It is fully independent of the `--pk-*` marketing tokens in home/packages/tokens and their re-skinned sibling in examples/vite-react, and evolves separately. No shadows on panels — separate surfaces with borders instead.
surface border text icon blue primary semantic selection timeline scrollbar divider shadow opacity gray input button typography fontSize iconSize control spacing rounded elevation boxModel motion
canvas base sidebar panel hover active selected overlay
#1E1E1E #242424 #2B2B2B #313131 #383838 #3F3F3F #454545 #202020F2
subtle default strong focus disabled
rgba(255,255,255,0.06) rgba(255,255,255,0.08) rgba(255,255,255,0.12) #0D99FF rgba(255,255,255,0.04)
primary secondary tertiary disabled placeholder inverse
#FFFFFF #C6C6C6 #9D9D9D #777777 #6A6A6A #111111
primary secondary muted disabled
#F3F3F3 #BEBEBE #8A8A8A #666666
50 100 200 300 400 500 600 700 800 900
#EAF6FF #CDEBFF #A8DBFF #75C7FF #33AEFF #0D99FF #007BE5 #0064BC #004D91 #00355F
primary hover active disabled bg bg-hover border
#0D99FF #33AEFF #007BE5 #4A82A6 rgba(13,153,255,0.10) rgba(13,153,255,0.15) rgba(13,153,255,0.50)
success success-hover success-active success-bg warning warning-hover warning-active warning-bg error error-hover error-active error-bg
#2ECC71 #27AE60 #1E874B rgba(46,204,113,0.12) #F5C84C #D9A822 #B48600 rgba(245,200,76,0.12) #FF4D4F #E53935 #C62828 rgba(255,77,79,0.12)
fill border handle guide
rgba(13,153,255,0.20) #0D99FF #FFFFFF #0D99FF
track marker active-marker highlight
#242424 #666666 #FFFFFF #0D99FF
track thumb thumb-hover thumb-active
transparent rgba(255,255,255,0.12) rgba(255,255,255,0.20) rgba(255,255,255,0.30)
horizontal vertical heavy
rgba(255,255,255,0.06) rgba(255,255,255,0.08) rgba(255,255,255,0.12)
xs sm floating
0 1px 2px rgba(0,0,0,0.16) 0 2px 8px rgba(0,0,0,0.20) 0 8px 32px rgba(0,0,0,0.35)
04 06 08 10 12 16 24 32 48 64
0.04 0.06 0.08 0.10 0.12 0.16 0.24 0.32 0.48 0.64
50 100 200 300 400 500 600 700 800 900 950
#FAFAFA #F5F5F5 #EBEBEB #DCDCDC #BEBEBE #9D9D9D #777777 #5A5A5A #3F3F3F #2B2B2B #1E1E1E
bg hover focus border focus-border disabled
#2B2B2B #313131 #313131 rgba(255,255,255,0.08) #0D99FF #252525
primary primary-hover primary-pressed primary-disabled secondary secondary-hover secondary-pressed secondary-disabled ghost-hover ghost-pressed
#0D99FF #33AEFF #007BE5 #3E5D73 #313131 #383838 #404040 #292929 rgba(255,255,255,0.05) rgba(255,255,255,0.08)
families
sans mono
"Inter", "Inter Fallback", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif "JetBrains Mono", "JetBrains Mono Fallback", ui-monospace, SFMono-Regular, Menlo, monospace
micro caption body label title heading
9 10 11 12 13 14
xs sm md lg
16 20 24 28
height icon-box gutter field-gap row-gap group-gap
24 28 8 2 6 12
hair xxs xs sm base md lg xl xxl section
1 4 8 12 16 20 24 32 48 80
none xs sm md lg xl pill full
0 4 6 8 12 16 9999 9999
flat card floating modal
none 0 0 0 1px var(--ap-border-default) 0 2px 8px rgba(0,0,0,0.20) 0 8px 32px rgba(0,0,0,0.35)
padding margin gap
#0D99FF #F5C84C #FF4D9D
ease ease-out ease-in-out dur-micro dur-base dur-slow
ease cubic-bezier(0.23, 1, 0.32, 1) cubic-bezier(0.77, 0, 0.175, 1) 100ms 150ms 200ms

Airship Editor Tokens

The visual editor's design system — a dark editor palette. The @airship/editor-tokens package generates its TypeScript token objects and CSS custom properties (--ap-*) directly from this front-matter, so this file and the code never drift. This system is independent of the --pk-* marketing tokens in home/packages/tokens; the two evolve separately.

There are in fact two --pk-* sets in this repo — home/packages/tokens and examples/vite-react/packages/tokens, which run an identical pipeline against different specs. They never collide with each other or with this one: each lives in its own pnpm workspace serving its own app, and pnpm scopes package names per workspace.

Character

  • Neutral grays, not warm — surfaces step #1E1E1E → #454545 in ~3–5% luminance increments ({surface.canvas} → {surface.selected}). This progression is the core of the editor's feel: calm, dense, cohesive.

  • Hairline borders, no heavy shadows. Panels separate with {border.*} hairlines; shadows are reserved for the floated chrome ({shadow.*}).

  • A single accent: Blue {primary.primary} #0D99FF. It carries selection, focus, links, and the primary action — used scarcely.

  • {semantic.*} is for state, and state only — success, warning, error. There used to be a purple and an orange family here too, and having them is what made them get used: purple ended up on a @media condition and on the word "Thinking", neither of which is a state. Hue in a grey editor is a claim that something needs attention, so a colour with no state behind it spends that attention on nothing. Anything that is merely a different kind of thing is said with weight, italics or the {text.*} ramp instead.

  • Dedicated {text.*}, {icon.*}, {border.*}, {blue.*} and `{gray.*}" scales keep surfaces, typography and icons decoupled from each other.

  • Icons sit one step below text. {icon.secondary} is the resting colour for every glyph; {icon.primary} is reserved for hover and the active state of a segmented control. An icon that matches the text colour reads as loud as a label, which is what makes a dense panel feel busy.

  • Type is a six-step ramp, {fontSize.micro} 9px → {fontSize.heading} 14px. The editor is deliberately denser than the marketing site: label (12px) is the workhorse for controls, body (11px) for monospace metadata, and heading (14px) is as large as chrome ever gets.

  • Icons render at {iconSize.*} — 16/20/24/28. The imported glyph set insets its artwork inside a 24 box (the mark spans roughly 4..20), so 24px is the default, not 16: a 24-box glyph drawn at 16px yields a ~10px mark.

  • {control.height} 24px and {control.gutter} 8px define the inspector's rhythm — every field, icon button and segmented cell is one control tall.

  • {control.icon-box} 28px is the chrome's ghost icon button — the bottom bar's tools, the canvas verbs beside them, the dock headers, Send. Larger than control.height on purpose: those buttons carry a 20px glyph and nothing else, so at 24 the mark would sit flush against the button's edge with no optical padding at all, which is what the dock headers used to do while the bottom bar a few pixels away did not. Inspector controls stay on control.height; they sit in a field grid whose rhythm is the thing that matters there.

  • Vertical spacing is a three-step scale, not one gap. A panel that puts everything at the same pitch reads as an undifferentiated stack however good its individual controls are, which is exactly what the inspector did before these existed:

    Token Separates
    {control.field-gap} 2px parts of one control — the four padding sides, a swatch and its hex, the cells of a segmented group
    {control.row-gap} 6px rows within a group
    {control.group-gap} 12px groups within a section
    {control.gutter} 8px columns on one row

    These are deliberately off the {spacing.*} scale. That scale is for page and panel chrome, where 4px is the smallest meaningful step; this one is for the inside of a 24px-tall control, where 2px is a real distance. Two scales, two jobs — and every gap inside the inspector must come from one of these four, so that changing the panel's density is one edit here rather than a sweep through twenty CSS rules.

  • {boxModel.*} are conventions, not choices. Blue inside the border, amber outside it, pink between children — the same three every browser's element inspector has used for fifteen years. Anyone who has opened one already knows which is which, and a prettier assignment would cost that recognition for nothing. They are their own group rather than borrowed from {semantic.*} because none of them is a state, and rather than from {primary.*} because only one of them is the accent.

  • Motion is three durations and three curves, and no more. Before {motion.*} existed the overlay had twenty-odd hand-written transition declarations and not one cubic-bezier anywhere, which is why nothing in it moved with a recognisable hand.

    Token Used for
    {motion.dur-micro} 100ms a control changing state under the pointer — background, colour, a caret
    {motion.dur-base} 150ms structure moving — a panel opening, a row stepping aside for a drag
    {motion.dur-slow} 200ms a whole surface re-anchoring, like a dock snapping to the other side
    {motion.ease} ease micro-states, where the curve is not perceptible and a named one is a lie
    {motion.ease-out} easeOutQuint the house curve: leaves fast, settles slowly, reads as physical
    {motion.ease-in-out} easeInOutQuart symmetric moves that start and end at rest

    Canvas chrome is exempt and must stay exempt. The hover and selection outlines are re-positioned on every pointer move, so a transition on them interpolates between two elements and the outline visibly trails the cursor — it reads as lag, not as easing. overlay/src/styles/chrome.css.ts carries no transition at all, and there is a build check that keeps it that way.

Theming

Editor chrome is dark-only. All tokens are emitted flat under --ap-* on the overlay's scoped root — there is no light/dark swap and no coupling to the marketing theme.