@manakind/shiptight-tokens
v0.21.0
Published
ShipTight's design tokens. Values live as DTCG-format JSON under `src/`, organized into three explicit layers, and are compiled to CSS and JS by Style Dictionary on every build.
Readme
@manakind/shiptight-tokens
ShipTight's design tokens. Values live as DTCG-format JSON under src/,
organized into three explicit layers, and are compiled to CSS and JS by
Style Dictionary on every build.
Two themes: Chart paper (light, the default) and Night watch (dark).
The dark values are ShipTight's brand palette: deep #0b1929, navy #0f2440,
slate #1b3a5c, steel #64748b, mist #94a3b8, light #e2e8f0, and signal
#ff6b35.
Design rules
The tokens are shaped so these are the easy path. Hold compositions to them too.
- Surfaces are solid. A fill that text sits on is always an opaque colour.
Translucency is for lines and washes behind artwork only. Where the
prototype had a translucent wash under text, the token is the wash
composited over the card as a solid (
signal.100,signal.950). - No cards inside cards. A card is the top surface. Group content inside it with spacing or a divider, not with a second card.
- Every text/background pair clears WCAG AA, including a label on its own
hover and pressed fill, and every label and line a scene draws on its sea.
bun run check:contrastmeasures the contract pairs plus the state and scene pairs incontrast-pairs.json, in both themes.
Layers
| Layer | Path | Rule |
|---|---|---|
| Primitives | src/primitives/ | Raw values only. The Chart paper families (cream, ink, sand, sage), the brand ramp (navy, slate, steel, mist, signal), status hues, and numeric scales. No UI meaning. |
| Semantic | src/semantic/ (+ src/semantic-dark/) | Role tokens that alias primitives (semantic.color.action.primary → {primitives.color.signal.700}). The dark file swaps colour only. |
| Component | src/component/ (+ src/component-dark/) | Per-component anatomy that aliases semantic, or a primitive where no role fits. The dark directory holds deltas only. |
Values the prototype did not state are marked DERIVED in their
$description, with the measurement that chose them: hover and pressed steps,
solid tints, the strong border, the info colour, and the spacing, size and
type scales, which keep Manuia's steps until ShipTight states its own.
grep -c DERIVED src/primitives/*.json gives the count.
The artwork colours have roles. Vocabulary drawings read the line and fill
semantic roles (default, accent, muted of each) through the
--ds-illustration-* contract. The scenes in @manakind/shiptight-ui also read
component.scene: the sea, the chart grid (ink-alpha.6/mist-alpha.7), the
contours (blue-alpha.30/sky-alpha.20), the land and its coastline
(sand/slate, sand.700/steel), the star, and the chart's drawn lines,
whose light value ink-alpha.56 is DERIVED so a route ahead clears 3:1 on the
sea. See
How illustration lives in the system.
Build outputs
bun run --filter @manakind/shiptight-tokens build writes to build/:
css/primitives.css—--primitives-*css/semantic.css,css/semantic-dark.css—--semantic-*css/components.css,css/components-dark.css—--component-*css/theme.css,css/theme-dark.css—--ds-*(the contract)js/tokens.ts— flat object for runtime accessnative/theme.ts— the typed React Native theme
The build outputs are committed so CI can detect drift.
Usage
import '@manakind/shiptight-tokens/css/primitives';
import '@manakind/shiptight-tokens/css/semantic';
import '@manakind/shiptight-tokens/css/components';
import '@manakind/shiptight-tokens/css/theme';
// Night watch. Emitted under `:root.dark, :root[data-theme='dark']`, so these
// are inert until you set the class or attribute.
import '@manakind/shiptight-tokens/css/semantic-dark';
import '@manakind/shiptight-tokens/css/components-dark';
import '@manakind/shiptight-tokens/css/theme-dark';Loading theme.css is what makes @manakind/ui-core (or
@manakind/shiptight-ui's re-exported components) render in ShipTight's
style. The lower layers are only needed if product code reads tokens directly.
The fonts are not bundled. Load Space Grotesk (600, 700), DM Sans (400, 500, 600), JetBrains Mono (400, 500, 600) and Fraunces (italic) in the app.
JS entry points
| Entry | Contents |
|---|---|
| @manakind/shiptight-tokens/js | Flat object of every token, keyed by its flattened name ('primitives-color-signal-500') |
| @manakind/shiptight-tokens/native | The typed React Native theme: the --ds-* contract as a nested JS object (themes.light.button.primary.bgHover) |
import { themes } from '@manakind/shiptight-tokens/native';
// themes.light (Chart paper) / themes.dark (Night watch)A native base component never imports this directly; it reads the theme
through useTheme(). See
Architecture conventions.
