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
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.
About
Use cases: design, frontend
Reviews
Write a reviewNo 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 theFrom zakariaf/ReactNative-Skills, MIT.
More from zakariaf/ReactNative-Skills
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
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
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
admob-ads-and-consent
zakariaf
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9
Cite this page
design-system-structure skill. Agent Mart. https://agentmart.dev/skills/zakariaf-design-system-structure (last updated 2026-10-24)