Skip to content

Commit 6b8d3bf

Browse files
authored
Merge pull request #27 from kurtstohrer/stress-test-skeleton
Stress-test skeleton: multi-MFE workspace, theme variants, init-skills
2 parents 47f44c1 + 4b8d9e7 commit 6b8d3bf

439 files changed

Lines changed: 20520 additions & 3551 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/annotask-apply/SKILL.md

Lines changed: 204 additions & 16 deletions
Large diffs are not rendered by default.

.agents/skills/annotask-init/SKILL.md

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ Scans the project to detect the framework, design tokens (colors, typography, sp
1616

1717
## Output schema
1818

19-
Every token uses this shape:
19+
Every token carries resolved values for every detected theme variant via a
20+
`values` map keyed by theme id. Tokens that don't change across variants repeat
21+
the same value under each id — this keeps every token one uniform shape.
2022

2123
```json
2224
{
2325
"role": "primary",
24-
"value": "#3b82f6",
26+
"values": { "light": "#3b82f6", "dark": "#60a5fa" },
2527
"cssVar": "--color-primary",
2628
"source": "var(--color-primary)",
2729
"sourceFile": "src/assets/main.css",
@@ -30,11 +32,25 @@ Every token uses this shape:
3032
```
3133

3234
- `role` — semantic name (see vocabularies below)
33-
- `value` — resolved value (hex color, font string, size, etc.)
35+
- `values` — resolved value per theme variant, keyed by theme id (`light`, `dark`, or a named theme). Single-theme apps use `{ "default": "…" }`.
3436
- `cssVar` — the CSS custom property name, if backed by one. Omit if not a CSS variable.
3537
- `source` — human-readable provenance: `var(--x)`, `tailwind.config:colors.primary`, `@theme:--color-primary`
3638
- `sourceFile` — relative path to the file where the value is defined
37-
- `sourceLine` — line number in that file
39+
- `sourceLine` — line number in that file (the `:root` variant's definition is fine when multiple exist)
40+
41+
Theme variants themselves are listed at the top of the spec under `themes`,
42+
with a `selector` describing how each variant is activated in the DOM so the
43+
Annotask shell can match it against the iframe's current state:
44+
45+
```json
46+
"themes": [
47+
{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } },
48+
{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "class", "host": "html", "name": "dark" } }
49+
],
50+
"defaultTheme": "light"
51+
```
52+
53+
`selector.kind` is one of `attribute` (with `name`, `value`, optional `host`), `class` (with `name`, optional `host`), `media` (with `media: '(prefers-color-scheme: dark)'`), or `default` (no selector — the base `:root` values). Apps with only one theme should emit a single `{ "id": "default", "name": "Default", "selector": { "kind": "default" } }` variant.
3854

3955
## Steps
4056

@@ -44,22 +60,52 @@ Check `package.json` dependencies:
4460
- `vue` → name "vue", read version
4561
- `react` / `react-dom` → name "react"
4662
- `svelte` → name "svelte"
63+
- `solid-js` → name "solid"
4764

4865
Check for styling:
4966
- `tailwindcss` in devDependencies or dependencies → add "tailwind" to styling array
5067
- Look for `<style scoped>` in `.vue` files → add "scoped-css"
5168
- Look for CSS module imports (`.module.css`) → add "css-modules"
5269

53-
### 2. Scan colors
70+
### 2. Detect theme variants
71+
72+
Before extracting any token values, determine which theme variants the project
73+
supports so you know which scopes to resolve values under. Check these signals
74+
in order and stop at the first match:
75+
76+
1. **Tailwind `.dark` class** — If CSS files contain `.dark { … }` / `:root.dark { … }` / `html.dark { … }` blocks, or Tailwind v3 config has `darkMode: 'class'`, or Tailwind v4 CSS contains `@custom-variant dark (&:where(.dark, .dark *))`. Emit two variants:
77+
- `{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } }`
78+
- `{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "class", "host": "html", "name": "dark" } }`
79+
80+
2. **Data-attribute theming** — If CSS contains `[data-theme="…"]`, `[data-bs-theme="…"]`, `[data-mui-color-scheme="…"]`, `[data-mantine-color-scheme="…"]`, or `[data-color-scheme="…"]` selectors. Emit one variant per distinct attribute value:
81+
- `{ "id": "<value>", "name": "<Value>", "scheme": "light|dark (if obvious)", "selector": { "kind": "attribute", "host": "html", "name": "<attr-name>", "value": "<value>" } }`
82+
83+
3. **`@media (prefers-color-scheme: dark)` blocks** — If the project only switches via media queries. Emit:
84+
- `{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } }`
85+
- `{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "media", "media": "(prefers-color-scheme: dark)" } }`
86+
87+
4. **Single theme** — If none of the above are present:
88+
- `[ { "id": "default", "name": "Default", "selector": { "kind": "default" } } ]`
89+
90+
Set `defaultTheme` to the variant id used when no selector matches — typically `"light"` or `"default"`.
91+
92+
### 3. Scan colors
5493

5594
**Find color sources:**
5695

57-
1. **CSS variables**: Search for `:root` declarations across CSS files and Vue `<style>` blocks. Extract `--variable-name: value` pairs.
96+
1. **CSS variables**: Search for `:root`, `:root.dark`, `html.dark`, `[data-theme="…"]`, and `@media (prefers-color-scheme: dark) :root` declarations across CSS files and Vue `<style>` blocks. Extract `--variable-name: value` pairs, remembering which variant scope each came from.
5897

59-
2. **Tailwind v4** (version >= 4): Search for `@theme` blocks in CSS files. Extract custom properties defined within them.
98+
2. **Tailwind v4** (version >= 4): Search for `@theme` blocks in CSS files. Extract custom properties defined within them. `@theme` blocks without a variant selector apply to every variant.
6099

61100
3. **Tailwind v3** (version < 4): Read `tailwind.config.js`, `tailwind.config.ts`, or `tailwind.config.mjs`. Extract `theme.extend.colors`. Convert to flat key-value pairs.
62101

102+
**Resolve each color under every detected variant.** Produce a `values` map
103+
keyed by theme id. When a variant doesn't override the token, copy the base
104+
(`:root`/light/default) value so every token has a value for every variant.
105+
When a variant-specific block defines a new CSS variable that doesn't appear
106+
in the base scope, leave the missing variants blank (the server normalizer
107+
will fill them when the spec is read).
108+
63109
**Classify into semantic roles using this fixed vocabulary:**
64110

65111
| Role | Match heuristics (in variable/key name) |
@@ -81,7 +127,11 @@ If a variable doesn't match any heuristic, use a descriptive role name derived f
81127

82128
**Limits**: Maximum 30 color tokens. Prioritize semantic roles first, then most-used custom colors.
83129

84-
### 3. Scan typography
130+
### 4. Scan typography
131+
132+
Apply the same per-variant `values` map strategy used for colors: resolve each
133+
token under every detected variant, copying the base value for variants that
134+
don't override it.
85135

86136
**Font families**: Search for:
87137
- CSS variables containing `font`, `family` in `:root` or `@theme`
@@ -102,7 +152,7 @@ Use roles: `xs`, `sm`, `base`, `lg`, `xl`, `2xl`, `3xl`, `4xl` (match by name or
102152

103153
**Weights**: Scan component files for `font-weight` values and Tailwind weight classes (`font-bold`, `font-semibold`, etc.). List unique weights as strings: `["400", "500", "600", "700"]`.
104154

105-
### 4. Scan spacing
155+
### 5. Scan spacing
106156

107157
Search for:
108158
- CSS variables containing `space`, `gap`, `margin`, `padding`, `size`
@@ -113,7 +163,7 @@ Use roles: `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `4xl` (match by name or a
113163

114164
**Limits**: Maximum 12 spacing tokens.
115165

116-
### 5. Scan border radius
166+
### 6. Scan border radius
117167

118168
Search for:
119169
- CSS variables containing `radius`, `rounded`
@@ -122,7 +172,7 @@ Search for:
122172

123173
Use roles: `sm`, `md`, `lg`, `xl`, `full` (match by name or ascending size).
124174

125-
### 6. Scan breakpoints
175+
### 7. Scan breakpoints
126176

127177
Detect responsive breakpoints from whatever styling system the project uses. Output as a flat object mapping name → min-width value.
128178

@@ -151,7 +201,7 @@ Detect responsive breakpoints from whatever styling system the project uses. Out
151201

152202
If no breakpoints are detected, omit the `breakpoints` field (don't include an empty object).
153203

154-
### 7. Detect icon library
204+
### 8. Detect icon library
155205

156206
Check `package.json` dependencies for:
157207

@@ -165,7 +215,7 @@ Check `package.json` dependencies for:
165215

166216
Read the version from package.json.
167217

168-
### 8. Detect component library
218+
### 9. Detect component library
169219

170220
Check `package.json` dependencies for:
171221

@@ -186,7 +236,7 @@ grep -rh "from '${package}" --include='*.vue' --include='*.ts' --include='*.js'
186236

187237
Extract component names from the imports. Read the version from package.json.
188238

189-
### 9. Write design spec
239+
### 10. Write design spec
190240

191241
Create `.annotask/design-spec.json`:
192242

@@ -198,25 +248,30 @@ Create `.annotask/design-spec.json`:
198248
"version": "3.5.0",
199249
"styling": ["tailwind", "scoped-css"]
200250
},
251+
"themes": [
252+
{ "id": "light", "name": "Light", "scheme": "light", "selector": { "kind": "default" } },
253+
{ "id": "dark", "name": "Dark", "scheme": "dark", "selector": { "kind": "class", "host": "html", "name": "dark" } }
254+
],
255+
"defaultTheme": "light",
201256
"colors": [
202-
{ "role": "primary", "value": "#3b82f6", "cssVar": "--color-primary", "source": "var(--color-primary)", "sourceFile": "src/assets/main.css", "sourceLine": 5 },
203-
{ "role": "background", "value": "#0b1120", "cssVar": "--bg", "source": "var(--bg)", "sourceFile": "src/assets/main.css", "sourceLine": 3 }
257+
{ "role": "primary", "values": { "light": "#3b82f6", "dark": "#60a5fa" }, "cssVar": "--color-primary", "source": "var(--color-primary)", "sourceFile": "src/assets/main.css", "sourceLine": 5 },
258+
{ "role": "background", "values": { "light": "#ffffff", "dark": "#0b1120" }, "cssVar": "--bg", "source": "var(--bg)", "sourceFile": "src/assets/main.css", "sourceLine": 3 }
204259
],
205260
"typography": {
206261
"families": [
207-
{ "role": "body", "value": "Inter, sans-serif", "cssVar": "--font-sans", "source": "var(--font-sans)", "sourceFile": "src/assets/main.css", "sourceLine": 10 }
262+
{ "role": "body", "values": { "light": "Inter, sans-serif", "dark": "Inter, sans-serif" }, "cssVar": "--font-sans", "source": "var(--font-sans)", "sourceFile": "src/assets/main.css", "sourceLine": 10 }
208263
],
209264
"scale": [
210-
{ "role": "base", "value": "1rem", "cssVar": "--text-base", "source": "var(--text-base)", "sourceFile": "src/assets/main.css", "sourceLine": 15 }
265+
{ "role": "base", "values": { "light": "1rem", "dark": "1rem" }, "cssVar": "--text-base", "source": "var(--text-base)", "sourceFile": "src/assets/main.css", "sourceLine": 15 }
211266
],
212267
"weights": ["400", "500", "600", "700"]
213268
},
214269
"spacing": [
215-
{ "role": "sm", "value": "8px", "cssVar": "--space-sm", "source": "var(--space-sm)", "sourceFile": "src/assets/main.css", "sourceLine": 20 }
270+
{ "role": "sm", "values": { "light": "8px", "dark": "8px" }, "cssVar": "--space-sm", "source": "var(--space-sm)", "sourceFile": "src/assets/main.css", "sourceLine": 20 }
216271
],
217272
"borders": {
218273
"radius": [
219-
{ "role": "md", "value": "8px", "cssVar": "--radius-md", "source": "var(--radius-md)", "sourceFile": "src/assets/main.css", "sourceLine": 25 }
274+
{ "role": "md", "values": { "light": "8px", "dark": "8px" }, "cssVar": "--radius-md", "source": "var(--radius-md)", "sourceFile": "src/assets/main.css", "sourceLine": 25 }
220275
]
221276
},
222277
"breakpoints": {
@@ -238,19 +293,19 @@ Create `.annotask/design-spec.json`:
238293
}
239294
```
240295

241-
### 10. Clean up old config
296+
### 11. Clean up old config
242297

243298
If `.annotask/config.json` exists, delete it — it's been replaced by `design-spec.json`.
244299

245-
### 11. Update .gitignore
300+
### 12. Update .gitignore
246301

247302
Check if `.gitignore` contains `.annotask/`. If not, append:
248303
```
249304
# Annotask (generated)
250305
.annotask/
251306
```
252307

253-
### 12. Report to user
308+
### 13. Report to user
254309

255310
Tell the user:
256311
- What was detected (framework, number of color/typography/spacing tokens, libraries)

0 commit comments

Comments
 (0)