Skip to content
Agentmart
Submit a tool

design-system-structure

Structures a design system: two-tier tokens, contrast-proven light/dark palettes, the theme provider, makeStyles, text roles, fonts.

Skill by zakariaf

IndexedMIT★ 11
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/zakariaf-design-system-structure

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Structures a design system: two-tier tokens, contrast-proven light/dark palettes, the theme provider, makeStyles, text roles, fonts. Use when touching colours, dark mode, fonts or theme files. Not for components (ui-component-library).

Use cases: design, frontend

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Node.js package

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/design-system-structure

What's inside

Skill name
design-system-structure
Description
Structures a design system: two-tier tokens, contrast-proven light/dark palettes, the theme provider, makeStyles, text roles, fonts. Use when touching colours, dark mode, fonts or theme files. Not for components (ui-component-library).
License
MIT
Instructions
257 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Files (93)

  • SKILL.md
  • default-contrast.json · assets/
  • shared.json · assets/
  • root-layout.tsx · examples/
  • task-summary-card.test.tsx · examples/
  • task-summary-card.tsx · examples/
  • tokens-dtcg-excerpt.json · examples/
  • layout-and-shape-tokens.md · references/
  • motion-tokens.md · references/
  • palettes-and-contrast.md · references/
  • text-and-fonts.md · references/
  • theme-in-react-native.md · references/
  • tokens-and-contract.md · references/
  • check-design-system.mjs · scripts/
  • check-lib.mjs · scripts/
  • check-raw-values.mjs · scripts/
  • app-modules.mjs · scripts/lib/
  • app-source.mjs · scripts/lib/
  • design-contract.mjs · scripts/lib/
  • font-tables.mjs · scripts/lib/
  • palette-rules.mjs · scripts/lib/
  • token-file.mjs · scripts/lib/
  • selftest.mjs · scripts/
  • write-palette.mjs · scripts/
  • design-system.json · templates/contracts/
  • design-system.schema.json · templates/contracts/
  • tokens.json · templates/design/
  • contrast.ts · templates/src/theme/
  • fonts.test.ts · templates/src/theme/
  • fonts.ts · templates/src/theme/
  • make-styles.test.tsx · templates/src/theme/
  • make-styles.ts · templates/src/theme/
  • motion.ts · templates/src/theme/
  • navigation-theme.test.tsx · templates/src/theme/
  • navigation-theme.tsx · templates/src/theme/
  • palette.test.ts · templates/src/theme/
  • palette.ts · templates/src/theme/
  • shadow-styles.test.ts · templates/src/theme/
  • shadow-styles.ts · templates/src/theme/
  • text-locale.tsx · templates/src/theme/
  • theme-context.ts · templates/src/theme/
  • theme-provider.test.tsx · templates/src/theme/
  • theme-provider.tsx · templates/src/theme/
  • theme-set.test.ts · templates/src/theme/
  • theme-set.ts · templates/src/theme/
  • theme-types.ts · templates/src/theme/
  • tokens.ts · templates/src/theme/
  • type-styles.test.ts · templates/src/theme/
  • type-styles.ts · templates/src/theme/
  • use-text-locale.ts · templates/src/theme/
  • use-text-style.test.tsx · templates/src/theme/
  • use-text-style.ts · templates/src/theme/
  • use-theme.ts · templates/src/theme/
  • app-text-balance.test.tsx · templates/src/ui/
  • app-text.android.test.tsx · templates/src/ui/
  • app-text.test.tsx · templates/src/ui/
  • app-text.tsx · templates/src/ui/
  • press-feedback-context.test.tsx · templates/src/ui/
  • press-feedback-context.tsx · templates/src/ui/
  • pressable-surface.android.test.tsx · templates/src/ui/
  • …and 33 more

SKILL.md (first lines)

# Design system structure

Makes every visual value in an Expo app come from one token file through one typed theme: semantic
colour roles in light and dark palettes that are proven readable pair by pair, `makeStyles` and one
`AppText` with type roles on the device pixel grid at every text size, the system font (bundled,
pinned fonts as an opt-in), motion tokens with complete springs, and one press surface whose look is
a token choice. Two checkers prove the theme equals the token file, the navigator and the root view
follow it, and that no raw colour, size, weight, radius, duration, spring, palette import, platform
shadow or second theme hides in the app's code.

## Rules that must hold

1. **The token file is the only source; theme modules copy it.** `design/tokens.json` holds primitives
   (tier 1) and semantic roles (tier 2); `tokens.ts`, `type-styles.ts`, `motion.ts` and `palette.ts`
   copy it, and every mapped export is compared value by value (`theme-mismatch`). A retyped value
   drifts by one digit and nobody notices until a screen looks wrong.
