Skip to content

Commit 6c0db86

Browse files
committed
Add skill for design token validation in components
1 parent 9f6b5f0 commit 6c0db86

1 file changed

Lines changed: 265 additions & 0 deletions

File tree

  • .opencode/skills/token-validation
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
---
2+
name: token-validation
3+
description: Validate OUDS design token usage in SCSS component files - checks that every component token used is present in all three brands (orange, sosh, orange-compact) and detects anti-patterns like hardcoded values, raw tokens, and forbidden CSS patterns
4+
compatibility: opencode
5+
metadata:
6+
audience: developers
7+
workflow: token-audit
8+
---
9+
10+
## What I do
11+
12+
I analyze SCSS component files in the OUDS Web project to:
13+
14+
1. **Cross-brand token presence check**: For every `$ouds-<component>-*` token referenced in a component SCSS file, I verify it is defined in ALL three brand `_component.scss` files:
15+
- `packages/orange/scss/tokens/_component.scss`
16+
- `packages/sosh/scss/tokens/_component.scss`
17+
- `packages/orange-compact/scss/tokens/_component.scss`
18+
19+
2. **Anti-pattern detection**: I flag forbidden patterns in component SCSS files:
20+
- Hardcoded numeric values (e.g. `8px`, `1rem`, `#ff7900`, `rgb(...)`)
21+
- Raw tokens used directly in components (`$core-ouds-*`, `$core-orange-*`, `$core-sosh-*`)
22+
- Forbidden Sass functions (`lighten(`, `darken(`)
23+
- `border: none` (must be `border: 0`)
24+
- Direct `transition:` or `border-radius:` properties without `@include`
25+
26+
## When to use me
27+
28+
Use me when:
29+
30+
- You have added or modified SCSS component files and want to verify token usage is correct across all brands
31+
- You are reviewing a PR that touches `scss/_*.scss` or `scss/forms/_*.scss` files
32+
- You want to audit the full codebase for token-related issues
33+
34+
## Scope
35+
36+
### Files to analyze
37+
38+
Only analyze files in these two directories:
39+
40+
- `scss/_*.scss` (root component files)
41+
- `scss/forms/_*.scss` (form component files)
42+
43+
### Files to EXCLUDE from analysis
44+
45+
These are not component implementations — skip them entirely:
46+
47+
- `scss/_variables.scss`
48+
- `scss/_variables-dark.scss`
49+
- `scss/_config.scss`
50+
- `scss/_functions.scss`
51+
- `scss/_mixins.scss`
52+
- `scss/_maps.scss`
53+
- `scss/_root.scss`
54+
- `scss/_reboot.scss`
55+
- `scss/_utilities.scss`
56+
- `scss/_helpers.scss`
57+
- `scss/_grid.scss`
58+
- `scss/_containers.scss`
59+
- `scss/_images.scss`
60+
- `scss/_type.scss`
61+
- `scss/_transitions.scss`
62+
- `scss/_forms.scss`
63+
64+
### Token files to read (all three brands, mandatory)
65+
66+
- `packages/orange/scss/tokens/_component.scss`
67+
- `packages/sosh/scss/tokens/_component.scss`
68+
- `packages/orange-compact/scss/tokens/_component.scss`
69+
70+
## Step-by-step instructions
71+
72+
### Phase 1: Load all component tokens per brand
73+
74+
Read all three brand `_component.scss` files. For each file, extract every SCSS variable definition that matches the pattern:
75+
76+
```
77+
^\$(ouds-[a-z][a-z0-9-]*):\s
78+
```
79+
80+
This gives you three sets: `tokens_orange`, `tokens_sosh`, `tokens_orange_compact`.
81+
82+
The union of all three sets is the complete list of known component tokens.
83+
84+
### Phase 2: Identify component SCSS files to analyze
85+
86+
Use Glob to list:
87+
88+
- `scss/_*.scss`
89+
- `scss/forms/_*.scss`
90+
91+
Then exclude the files listed in the **Files to EXCLUDE** section above.
92+
93+
If the user specifies a particular component (e.g. "validate button"), only analyze `scss/_buttons.scss`.
94+
95+
### Phase 3: For each component SCSS file
96+
97+
#### 3a. Extract all `$ouds-*` token references
98+
99+
Grep the file for all occurrences of `\$ouds-[a-z][a-z0-9-]*` (excluding the `: ` assignment syntax to avoid re-reading token definitions if the file defines local overrides). Collect unique token names used.
100+
101+
**Important**: A token is "used" if it appears as a value reference (e.g. `$ouds-button-size-min-width`, not as a definition). In practice, component SCSS files do not define `$ouds-*` tokens — they only use them. So any `$ouds-*` occurrence is a usage.
102+
103+
#### 3b. Cross-brand presence check
104+
105+
For each token found in step 3a:
106+
107+
- Check if it exists in `tokens_orange`
108+
- Check if it exists in `tokens_sosh`
109+
- Check if it exists in `tokens_orange_compact`
110+
111+
A token **passes** if it exists in all three.
112+
A token **fails** if it is missing in one or more brands.
113+
114+
#### 3c. Anti-pattern detection
115+
116+
Scan the file content for:
117+
118+
| Anti-pattern | Regex / Pattern | Severity |
119+
| ----------------------------- | --------------------------------------------------------------------------- | -------- |
120+
| Hardcoded pixel/rem/em values | `:\s*[\d.]+(?:px\|rem\|em)` | WARNING |
121+
| Hardcoded hex color | `#[0-9a-fA-F]{3,8}(?!\s*[;,{])` or as a value | WARNING |
122+
| Hardcoded rgb/rgba | `rgba?\(` | WARNING |
123+
| Raw OUDS core token | `\$core-ouds-` | ERROR |
124+
| Raw brand token | `\$core-orange-\|\$core-sosh-\|\$core-orange-compact-` | ERROR |
125+
| Forbidden Sass function | `lighten\(\|darken\(` | ERROR |
126+
| border: none | `border:\s*none` | ERROR |
127+
| Direct transition property | `^\s+transition:\s` (without `@include`) | ERROR |
128+
| Direct border-radius property | `^\s+border-radius:\s` (without `@include` and without `stylelint-disable`) | ERROR |
129+
130+
**Exceptions for hardcoded values** (do NOT flag these):
131+
132+
- Standalone `0` (e.g. `margin: 0`, `border: 0`, `padding: 0`) — `0` has no unit and is valid
133+
- Values inside `@keyframes` blocks (animation percentages are not design tokens)
134+
- Values inside comments `//` or `/* */`
135+
- `100%` used for width/height fill
136+
- `1` or `-1` used as multipliers in `calc()` expressions
137+
- Line numbers or indices in CSS counters
138+
- Values flagged with `// stylelint-disable` comments on the same line
139+
140+
### Phase 4: Generate the report
141+
142+
Output a clear, structured report:
143+
144+
```
145+
╔══════════════════════════════════════════════════════╗
146+
║ OUDS TOKEN VALIDATION REPORT ║
147+
║ Files analyzed: <N> ║
148+
╚══════════════════════════════════════════════════════╝
149+
150+
── CROSS-BRAND TOKEN PRESENCE ──────────────────────────
151+
152+
✅ PASS (<N> files — all tokens present in all brands)
153+
• _alert.scss ............... 13 tokens ✓
154+
• _buttons.scss ............. 59 tokens ✓
155+
• _chips.scss ............... 44 tokens ✓
156+
[list all passing files]
157+
158+
❌ FAIL (<N> files)
159+
• _<component>.scss
160+
❌ $ouds-<token-name>
161+
orange: ✓
162+
sosh: ✗ MISSING ← packages/sosh/scss/tokens/_component.scss
163+
orange-compact: ✓
164+
165+
── ANTI-PATTERNS ───────────────────────────────────────
166+
167+
✅ No anti-patterns found
168+
169+
OR
170+
171+
⚠️ WARNING — Hardcoded values
172+
• scss/_chips.scss
173+
line 5: gap: 0 8px; → replace with a spacing token
174+
line 6: padding: 0 5px; → replace with a spacing token
175+
176+
❌ ERROR — Forbidden patterns
177+
• scss/_<component>.scss
178+
line 42: $core-ouds-dimension-200 → use semantic token instead
179+
180+
── SUMMARY ─────────────────────────────────────────────
181+
Cross-brand: <N_pass> pass, <N_fail> fail
182+
Anti-patterns: <N_warnings> warnings, <N_errors> errors
183+
```
184+
185+
## Important context
186+
187+
### Token naming convention
188+
189+
Component tokens follow this pattern: `$ouds-<component>-<category>-<variant>`
190+
191+
Where `<component>` can be multi-word with hyphens:
192+
193+
- `$ouds-alert-*` → component: alert
194+
- `$ouds-button-*` → component: button
195+
- `$ouds-bullet-list-*` → component: bullet-list
196+
- `$ouds-control-item-*` → component: control-item
197+
- `$ouds-input-tag-*` → component: input-tag
198+
- `$ouds-text-input-*` → component: text-input
199+
- `$ouds-text-area-*` → component: text-area
200+
201+
### Brand token files structure
202+
203+
Each brand's `_component.scss` groups tokens by component, delimited by markers:
204+
205+
```scss
206+
// scss-docs-start ouds-component-<name>
207+
$ouds-<name>-...: ... !default;
208+
// scss-docs-end ouds-component-<name>
209+
```
210+
211+
The three brand files have the **same token names** but **different values**. All three must define the same set of tokens.
212+
213+
### Known component token groups (from \_component.scss)
214+
215+
The following component token groups are defined (same across all brands):
216+
217+
- `ouds-component-alert``$ouds-alert-*`
218+
- `ouds-component-badge``$ouds-badge-*`
219+
- `ouds-component-breadcrumb``$ouds-breadcrumb-*`
220+
- `ouds-component-bullet``$ouds-bullet-list-*`
221+
- `ouds-component-button``$ouds-button-*`
222+
- `ouds-component-checkbox``$ouds-checkbox-*`
223+
- `ouds-component-chip``$ouds-chip-*`
224+
- `ouds-component-control``$ouds-control-item-*`
225+
- `ouds-component-divider``$ouds-divider-*`
226+
- `ouds-component-expand``$ouds-expand-*`
227+
- `ouds-component-input``$ouds-input-tag-*`
228+
- `ouds-component-link``$ouds-link-*`
229+
- `ouds-component-pin``$ouds-pin-code-input-*`
230+
- `ouds-component-quantity``$ouds-quantity-input-*`
231+
- `ouds-component-radio``$ouds-radio-button-*`
232+
- `ouds-component-select``$ouds-select-input-*`
233+
- `ouds-component-skeleton``$ouds-skeleton-*`
234+
- `ouds-component-switch``$ouds-switch-*`
235+
- `ouds-component-tag``$ouds-tag-*`
236+
- `ouds-component-text``$ouds-text-input-*`, `$ouds-text-area-*`
237+
238+
### What counts as a component SCSS token vs a semantic token
239+
240+
- Component tokens: `$ouds-<specific-component>-*` (e.g. `$ouds-button-size-min-width`) → MUST be checked across all brands
241+
- Semantic tokens: `$ouds-color-*`, `$ouds-space-*`, `$ouds-border-*`, `$ouds-dimension-*`, `$ouds-size-*`, `$ouds-opacity-*` → These are defined in `_semantic.scss`, not `_component.scss`. **Do NOT check these cross-brand** — they are always present.
242+
243+
To distinguish: a component token is any `$ouds-*` variable whose name prefix matches one of the known component groups listed above.
244+
245+
## Known real anti-patterns (validated)
246+
247+
These are confirmed issues found in the codebase that the skill should flag:
248+
249+
| File | Line | Pattern | Type |
250+
| ------------------------- | ---------------- | ------------------------------- | ------------------------ |
251+
| `scss/_button-group.scss` | 142, 146 | `$core-orange-color-orange-500` | ERROR: raw brand token |
252+
| `scss/_chips.scss` | 90, 91, 158, 159 | `width: 1em; height: 1em;` | WARNING: hardcoded value |
253+
254+
## How to run
255+
256+
When this skill is loaded, immediately:
257+
258+
1. Read all three brand `_component.scss` files
259+
2. List all SCSS component files (excluding non-component files)
260+
3. For each file, run checks as described above
261+
4. Output the full report
262+
263+
If the user says "validate [component name]", only analyze the SCSS file(s) corresponding to that component.
264+
265+
Do not make any file modifications. This is a read-only audit.

0 commit comments

Comments
 (0)