ux-ui-agent-skills 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/design-code/SKILL.md +11 -0
- package/.claude/skills/design-qa/SKILL.md +20 -0
- package/.claude/skills/figma-integration/SKILL.md +21 -0
- package/.claude/skills/governance/SKILL.md +21 -0
- package/.claude/skills/performance/SKILL.md +20 -0
- package/.claude/skills/prototype/SKILL.md +6 -0
- package/.claude/skills/redesign/SKILL.md +4 -1
- package/.claude/skills/token-build/SKILL.md +20 -0
- package/.claude/skills/ux-writing/SKILL.md +7 -0
- package/CLAUDE.md +26 -2
- package/README.md +30 -5
- package/components/atoms.md +10 -6
- package/components/data-viz.md +2 -0
- package/components/organisms.md +2 -0
- package/docs/WORKFLOW.md +171 -0
- package/examples/golden/Button.tsx +49 -0
- package/examples/golden/Modal.tsx +79 -0
- package/examples/golden/README.md +25 -0
- package/examples/golden/theme.css +77 -0
- package/package.json +6 -3
- package/scripts/lint_hardcodes.py +116 -0
- package/scripts/lint_taste.py +85 -0
- package/scripts/validate_component_spec.py +57 -0
- package/scripts/validate_contrast.py +148 -0
- package/scripts/validate_theme_refs.py +81 -0
- package/scripts/validate_tokens.py +8 -4
- package/tokens/colors.json +9 -7
- package/workflows/performance.md +1 -1
|
@@ -19,3 +19,14 @@ Render components into a target framework via the adapter system.
|
|
|
19
19
|
|
|
20
20
|
## Output rules (mandatory)
|
|
21
21
|
Use tokens (never hardcode) · include a11y on every interactive element · handle all applicable of the 8 states · support dark mode at the semantic layer · mobile-first · honor reduced motion · **deliver complete files, no placeholders** (`workflows/redesign-audit.md` → Output Completeness).
|
|
22
|
+
|
|
23
|
+
## Verification (mandatory before declaring done)
|
|
24
|
+
Code is the highest-stakes output — self-check every time:
|
|
25
|
+
1. **No hardcoded values** — every color/size/radius/shadow/duration/**font** traces to a token (CSS var / theme key / asset). Run `scripts/lint_hardcodes.py` over the output; zero raw hex/px/ms AND zero raw Tailwind palette utilities (`bg-gray-500`, `text-blue-600`) — use semantic utilities/tokens (`bg-surface`, `text-primary`). Run `scripts/validate_theme_refs.py` so every `var(--…)` resolves to a defined theme token (no floating tokens). One allowed exception: 3rd-party theme-config (MUI/Mantine) mapping our tokens INTO their API.
|
|
26
|
+
2. **All applicable states present** — Default, Hover, Focus(-visible ring), Active, Disabled, Loading(+`aria-busy`), Error, Selected — or justified N/A.
|
|
27
|
+
3. **Accessibility wired** — correct role/ARIA per `accessibility/aria-patterns.md`, keyboard model, focus management, ≥24px target; verify contrast (`scripts/contrast.py`) for any new color pair.
|
|
28
|
+
4. **Dark mode + reduced motion + responsive** — semantic tokens swap; motion has a `prefers-reduced-motion` fallback; layout is mobile-first.
|
|
29
|
+
5. **Completeness** — full files, no `// ...`; if asked for N, deliver N. If any check fails, fix before returning (run `a11y-audit` if unsure).
|
|
30
|
+
6. **Single-theme consistency** — consume the project's ONE shared token theme (the root CSS-var layer); never define a per-page palette or new colors. Across multiple pages/screens, the same semantic tokens must drive every surface so the whole product stays visually identical and themeable from one place (CLAUDE.md → Single-Theme Consistency).
|
|
31
|
+
7. **One shared primitive layer** — build ONE reusable component per atom (`Button`, `Input`, `Modal`, `Badge` via `cva`/equivalent); never repeat utility-class clusters inline across files or hand-roll a div-as-modal per screen. Overlays reuse the single `Modal` primitive: focus trap, `role="dialog"`, `aria-modal="true"`, `aria-labelledby`, Escape, **return focus on close**, backdrop (WCAG 2.4.3 + 2.1.2). See `examples/golden/Button.tsx` + `examples/golden/Modal.tsx`.
|
|
32
|
+
8. **Font loading** — NEVER `@import` web fonts in CSS (render-blocking). Use a framework loader (`next/font`) or `<link rel="preconnect">` + `<link rel="preload">` with `font-display: swap`; self-host when possible. Font family comes from a token (`--font-sans`), never a literal.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-qa
|
|
3
|
+
description: Set up or run design QA gates — token + hardcoded-value lint, automated a11y (axe), contrast, visual regression across variants/states/themes/RTL, and the manual a11y checklist. Use when the user wants CI quality gates, to prevent design regressions, or to QA a component/screen before shipping.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Design QA
|
|
7
|
+
|
|
8
|
+
Stand up the automated + manual gates that stop quality from regressing.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Read `workflows/design-qa.md` (the QA pyramid: token/lint gates → automated a11y → visual regression → manual a11y).
|
|
12
|
+
2. Wire the **fast gates** first (every commit/PR): `python3 scripts/validate_tokens.py`, `python3 scripts/validate_contrast.py` (batch WCAG over the token pairs), and `python3 scripts/lint_hardcodes.py <src>` (no raw hex/px/timing in component code). The repo's `.github/workflows/ci.yml` runs these.
|
|
13
|
+
3. Add **automated a11y** (axe-core / Pa11y) over each component's states (error/loading/disabled/expanded/selected), zero serious/critical to merge.
|
|
14
|
+
4. Add **visual regression** snapshots across variants × sizes × states × light/dark + key breakpoints + RTL; freeze animations + deterministic data.
|
|
15
|
+
5. Sign off the **manual a11y** checklist per release (keyboard, screen reader, 400% reflow, 200% text-spacing, reduced-motion, forced-colors — `accessibility/*`).
|
|
16
|
+
|
|
17
|
+
## Verification (definition of done)
|
|
18
|
+
- An unreviewed PR cannot introduce an unresolved token alias, a contrast failure, a raw hex/px, or an axe violation — a gate blocks each.
|
|
19
|
+
- Snapshots cover dark mode + RTL + the semantic-changing states, not just the happy path.
|
|
20
|
+
- Manual a11y checklist signed off for the release.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-integration
|
|
3
|
+
description: Keep Figma and code in sync — map the 3-tier DTCG tokens to Figma Variables (collections + modes), sync in either direction, use the Figma MCP when connected, and verify component parity (variants/states). Use when the user wants to push tokens/components to Figma, pull a design into code, set up token↔Variable sync, or check design-code drift.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Figma Integration
|
|
7
|
+
|
|
8
|
+
Bridge design (Figma) and code (this repo) in both directions. The token JSON stays the source of truth; Figma Variables mirror it.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Read `workflows/figma-integration.md` (token↔Variable mapping, sync directions, MCP usage, parity).
|
|
12
|
+
2. Map the 3-tier hierarchy → three Figma collections (Primitives → Semantic → Component) with aliasing; dark/brand/density → Figma **Modes** (`tokens/theming.json`).
|
|
13
|
+
3. Choose ONE authoritative sync direction (code→Figma publish, or Figma→code extract via Tokens Studio / Variables REST API). The other side is generated — never hand-edit both.
|
|
14
|
+
4. **If a Figma MCP server is connected:** prefer its tools/skills (load its mandatory prerequisite skill first). Use it to read frames/variables/screenshots into code, build Variables/components from our tokens, or wire Code Connect to `components/*`.
|
|
15
|
+
5. Check component parity: Figma variants/properties must cover our variants + sizes + the 8 states; flag gaps.
|
|
16
|
+
|
|
17
|
+
## Verification (definition of done)
|
|
18
|
+
- Every Figma Variable resolves to a token in `tokens/*.json` (no orphan hex in designs).
|
|
19
|
+
- One authoritative direction; the generated side has zero hand edits.
|
|
20
|
+
- Variant sets cover all 8 states; Code Connect points to the right `components/*` file.
|
|
21
|
+
- After any token import: `python3 scripts/validate_tokens.py` passes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: governance
|
|
3
|
+
description: Govern how the design system evolves — SemVer for tokens/components, the contribution workflow, deprecation policy, and change communication. Use when the user wants to add/promote/deprecate a component or token, decide a version bump, set up a contribution process, or keep the system from fragmenting.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Governance
|
|
7
|
+
|
|
8
|
+
Keep the system consistent as it grows. Apply versioning, contribution, and deprecation rules.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Read `workflows/governance.md` (SemVer table, contribution workflow, deprecation policy, change comms).
|
|
12
|
+
2. Classify the change: **major** (breaking — renamed/removed token or prop, changed anatomy/default), **minor** (additive — new token/component/variant/optional prop), **patch** (fix — contrast/bug/doc/value tweak in tolerance).
|
|
13
|
+
3. For a **new** component/token: confirm it serves a real, repeated need (≥ 2 places) before promoting product → candidate → core. Design it to the full quality bar (CLAUDE.md Component Quality Bar).
|
|
14
|
+
4. For a **deprecation**: mark with reason + replacement + removal version; keep working ≥ 1 minor cycle; provide a migration map (`design-systems/crosswalk.md` style); remove only in a major.
|
|
15
|
+
5. Wire any new file into `CLAUDE.md` (File Reference Map + relevant table/router) and add a changelog entry.
|
|
16
|
+
|
|
17
|
+
## Verification (definition of done)
|
|
18
|
+
- Change has a SemVer level **and** a changelog entry.
|
|
19
|
+
- Removals have a deprecation window, a replacement, and a migration table.
|
|
20
|
+
- New spec meets the 8-state + a11y + token-mapping bar and is reachable via the router.
|
|
21
|
+
- `python3 scripts/validate_tokens.py` passes; contrast re-checked if colors changed.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance
|
|
3
|
+
description: Optimize UI performance against Core Web Vitals — LCP, INP, CLS — with loading/code-split strategy, layout-shift prevention, and animation performance rules. Use when the user wants to improve speed, fix jank or layout shift, hit Web Vitals budgets, or make a UI feel fast on low-end devices.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Performance
|
|
7
|
+
|
|
8
|
+
Make the UI fast and stable. Treat performance as an accessibility concern — slow/janky UIs fail low-end devices first.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Read `workflows/performance.md` (Core Web Vitals targets, loading strategy, layout-shift, animation perf, design-system runtime cost).
|
|
12
|
+
2. Diagnose against budgets: **LCP ≤ 2.5s**, **INP ≤ 200ms**, **CLS ≤ 0.1**. Measure on a mid-tier mobile profile (throttle slow 4G + 4× CPU).
|
|
13
|
+
3. Loading: render above-fold first (SSR/Server Components), code-split by route + heavy widget, lazy-load below-fold/behind-interaction (Astro islands / Qwik), preload one critical font weight, modern responsive images.
|
|
14
|
+
4. Kill layout shift: size skeletons to final dimensions, reserve media space via `aspect-ratio` (`tokens/sizing.json`), never inject content above existing.
|
|
15
|
+
5. Animation: only `transform`/`opacity`, `will-change` sparingly, 100–300ms (`tokens/motion.json`), honor `prefers-reduced-motion`. Prefer CSS state styling over JS for low INP; tree-shake/per-component imports.
|
|
16
|
+
|
|
17
|
+
## Verification (definition of done)
|
|
18
|
+
- Lighthouse / field data meets LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1 on mid-tier mobile.
|
|
19
|
+
- First interaction stays responsive under slow-4G + 4× CPU throttle.
|
|
20
|
+
- Skeletons + media reserve space (zero CLS on load); no non-compositor animation in loops.
|
|
@@ -17,3 +17,9 @@ Guide work through the right fidelity level with validation.
|
|
|
17
17
|
|
|
18
18
|
## Output
|
|
19
19
|
The artifact at the chosen fidelity + an explicit "what we validate next" plan.
|
|
20
|
+
|
|
21
|
+
## Verification (before declaring done)
|
|
22
|
+
- The fidelity matches the question being answered — no level skipped.
|
|
23
|
+
- Flows include decision points, **error paths, and edge cases** (empty/loading/overflow), not just the happy path.
|
|
24
|
+
- A concrete validation step is named (tasks + success criteria), not "test later".
|
|
25
|
+
- High-fi/code artifacts pass the same token + a11y bar as `design-code` (no hardcoded values, contrast, states).
|
|
@@ -13,7 +13,10 @@ Audit-first redesign that preserves behavior.
|
|
|
13
13
|
3. **Diagnose** with the Banned Defaults checklist (`taste/design-taste.md`) + the review rubric (`design-review` skill). Produce a prioritized findings table.
|
|
14
14
|
4. **Direct**: choose an archetype/system (`apply-aesthetic` skill) that fits the brand.
|
|
15
15
|
5. **Apply** in order — tokens first, then typography/spacing, then component states, then motion — without changing routes/data/markup semantics (except a11y fixes).
|
|
16
|
-
6. **Verify**: re-run `design-review` + `a11y-audit`; smoke-test every previously working flow; dark mode + responsive spot-check.
|
|
16
|
+
6. **Verify**: re-run `design-review` + `a11y-audit`; smoke-test every previously working flow; dark mode + responsive spot-check. Run `scripts/validate_contrast.py` on the token source and `scripts/lint_hardcodes.py` over the changed code.
|
|
17
17
|
|
|
18
18
|
## Guardrails
|
|
19
19
|
Never sacrifice a working feature for aesthetics. Never ship a brand color that fails contrast. Never remove existing accessibility affordances. Deliver complete files — no placeholders.
|
|
20
|
+
|
|
21
|
+
## Single-theme consistency (critical for multi-page apps)
|
|
22
|
+
Consolidate to ONE shared token theme and make **every page** consume it — a redesign that leaves different pages on different palettes has failed. Replace per-page/ad-hoc colors with semantic tokens; verify with `scripts/lint_hardcodes.py` that no page reintroduces off-theme values. Theme switches must come from the single token source, not page edits (CLAUDE.md → Single-Theme Consistency).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: token-build
|
|
3
|
+
description: Set up or run the token build pipeline — transform the DTCG tokens/*.json (source of truth) into platform artifacts (CSS variables, Tailwind @theme, JS/TS, iOS Asset Catalog, Android, Compose) with Style Dictionary / Tokens Studio / W3C DTCG export. Use when the user wants to generate platform theme files from tokens, wire token CI, or multi-platform token output.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Token Build
|
|
7
|
+
|
|
8
|
+
Turn the DTCG token source of truth into platform-ready outputs. Tokens are authored once; every platform output is generated.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Read `workflows/token-build.md` (architecture, tool options, resolution rules, output targets, CI).
|
|
12
|
+
2. Pick the tool: **Style Dictionary** (multi-platform, the default), **Tokens Studio** (Figma-owned tokens — pairs with `figma-integration`), W3C DTCG export, or a small custom script (model on `scripts/validate_tokens.py`).
|
|
13
|
+
3. Honor the resolution rules: resolve aliases to final values per platform; expose semantic + component tokens (primitives stay internal); emit base + dark/brand/density overrides (`tokens/theming.json`) as deltas only; format by `$type`.
|
|
14
|
+
4. Generate the requested target(s): CSS `:root` vars, Tailwind v4 `@theme`, typed JS/TS, iOS Asset Catalog + `Color.DS`/`Spacing`, Android `colors.xml`/Compose theme.
|
|
15
|
+
5. Wire CI: on `tokens/*.json` change, run `scripts/validate_tokens.py`, regenerate, fail if committed artifacts are stale; gate colors with `scripts/contrast.py`.
|
|
16
|
+
|
|
17
|
+
## Verification (definition of done)
|
|
18
|
+
- `python3 scripts/validate_tokens.py` passes (no unresolved aliases).
|
|
19
|
+
- Regenerating produces no diff vs. committed artifacts.
|
|
20
|
+
- Dark/brand/density outputs contain only deltas, not full duplicates.
|
|
@@ -19,3 +19,10 @@ Produce or critique interface copy in the project's voice.
|
|
|
19
19
|
|
|
20
20
|
## Output
|
|
21
21
|
Final copy (or a redline review) that reads naturally aloud and passes the checklist. Keep within any character limits for tight UI.
|
|
22
|
+
|
|
23
|
+
## Verification (mandatory before declaring done)
|
|
24
|
+
Run every line through the 10-item pre-ship checklist in `content/voice-tone.md` — do not skip it:
|
|
25
|
+
- Reads naturally **aloud**; frontloaded verb on actions; no jargon/blame/dead-ends.
|
|
26
|
+
- Errors follow what→why→how; empty states give value→action; no bare "No data"/"Error".
|
|
27
|
+
- Mechanics: sentence case, numerals, labels (not placeholders), no color/direction-only cues, inclusive language.
|
|
28
|
+
- Within character limits; translatable (no concatenation — see `accessibility/i18n-rtl.md`).
|
package/CLAUDE.md
CHANGED
|
@@ -36,6 +36,15 @@ Match the request to the files to load (and the runnable skill, invocable via `/
|
|
|
36
36
|
| Map to/from another design system | `migrate-design-system` | `design-systems/interop-protocol.md` + `crosswalk.md` |
|
|
37
37
|
| Prototype / wireframe / user flow / usability test | `prototype` | `workflows/prototyping.md` |
|
|
38
38
|
| Write/review UI copy | `ux-writing` | `content/voice-tone.md` |
|
|
39
|
+
| Versioning / contribution / deprecation / add-or-promote a component | `governance` | `workflows/governance.md`; `scripts/validate_tokens.py` |
|
|
40
|
+
| Token build pipeline → CSS/Tailwind/iOS/Android (Style Dictionary, DTCG) | `token-build` | `workflows/token-build.md`; `scripts/validate_tokens.py` |
|
|
41
|
+
| Figma ↔ code sync, Variables, Code Connect, Figma MCP | `figma-integration` | `workflows/figma-integration.md` |
|
|
42
|
+
| QA gates / CI / visual regression / prevent regressions | `design-qa` | `workflows/design-qa.md`; `scripts/validate_contrast.py`, `scripts/lint_hardcodes.py` |
|
|
43
|
+
| Performance / Core Web Vitals / jank / layout shift | `performance` | `workflows/performance.md` |
|
|
44
|
+
| Charts / data-viz / chart colors | `design-component` | `components/data-viz.md`, `tokens/data-viz.json` |
|
|
45
|
+
| Calendar / Carousel / Tree | `design-component` | `components/data-display.md` |
|
|
46
|
+
| Icon system / icon sizing / icon a11y | `design-component` | `components/icon-system.md` |
|
|
47
|
+
| Cognitive a11y / i18n-RTL / low-vision / WCAG AAA | `a11y-audit` | `accessibility/cognitive.md`, `accessibility/i18n-rtl.md`, `accessibility/vision.md`, `accessibility/wcag-aaa.md` |
|
|
39
48
|
|
|
40
49
|
---
|
|
41
50
|
|
|
@@ -132,7 +141,7 @@ Examples: `semantic.text.primary`, `component.button.primary-bg-hover`, `semanti
|
|
|
132
141
|
|---------|--------------|---------|
|
|
133
142
|
| Normal text (< 24px) | 4.5:1 | `text.primary` on `surface.page` = 15.4:1 ✓ |
|
|
134
143
|
| Large text (≥ 24px or ≥ 18.66px bold) | 3:1 | `text.secondary` on `surface.page` = 5.7:1 ✓ |
|
|
135
|
-
| UI components & graphical objects | 3:1 | `border.
|
|
144
|
+
| UI components & graphical objects | 3:1 | `border.strong` on `surface.page` = 4.8:1 ✓ (use for essential control borders). `border.default` = 1.2:1 is decorative-only (dividers/card edges) |
|
|
136
145
|
| Focus indicators | 3:1 | Focus ring uses `shadow.focus-ring` double ring |
|
|
137
146
|
|
|
138
147
|
### Color Usage Rules
|
|
@@ -149,6 +158,16 @@ Use **OKLCH color space** for perceptually uniform shade scales:
|
|
|
149
158
|
3. Verify 500 shade meets 4.5:1 contrast on white for text use
|
|
150
159
|
4. Verify 600 shade meets 3:1 contrast on white for UI use
|
|
151
160
|
|
|
161
|
+
### Single-Theme Consistency (cross-page — non-negotiable)
|
|
162
|
+
Every page, screen, and component in a project MUST render from **one shared token theme** — never a per-page palette or ad-hoc colors. This is what keeps a 50-screen product visually identical and themeable from one place.
|
|
163
|
+
|
|
164
|
+
1. **One source of truth** — the project's `tokens/*.json` → a single CSS-variable layer (`:root` + `[data-theme="dark"]`) imported **once** at the app root. Every page references the same semantic tokens; none redefines colors.
|
|
165
|
+
2. **No off-theme values** — zero hardcoded hex/px/timing in component/page code. Enforced by `scripts/lint_hardcodes.py` (the one allowed exception: adapter theme-config that maps our tokens *into* a 3rd-party API, e.g. MUI/Mantine).
|
|
166
|
+
3. **Real WCAG, on the source** — the token theme itself passes WCAG 2.2 in **both** light and dark before any page ships. Enforced by `scripts/validate_contrast.py` (required text/action pairs fail the build; tertiary/decorative are advisory).
|
|
167
|
+
4. **One CI enforces all of it** — `.github/workflows/ci.yml` runs `validate_tokens` + `validate_contrast` + `validate_component_spec` + `npm test` on every push/PR. A page that introduces drift, a contrast regression, or an off-theme color cannot merge.
|
|
168
|
+
|
|
169
|
+
> Switching brand/theme = editing the token source once → every page updates. If a page "looks different," it's a bug: it bypassed the theme.
|
|
170
|
+
|
|
152
171
|
---
|
|
153
172
|
|
|
154
173
|
## Typography Guidelines
|
|
@@ -493,5 +512,10 @@ frameworks/
|
|
|
493
512
|
.claude/skills/ ← Runnable skills (invoke via /name): design-tokens, design-component,
|
|
494
513
|
design-code, design-review, a11y-audit, apply-aesthetic, redesign,
|
|
495
514
|
migrate-design-system, prototype, ux-writing
|
|
496
|
-
scripts/ ← validate_tokens.py · contrast.py ·
|
|
515
|
+
scripts/ ← validate_tokens.py · contrast.py · validate_contrast.py (batch WCAG, light+dark)
|
|
516
|
+
· validate_component_spec.py · lint_hardcodes.py (hex/px/ms + Tailwind palette + font)
|
|
517
|
+
· validate_theme_refs.py (every var(--…) resolves to the theme) · lint_taste.py
|
|
518
|
+
· design_systems.py · scaffold_component.py
|
|
519
|
+
.github/workflows/ ← ci.yml (quality gates: tokens + contrast + spec + npm test on push/PR)
|
|
520
|
+
· release.yml (auto GitHub Release + npm publish on tag)
|
|
497
521
|
```
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A comprehensive kit of structured instructions, design tokens, runnable skills,
|
|
|
8
8
|
|
|
9
9
|
<br>
|
|
10
10
|
|
|
11
|
-
[](https://github.com/plugin87/ux-ui-agent-skills/releases)
|
|
12
12
|
[](#-license)
|
|
13
13
|
[](#-accessibility-standards)
|
|
14
14
|
|
|
@@ -17,7 +17,7 @@ A comprehensive kit of structured instructions, design tokens, runnable skills,
|
|
|
17
17
|
[](https://www.npmjs.com/package/ux-ui-agent-skills)
|
|
18
18
|
[](https://www.npmjs.com/package/ux-ui-agent-skills)
|
|
19
19
|

|
|
20
|
-

|
|
21
21
|

|
|
22
22
|

|
|
23
23
|

|
|
@@ -32,7 +32,7 @@ A comprehensive kit of structured instructions, design tokens, runnable skills,
|
|
|
32
32
|
|
|
33
33
|
## 📌 Version
|
|
34
34
|
|
|
35
|
-
**Current release: `
|
|
35
|
+
**Current release: `v2.0.0`** · See the [Changelog](#-changelog) · [All releases](https://github.com/plugin87/ux-ui-agent-skills/releases)
|
|
36
36
|
|
|
37
37
|
> No build tools, dependencies, or runtime required — this is a pure instruction & knowledge layer for AI agents.
|
|
38
38
|
|
|
@@ -100,6 +100,8 @@ cp -r ux-ui-agent-skills/ your-project/
|
|
|
100
100
|
|
|
101
101
|
## 🎮 How to Use
|
|
102
102
|
|
|
103
|
+
> 📖 **New here?** Read [**docs/WORKFLOW.md**](docs/WORKFLOW.md) for the full end-to-end picture — how the Request Router loads layers on demand, real usage scenarios, and the automated release pipeline.
|
|
104
|
+
|
|
103
105
|
There are **three ways** to drive the kit. Use whichever fits the moment.
|
|
104
106
|
|
|
105
107
|
### 1. Just ask (zero commands)
|
|
@@ -142,12 +144,17 @@ Plain `python3` — useful in the terminal or CI:
|
|
|
142
144
|
|
|
143
145
|
```bash
|
|
144
146
|
python3 scripts/validate_tokens.py # validate token JSON + alias refs
|
|
145
|
-
python3 scripts/
|
|
147
|
+
python3 scripts/validate_contrast.py # batch WCAG gate: token pairs, light + dark
|
|
148
|
+
python3 scripts/contrast.py "#1d1d1f" "#ffffff" # WCAG contrast ratio for one pair
|
|
149
|
+
python3 scripts/validate_component_spec.py # every component spec is complete
|
|
150
|
+
python3 scripts/lint_hardcodes.py src/ # no off-theme hex/px/timing (consistency)
|
|
151
|
+
python3 scripts/lint_taste.py page.html # heuristic anti-slop taste check
|
|
146
152
|
python3 scripts/design_systems.py list # browse the 138-system library
|
|
147
|
-
python3 scripts/design_systems.py show apple # inspect one system
|
|
148
153
|
python3 scripts/scaffold_component.py "Date Picker" # emit a component spec stub
|
|
149
154
|
```
|
|
150
155
|
|
|
156
|
+
These are the same gates CI runs (`.github/workflows/ci.yml`) — token validity, **WCAG contrast in light + dark**, spec completeness, and zero hardcoded values — so theme/color stays consistent across every page and accessibility is enforced, not assumed.
|
|
157
|
+
|
|
151
158
|
### Typical flow
|
|
152
159
|
|
|
153
160
|
```text
|
|
@@ -315,6 +322,24 @@ This is a **starter kit** — make it yours:
|
|
|
315
322
|
|
|
316
323
|
## 📝 Changelog
|
|
317
324
|
|
|
325
|
+
### `v2.0.0` — Enforcement layer (breaking)
|
|
326
|
+
|
|
327
|
+
> **Breaking:** dark-mode token values changed (link, primary action) and `border.strong` now meets 3:1 — re-verify any snapshots/visual tests. The kit moves from *advisory* guidance to *enforced* gates.
|
|
328
|
+
|
|
329
|
+
**Theme consistency + real WCAG, enforced (driven by real-world audit feedback):**
|
|
330
|
+
- 🎨 **Single-theme consistency** — every page renders from ONE shared token theme; CLAUDE.md rule + `examples/golden/` (theme.css with full color/type/spacing/breakpoint tokens, Button.tsx, Modal.tsx). `design-code`/`redesign` require consuming the one theme — no per-page palettes.
|
|
331
|
+
- 🚫 **Hardcode linter catches real drift** — `scripts/lint_hardcodes.py` now flags raw hex/px/ms, **raw Tailwind palette utilities** (`bg-gray-500`, `text-blue-600`), and literal `font-family` (the 527-hardcode problem).
|
|
332
|
+
- 🔗 **No floating tokens** — `scripts/validate_theme_refs.py` proves every `var(--…)` a component uses is defined in the theme (precision/consistency gate).
|
|
333
|
+
- ✅ **Real WCAG gate** — `scripts/validate_contrast.py` checks required text/action/**border** pairs in **light + dark**; fixed genuine dark-mode contrast bugs (link, primary action) and made `border.strong` meet 3:1 for essential control borders.
|
|
334
|
+
- 🪟 **One Modal primitive** — golden `Modal.tsx` + hardened spec: focus trap, `role="dialog"`, `aria-modal`, return-focus on close (fixes the 0/14-focus-trap class of bug, WCAG 2.4.3 + 2.1.2).
|
|
335
|
+
- 🤖 **One CI enforces all of it** — `.github/workflows/ci.yml` runs tokens + contrast + spec + hardcode + theme-ref + `npm test` on every push/PR. Drift, contrast regressions, off-theme colors, and floating tokens cannot merge.
|
|
336
|
+
- ⚡ **5 new runnable skills** (10 → **15**) — `governance`, `token-build`, `figma-integration`, `design-qa`, `performance`; verification steps added to `design-code`/`prototype`/`ux-writing`; router rows for all newer knowledge.
|
|
337
|
+
- 🧹 `validate_component_spec.py` + `lint_taste.py`; fixed `validate_tokens.py` cross-file aliases; atoms Button/Input document all 8 states.
|
|
338
|
+
|
|
339
|
+
### `v1.2.1`
|
|
340
|
+
- 📖 **Docs** — added [`docs/WORKFLOW.md`](docs/WORKFLOW.md): end-to-end how-it-works guide (Request Router, on-demand layer loading, real usage scenarios, automated release pipeline) + linked from the README
|
|
341
|
+
- 🤖 **CI** — first release shipped fully automatically by the `release.yml` workflow (GitHub Release notes from this changelog + `npm publish --provenance` on tag push)
|
|
342
|
+
|
|
318
343
|
### `v1.2.0`
|
|
319
344
|
- 🧩 **8 new component specs** — `data-display.md` (Calendar, Carousel, Tree) + `data-viz.md` (Bar, Line/Area, Pie/Donut, Sparkline, Scatter) → **50 components**; plus `components/icon-system.md`
|
|
320
345
|
- 📊 **Data-viz tokens** — `tokens/data-viz.json`: color-blind-aware (Okabe–Ito) categorical/sequential/diverging palettes + axis/grid/tooltip → **14 token files**
|
package/components/atoms.md
CHANGED
|
@@ -26,13 +26,15 @@ The primary interactive control for triggering actions.
|
|
|
26
26
|
| md | 40px | 16px × 10px | body-base (16px) | 20px |
|
|
27
27
|
| lg | 48px | 24px × 12px | body-lg (18px) | 24px |
|
|
28
28
|
|
|
29
|
-
**States (
|
|
29
|
+
**States (8):**
|
|
30
30
|
1. **Default** — resting state
|
|
31
31
|
2. **Hover** — cursor over; background shifts one shade
|
|
32
|
-
3. **
|
|
33
|
-
4. **
|
|
32
|
+
3. **Focus** — keyboard focus; apply `focus-ring` shadow token
|
|
33
|
+
4. **Active/Pressed** — mouse down; background shifts two shades
|
|
34
34
|
5. **Disabled** — `opacity: 0.5`, `cursor: not-allowed`, `pointer-events: none`
|
|
35
35
|
6. **Loading** — replace icon with spinner, add `aria-busy="true"`, disable interaction
|
|
36
|
+
7. **Error** — N/A for a standalone button; a submit button reflects form error via the form, not its own style
|
|
37
|
+
8. **Selected** — for toggle/segmented buttons only: `interactive.selected-bg` + `aria-pressed`/`aria-selected`
|
|
36
38
|
|
|
37
39
|
**Accessibility:**
|
|
38
40
|
- Native `<button>` element always; never `<div>`
|
|
@@ -58,13 +60,15 @@ Single-line text entry field.
|
|
|
58
60
|
| md | 40px | 12px × 10px | body-base |
|
|
59
61
|
| lg | 48px | 16px × 12px | body-lg |
|
|
60
62
|
|
|
61
|
-
**States (
|
|
63
|
+
**States (8):**
|
|
62
64
|
1. **Default** — `input.border` (gray-200)
|
|
63
65
|
2. **Hover** — `input.border-hover` (gray-300)
|
|
64
66
|
3. **Focus** — `input.border-focus` (blue-500) + focus ring
|
|
65
|
-
4. **
|
|
67
|
+
4. **Active** — N/A for text entry (no pressed state); typing reflects via the caret/value
|
|
66
68
|
5. **Disabled** — `input.bg-disabled` + reduced opacity
|
|
67
|
-
6. **
|
|
69
|
+
6. **Loading** — async validation/lookup: trailing spinner + `aria-busy="true"`
|
|
70
|
+
7. **Error** — `input.border-error` (red-500) + error icon + message + `aria-invalid`
|
|
71
|
+
8. **Selected** — filled/has-value styling; read-only is a variant of default (non-editable, no hover)
|
|
68
72
|
|
|
69
73
|
**Accessibility:**
|
|
70
74
|
- Always associate with `<label>` via `for`/`id`
|
package/components/data-viz.md
CHANGED
|
@@ -4,6 +4,8 @@ Charts and graphs. Color comes from `tokens/data-viz.json` (color-blind-aware ca
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
**Anatomy (shared):** `[plot area] + [axes + tick labels] + [gridlines] + [series (bars/lines/points)] + [legend] + [tooltip on hover] + [optional title/caption]`. Every chart composes from these regions; color/axis/grid styling comes from `tokens/data-viz.json`.
|
|
8
|
+
|
|
7
9
|
## Universal chart rules
|
|
8
10
|
|
|
9
11
|
1. **Token-driven series color** — map series to `dataviz.categorical.*`; sequences to `dataviz.sequential.*`; +/− to `dataviz.diverging.*` / `dataviz.semantic.*`. Limit to ≤ 8 categorical series; beyond that, group "Other" or switch encoding.
|
package/components/organisms.md
CHANGED
|
@@ -221,6 +221,8 @@ A structured data display with sorting, filtering, pagination, and row actions.
|
|
|
221
221
|
|
|
222
222
|
A focused overlay that interrupts workflow for critical information or action.
|
|
223
223
|
|
|
224
|
+
> **Non-negotiable — build ONE reusable Modal primitive and reuse it for every dialog.** Never hand-roll a div-as-modal per screen. Every instance MUST have: focus trap, `role="dialog"`, `aria-modal="true"`, `aria-labelledby`, Escape-to-close, **focus returned to the trigger on close**, and a backdrop. Missing the focus trap violates WCAG 2.4.3 + 2.1.2. Reference implementation: `examples/golden/Modal.tsx`.
|
|
225
|
+
|
|
224
226
|
**Grid Structure:**
|
|
225
227
|
```
|
|
226
228
|
┌──────────── Overlay ─────────────────┐
|
package/docs/WORKFLOW.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# How It Works — End-to-End Workflow
|
|
2
|
+
|
|
3
|
+
This document explains how the kit operates from both sides: the **consumer** (a designer/developer who installs it into a project and uses it through Claude) and the **maintainer** (who edits the knowledge files and ships releases).
|
|
4
|
+
|
|
5
|
+
> TL;DR — **Easy to use, smart on the inside, automated on release.** There is no runtime, no build step, and no dependency. The kit is a pure knowledge + instruction layer that Claude loads on demand.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. What it actually is
|
|
10
|
+
|
|
11
|
+
This is **not a library you import or build** — it's a "brain" you plug into Claude (knowledge files + an instruction router). Once installed in a project, Claude in that project behaves like a Senior Design Architect: it answers design-system questions with real principles — design tokens, accessibility, and taste — and generates production-ready output.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
A normal UI library This kit
|
|
15
|
+
───────────────────── ─────────────────────────────
|
|
16
|
+
import { Button } you talk to Claude / run /skills
|
|
17
|
+
npm build no build, no runtime
|
|
18
|
+
ships JS/CSS to the browser ships KNOWLEDGE to the model
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. Consumer workflow (real usage)
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
┌─ Install once ─────────────────────────────────────────┐
|
|
27
|
+
│ npx ux-ui-agent-skills init │
|
|
28
|
+
│ → copies the knowledge files + .claude/skills/ in │
|
|
29
|
+
└────────────────────────────────────────────────────────┘
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
You talk to Claude normally, or invoke a /skill
|
|
33
|
+
│
|
|
34
|
+
┌───────────────┴───────────────┐
|
|
35
|
+
▼ ▼
|
|
36
|
+
"build me a Button in React" /design-tokens "make a purple palette"
|
|
37
|
+
│ │
|
|
38
|
+
▼ ▼
|
|
39
|
+
┌─────────────────────────────────────────────────────────┐
|
|
40
|
+
│ Claude reads CLAUDE.md → the "Request Router" │
|
|
41
|
+
│ matches your intent → knows exactly which files to load │
|
|
42
|
+
└─────────────────────────────────────────────────────────┘
|
|
43
|
+
│
|
|
44
|
+
▼
|
|
45
|
+
┌─────────────────────────────────────────────────────────┐
|
|
46
|
+
│ Loads only the relevant LAYERS and composes them: │
|
|
47
|
+
│ tokens/ + components/ + accessibility/ + taste/ │
|
|
48
|
+
│ + frameworks/adapters/ (when you ask for a framework) │
|
|
49
|
+
└─────────────────────────────────────────────────────────┘
|
|
50
|
+
│
|
|
51
|
+
▼
|
|
52
|
+
Output: code / spec / review that is token-driven and
|
|
53
|
+
accessible by default (no hardcoded colors, all 8 states,
|
|
54
|
+
dark-mode aware, ARIA wired).
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### The Request Router is the core
|
|
58
|
+
|
|
59
|
+
Claude does **not** read every file on every request (that would waste context). Instead, `CLAUDE.md` contains a routing table — Claude reads it and pulls only what the task needs:
|
|
60
|
+
|
|
61
|
+
| You ask for… | Claude loads |
|
|
62
|
+
|--------------|--------------|
|
|
63
|
+
| Generate / edit tokens | `tokens/*.json` + `scripts/validate_tokens.py` |
|
|
64
|
+
| A component spec | `components/*` + `accessibility/aria-patterns.md` |
|
|
65
|
+
| Code in any framework | `frameworks/adapter-protocol.md` → the matching adapter |
|
|
66
|
+
| A design review / audit | `workflows/design-review.md` + `taste/` |
|
|
67
|
+
| An accessibility check | `accessibility/*` + `scripts/contrast.py` |
|
|
68
|
+
| A specific look / vibe | `taste/` + one of 138 design systems |
|
|
69
|
+
|
|
70
|
+
This is what makes the kit feel "intelligent" — it self-routes instead of dumping a flat file list into context.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 3. Three real scenarios
|
|
75
|
+
|
|
76
|
+
**A. A developer wants code**
|
|
77
|
+
```
|
|
78
|
+
You: /design-code build a Card component in Vue
|
|
79
|
+
Claude: → router points to frameworks/adapters/vue.md + components/molecules.md
|
|
80
|
+
→ returns a .vue file using CSS-variable tokens, with
|
|
81
|
+
hover/focus/disabled states and dark mode.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**B. A designer wants a specific look**
|
|
85
|
+
```
|
|
86
|
+
You: /apply-aesthetic make it feel like Linear
|
|
87
|
+
Claude: → loads design-systems/library/linear-app + taste/
|
|
88
|
+
→ remaps tokens (color/shadow/radius/font), then re-checks contrast.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**C. QA before shipping**
|
|
92
|
+
```
|
|
93
|
+
You: /a11y-audit does this screen pass WCAG?
|
|
94
|
+
Claude: → accessibility/wcag-checklist.md + runs scripts/contrast.py
|
|
95
|
+
→ returns a table: criterion + severity + exact fix.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The ten runnable skills: `design-tokens`, `design-component`, `design-code`, `design-review`, `a11y-audit`, `apply-aesthetic`, `redesign`, `migrate-design-system`, `prototype`, `ux-writing`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 4. Is it complex or hard to use?
|
|
103
|
+
|
|
104
|
+
| Perspective | Difficulty | Why |
|
|
105
|
+
|-------------|------------|-----|
|
|
106
|
+
| **Consumer (designer/dev)** | 🟢 Very easy | Just talk in plain language or type `/skill`. No config, no build, no API to memorize. The complexity is hidden inside the knowledge files. |
|
|
107
|
+
| **Install** | 🟢 One command | `npx ux-ui-agent-skills init` and you're done. |
|
|
108
|
+
| **Maintainer (releasing)** | 🟢 Easy now | A release is two commands (see §5). |
|
|
109
|
+
| **Contributor (adding a component/adapter)** | 🟡 Moderate | Must follow the house style and wire the file into `CLAUDE.md`. `workflows/governance.md` is the guide. |
|
|
110
|
+
|
|
111
|
+
**Bottom line:** the intelligence lives in the files, so the hard part is *authoring the kit* (already done), not *using it*. Consumers have almost nothing to learn.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 5. Maintainer workflow (the release pipeline)
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
Edit knowledge files → commit
|
|
119
|
+
│
|
|
120
|
+
▼
|
|
121
|
+
npm version patch ← bumps package.json + creates a git tag
|
|
122
|
+
git push origin main --follow-tags ← pushes code + tag
|
|
123
|
+
│
|
|
124
|
+
▼ (tag vX.Y.Z lands on GitHub)
|
|
125
|
+
┌──────────────────────────────────────────────┐
|
|
126
|
+
│ GitHub Action: .github/workflows/release.yml │
|
|
127
|
+
│ job 1 "release" → creates a GitHub Release │
|
|
128
|
+
│ (notes pulled from the │
|
|
129
|
+
│ README changelog block) │
|
|
130
|
+
│ job 2 "publish" → guards tag == pkg version │
|
|
131
|
+
│ → npm publish --provenance │
|
|
132
|
+
│ (uses the NPM_TOKEN │
|
|
133
|
+
│ repo secret) │
|
|
134
|
+
└──────────────────────────────────────────────┘
|
|
135
|
+
│
|
|
136
|
+
▼
|
|
137
|
+
✅ GitHub Release is live + ✅ npm is updated — automatically.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Setup (one-time, already done)
|
|
141
|
+
- **Repo secret `NPM_TOKEN`** = an npm **Automation token** (bypasses 2FA so CI can publish).
|
|
142
|
+
- The workflow also supports **manual backfill**: Actions → *Release on tag* → *Run workflow* → enter an existing tag (this creates the Release only; it does **not** re-publish to npm).
|
|
143
|
+
|
|
144
|
+
### Day-to-day releasing
|
|
145
|
+
```bash
|
|
146
|
+
npm version patch # or minor / major
|
|
147
|
+
git push origin main --follow-tags
|
|
148
|
+
```
|
|
149
|
+
That's the entire release. CI creates the GitHub Release and publishes to npm — no manual `gh` or `npm login`, no 2FA prompts.
|
|
150
|
+
|
|
151
|
+
> Before this pipeline existed, each of these steps was manual (npm login, 2FA, hand-writing release notes, creating the Release page). Now it's two commands.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 6. Mental model
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
CONSUMER SIDE MAINTAINER SIDE
|
|
159
|
+
(uses Claude in a project) (edits this repo, ships versions)
|
|
160
|
+
|
|
161
|
+
talk / /skill ─┐ edit files ─┐
|
|
162
|
+
│ │
|
|
163
|
+
CLAUDE.md (Request Router) npm version + git push
|
|
164
|
+
│ │
|
|
165
|
+
loads layers on demand GitHub Action
|
|
166
|
+
│ │
|
|
167
|
+
token-driven, a11y, on-brand Release page + npm publish
|
|
168
|
+
output (automatic)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Easy at the edges, smart in the middle.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Button.tsx — golden reference. Token-driven, all 8 states, accessible, dark-mode safe.
|
|
2
|
+
// Every value comes from theme.css variables — zero hardcoded colors/sizes (passes lint_hardcodes).
|
|
3
|
+
import { forwardRef, type ButtonHTMLAttributes } from "react";
|
|
4
|
+
|
|
5
|
+
type Props = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
6
|
+
variant?: "primary" | "secondary";
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
selected?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const Button = forwardRef<HTMLButtonElement, Props>(function Button(
|
|
12
|
+
{ variant = "primary", loading = false, selected, disabled, children, ...rest },
|
|
13
|
+
ref
|
|
14
|
+
) {
|
|
15
|
+
return (
|
|
16
|
+
<button
|
|
17
|
+
ref={ref}
|
|
18
|
+
data-variant={variant}
|
|
19
|
+
aria-pressed={selected}
|
|
20
|
+
aria-busy={loading || undefined}
|
|
21
|
+
disabled={disabled || loading}
|
|
22
|
+
className="ds-btn"
|
|
23
|
+
{...rest}
|
|
24
|
+
>
|
|
25
|
+
{loading && <span className="ds-spinner" aria-hidden="true" />}
|
|
26
|
+
{children}
|
|
27
|
+
</button>
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
.ds-btn {
|
|
33
|
+
display: inline-flex; align-items: center; gap: var(--space-2);
|
|
34
|
+
block-size: var(--size-control-md); padding-inline: var(--space-4);
|
|
35
|
+
border-radius: var(--radius-button); border: 0;
|
|
36
|
+
background: var(--color-action-primary); color: var(--color-text-on-action);
|
|
37
|
+
transition: background var(--transition-micro);
|
|
38
|
+
}
|
|
39
|
+
.ds-btn:hover { background: var(--color-action-primary-hover); } /* Hover */
|
|
40
|
+
.ds-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus-ring); } /* Focus */
|
|
41
|
+
.ds-btn:active { filter: brightness(0.95); } /* Active */
|
|
42
|
+
.ds-btn:disabled { opacity: var(--opacity-disabled); pointer-events: none; } /* Disabled */
|
|
43
|
+
.ds-btn[aria-busy="true"] { cursor: progress; } /* Loading */
|
|
44
|
+
.ds-btn[aria-pressed="true"] { background: var(--color-action-primary-hover); }/* Selected */
|
|
45
|
+
.ds-btn[data-variant="secondary"] {
|
|
46
|
+
background: transparent; color: var(--color-text-primary);
|
|
47
|
+
box-shadow: inset 0 0 0 1px var(--color-border-strong);
|
|
48
|
+
}
|
|
49
|
+
*/
|