2. **Components read semantic roles only, through the theme.** Colours come from
   `theme.colors.<role>` through `makeStyles` or `useTheme`; no colour literal (hex, `rgb()`, `hsl()`,
   or a named colour such as `color="white"` on a colour prop) or raw radius lives outside `src/theme`
   (`no-color-literal`, `no-raw-radius`), no palette module, token file or `APP_THEMES` is imported
   there (`no-palette-import`), and no second theme (the create-expo-app `constants/theme.ts`, another
   `useTheme`) sits beside it (`second-theme-system`). A colour read any other way is frozen to one
   scheme: it breaks dark mode and every palette flavour at once.
3. **Every palette is generated and proven readable, pair by pair.** `write-palette.mjs` writes
   `palette.ts` only when every pair passes (text 4.5:1; edges, focus and icons 3:1; filled shapes
   3:1 off their ground or edge), with translucent colours painted over their ground first. The
   pairs are the app's `contracts/contrast.json` (accessibility-as-code owns that contract and the
   rule to declare every pair there), or the default pairs until it exists; the core pairs are
   checked whatever the contract says (no exception accepts one), and `palette.test.ts` keeps them
   in Jest. Contrast belongs to pairs, so it is tested as pairs, from one table.
4. **Light and dark are two hand-tuned sets that follow the person on iOS and Android, everywhere.**
   `AppThemeProvider` resolves the saved preference against the phone, mirrors it to `Appearance`
   and paints the root view (`SystemUI.setBackgroundColorAsync`); `NavigationThemeProvider` sits
   inside it and hands the navigator colours mapped from the theme (`navigation-theme-mapped`: never
   a bare `DarkTheme`/`DefaultTheme`); the app config says `userInterfaceStyle: 'automatic'` with no
   `ios` or `android` override saying otherwise, and `expo-system-ui` is installed; a runtime file
   (the routes layout, or `App.tsx` with React Navigation) renders the provider imported from
   `src/theme/theme-provider` (`color-scheme-config`, `theme-provider-behaviour`,
   `theme-provider-mounted`). Miss one and iOS reports light forever, Android ignores the setting,
   or headers and screen backgrounds stay light in dark mode.
5. **A makeStyles factory builds with StyleSheet.create and returns it.** Styles are cached per
   theme object (`make-styles-factory`). That keeps tsc's excess-property check and the style lint
   rules; a plain object loses both.
6. **Text renders only through AppText with a type role or style.** No raw `fontSize` or
   `letterSpacing`, inline or as a branch of a conditional (`no-raw-font-size`, `no-letter-spacing`);
   the templates use the system font (`null` in `SCRIPT_FONTS`, weight by `fontWeight`); a bundled
   family is one face per weight, so never `fontWeight` with it (`no-font-weight`: there is no face to
   choose, Android fakes a bold and iOS swaps files); changing numbers use tabular figures
   (`tabular-digits`); headings set the header role; font scaling caps at 200 % (`MAX_FONT_SCALE`).
7. **Line heights sit on the device pixel grid at the current font scale, and glyphs are never
   nudged.** `lineHeight = snapToPixels(fontSize * ratio * fontScale) / fontScale`, because React
   Native multiplies line heights by the font scale and rounds every text box up to whole pixels
   (`line-height-grid`, `no-rounded-line-height`); no per-size padding, margin or translate on text
   (`no-glyph-nudge`): a nudge breaks every height built on exact boxes.
8. **Tall scripts get their own line heights and an overflow guard from font metrics.** A tall ratio
   is never tighter than the base one and meets the contract's device-proven minimums
   (`tall-script-line-height`); `TALL_SCRIPT_METRICS` equals the TTF's hhea values and AppText pads
   the line from them at the drawn size (style times font scale; shifted on iOS, grown on Android)
   inside a wrapper that keeps the testID (`tall-script-overflow-guard`). iOS otherwise clips the
   tops of tall glyphs, worst at the large text sizes people choose to read better.
9. **Display text wraps balanced.** AppText runs `useBalancedWrap` for display text; the hook
   updates state functionally and probes only narrower widths (`balanced-display`). The obvious
   search passes Jest and stalls on a device.
10. **The contract declares the fonts: `"system"` by default; bundled files (opt-in) are pinned and
    named once.** With files listed, one `expo-font` plugin entry embeds every one (`font-plugin`);
    file name = PostScript name = `fontFamily` (`font-family`), because Android finds a font by file
    name and iOS by the name inside it; each file's sha256 is recorded and its licence ships
    (`font-files`); `fonts.ts` and the contract agree either way (`font-family`); with a bundled
    Latin family every script of the app's languages has its own family or an explicit `null`
    (`font-script-coverage`). Expo's object syntax (one family name plus `fontWeight`) is the
    fenced alternative, `fonts.weightBy: "fontWeight"`.
11. **One press surface; its look is a token choice, and persistent states move by layout.**
    `PressableSurface` owns the press look (`PRESS.kind`: `opacity` by default, `scale`, or the
    opt-in `sink`), the Android ripple, the disabled style, the feedback seam and the accessibility
    state. A sinking key that stays pushed in moves with `top: elevation`, never a transform
    (`pressed-by-layout`, checked wherever the sink look exists), because VoiceOver, TalkBack and
    Maestro report layout frames. Static shadows use `shadow()`/`ring()` (boxShadow); legacy
    `shadow*` and `elevation` keys are banned (`no-legacy-shadow`): `shadowOffset`, `shadowOpacity`
    and `shadowRadius` draw on iOS only, `elevation` is Android's own shadow, and `shadowColor`
    only tints it on Android 9+.
12. **Motion comes from tokens, springs are complete, and every moment has a reduce-motion
    alternative.** No duration or delay number in an animation (`no-raw-duration`: `withTiming`,
    `withDelay`, `withRepeat`, `Animated.timing`, `LayoutAnimation`, `.duration(n)` layout-animation
    builders, CSS transition keys) and no spring numbers in a component (`no-raw-spring`): inline, in
    a same-file const (`{ duration: FADE_MS }`, `.duration(SLIDE)`, `withSpring(1, BOUNCE)`) or inside
    an expression (`isSlow ? 600 : 200`, `index * 50`); every spring token writes damping,
    stiffness and mass (`spring-config-complete`: Reanimated 4.5.1 defaults mass to 4); functional
    timers never shorten. Domain layers (`src/core`, `src/data` ...) are not scanned: a task's
    duration is data.
13. **Colour is never the only signal, and a colour-blind variant must change colours.** Shape and
    text carry meaning (accessibility-as-code checks the cues); a `colorBlind` palette that changes
    no primary or status colour is refused (`palette-colorblind`). Brand-invariant roles stay equal
    in every palette (`palette-invariant`).

Visual policies such as flat paint, a maximum radius or the sinking key look are the app's choice,
switched on in the contract or the tokens (`no-gradient`, `no-pill`, `PRESS.kind`), not library rules.

## Workflow

1. **Detect first.** If `package.json` has another styling system (NativeWind, Tamagui, Restyle,
   Unistyles, styled-components) and no `src/theme`, the checkers print NOT APPLICABLE: extend that
   system in its own idiom (Expo's `expo-design-system` skill) and never add a second one. A
   create-expo-app project's `src/constants/theme.ts`, `src/hooks/use-theme.ts`,
   `use-color-scheme*.ts`, `themed-text.tsx` and `themed-view.tsx` are values to migrate, not a
   system to keep: map them into the token file and delete them (tokens-and-contract.md, "Adopting
   the create-expo-app theme"). Routes in `app/` at the repo root are found; an app without `src/`
   adds `rn-skills.config.json` `{ "srcDir": "." }` (tokens-and-contract.md, "Layout keys").
2. **Set up the theme (once).** Read [references/tokens-and-contract.md](references/tokens-and-contract.md),
   [references/layout-and-shape-tokens.md](references/layout-and-shape-tokens.md) (spacing and layout
   steps, the edge-to-edge screen frame, wide windows, strokes, elevation) and
   [references/theme-in-react-native.md](references/theme-in-react-native.md). Copy
   `templates/src/theme/`, `templates/src/ui/`, `templates/contracts/` and `templates/design/` to the
   same paths in the app. Replace the demo values in `design/tokens.json` with the design's, then
   copy them into `tokens.ts`, `type-styles.ts` and `motion.ts` (the token file first, always),
   including the press look (`press` in the token file, `PRESS` in `tokens.ts`).
3. **Write the palette.** Read [references/palettes-and-contrast.md](references/palettes-and-contrast.md).
   Run `node ${CLAUDE_SKILL_DIR}/scripts/write-palette.mjs . --dry-run` until it prints
   `RESULT: PASS`, then without `--dry-run`. When a component puts a colour on a new ground, declare
   the pair in `contracts/contrast.json` (accessibility-as-code's template, or the shape in the

From zakariaf/ReactNative-Skills, MIT.

More from zakariaf/ReactNative-Skills

Signals

Stars
11
Forks
2
Open issues
0
Last commit (this folder)
Oct 17, 2026 (12 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from zakariaf

Cite this page

design-system-structure skill. Agent Mart. https://agentmart.dev/skills/zakariaf-design-system-structure (last updated 2026-10-24)