codebyplan 1.5.1 → 1.8.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/dist/cli.js +4462 -748
- package/package.json +5 -1
- package/templates/.gitkeep +0 -0
- package/templates/README.md +20 -0
- package/templates/agents/cbp-cc-executor.md +213 -0
- package/templates/agents/cbp-database-agent.md +229 -0
- package/templates/agents/cbp-improve-claude.md +245 -0
- package/templates/agents/cbp-improve-round.md +284 -0
- package/templates/agents/cbp-mechanical-edits.md +111 -0
- package/templates/agents/cbp-research.md +282 -0
- package/templates/agents/cbp-round-executor.md +604 -0
- package/templates/agents/cbp-security-agent.md +134 -0
- package/templates/agents/cbp-task-check.md +213 -0
- package/templates/agents/cbp-task-planner.md +582 -0
- package/templates/agents/cbp-test-e2e-agent.md +363 -0
- package/templates/agents/cbp-testing-qa-agent.md +400 -0
- package/templates/context/mcp-docs.md +139 -0
- package/templates/hooks/README.md +236 -0
- package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
- package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
- package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
- package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
- package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
- package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
- package/templates/hooks/cbp-notify.sh +68 -0
- package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
- package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
- package/templates/hooks/cbp-statusline.sh +347 -0
- package/templates/hooks/cbp-subagent-statusline.sh +182 -0
- package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
- package/templates/hooks/cbp-test-hooks.sh +320 -0
- package/templates/hooks/hooks.json +85 -0
- package/templates/hooks/validate-context-usage.sh +59 -0
- package/templates/hooks/validate-git-commit.sh +78 -0
- package/templates/hooks/validate-git-stash-deny.sh +32 -0
- package/templates/hooks/validate-structure-lengths.sh +57 -0
- package/templates/hooks/validate-structure-lib.sh +104 -0
- package/templates/hooks/validate-structure-patterns.sh +54 -0
- package/templates/hooks/validate-structure-scope.sh +33 -0
- package/templates/hooks/validate-structure-smoke.sh +95 -0
- package/templates/hooks/validate-structure-templates.sh +34 -0
- package/templates/hooks/validate-structure.sh +69 -0
- package/templates/rules/.gitkeep +0 -0
- package/templates/rules/README.md +47 -0
- package/templates/rules/context-file-loading.md +52 -0
- package/templates/rules/scope-vocabulary.md +64 -0
- package/templates/rules/todo-backend.md +109 -0
- package/templates/settings.project.base.json +55 -0
- package/templates/settings.user.base.json +25 -0
- package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
- package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
- package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
- package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
- package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
- package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
- package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
- package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
- package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
- package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
- package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
- package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
- package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
- package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
- package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
- package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
- package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
- package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
- package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
- package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
- package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
- package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
- package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
- package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
- package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
- package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
- package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
- package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
- package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
- package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
- package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
- package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
- package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
- package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
- package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
- package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
- package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
- package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
- package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
- package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
- package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
- package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
- package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
- package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
- package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
- package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
- package/templates/skills/cbp-checkpoint-create/SKILL.md +287 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
- package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
- package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
- package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
- package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
- package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
- package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
- package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
- package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
- package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
- package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
- package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
- package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
- package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
- package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
- package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
- package/templates/skills/cbp-git-commit/SKILL.md +278 -0
- package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
- package/templates/skills/cbp-merge-main/SKILL.md +228 -0
- package/templates/skills/cbp-round-check/SKILL.md +104 -0
- package/templates/skills/cbp-round-end/SKILL.md +183 -0
- package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
- package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
- package/templates/skills/cbp-round-execute/SKILL.md +211 -0
- package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
- package/templates/skills/cbp-round-input/SKILL.md +165 -0
- package/templates/skills/cbp-round-start/SKILL.md +222 -0
- package/templates/skills/cbp-round-update/SKILL.md +163 -0
- package/templates/skills/cbp-session-end/SKILL.md +187 -0
- package/templates/skills/cbp-session-start/SKILL.md +155 -0
- package/templates/skills/cbp-ship/SKILL.md +332 -0
- package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
- package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
- package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
- package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
- package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
- package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
- package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
- package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
- package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
- package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
- package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
- package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
- package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
- package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
- package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
- package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
- package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
- package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
- package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
- package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
- package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
- package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
- package/templates/skills/cbp-ship/reference/versioning.md +116 -0
- package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
- package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
- package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
- package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
- package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
- package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
- package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
- package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
- package/templates/skills/cbp-ship/templates/eas.json +66 -0
- package/templates/skills/cbp-ship/templates/railway.toml +15 -0
- package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
- package/templates/skills/cbp-ship/templates/vercel.json +19 -0
- package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
- package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
- package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
- package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
- package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
- package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
- package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
- package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
- package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
- package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
- package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
- package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
- package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
- package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
- package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
- package/templates/skills/cbp-ship-main/SKILL.md +65 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
- package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
- package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
- package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
- package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
- package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
- package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
- package/templates/skills/cbp-task-check/SKILL.md +166 -0
- package/templates/skills/cbp-task-complete/SKILL.md +206 -0
- package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
- package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
- package/templates/skills/cbp-task-create/SKILL.md +167 -0
- package/templates/skills/cbp-task-start/SKILL.md +239 -0
- package/templates/skills/cbp-task-testing/SKILL.md +277 -0
- package/templates/skills/cbp-todo/SKILL.md +97 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
scope: org-shared
|
|
3
|
+
name: cbp-frontend-ui
|
|
4
|
+
description: Visual quality self-review pass invoked twice per round — once by round-executor Step 3.8 (phase 'style_only', no screenshots) for token/spacing/typography/color/cohesion, once by /cbp-round-execute Step 5b (phase 'screenshot_review', with e2e screenshots) for rendered-output review and baseline regressions. Default phase 'full' runs everything for back-compat.
|
|
5
|
+
effort: xhigh
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Frontend UI (Post-Implementation Visual Self-Review)
|
|
9
|
+
|
|
10
|
+
Invoked twice per round in non-`claude_only` profiles:
|
|
11
|
+
|
|
12
|
+
1. `round-executor` Step 3.8 — `phase: 'style_only'`, no e2e screenshots. Reviews token/spacing/typography/color/cohesion against the just-written code.
|
|
13
|
+
2. `/cbp-round-execute` Step 5b — `phase: 'screenshot_review'`, with screenshots from `test-e2e-agent`. Reviews rendered output and detects baseline regressions.
|
|
14
|
+
|
|
15
|
+
Default `phase: 'full'` runs everything (back-compat for any caller not yet migrated). Inline counterpart of the up-front `frontend-design` skill — `frontend-design` decides direction before code; `frontend-ui` reviews and polishes after code.
|
|
16
|
+
|
|
17
|
+
## When this skill fires
|
|
18
|
+
|
|
19
|
+
Round-executor invokes when the round's `files_changed` contains ANY of:
|
|
20
|
+
|
|
21
|
+
- `*.tsx`, `*.jsx` (React, RN, RN-Web components)
|
|
22
|
+
- `*.scss`, `*.css`, `*.module.{scss,css}`
|
|
23
|
+
- Files under design-system / token folders, app-level styles
|
|
24
|
+
- New page / screen / route / component files
|
|
25
|
+
|
|
26
|
+
Same trigger condition as Step 2.7's `frontend-design` pre-implementation pass. If none match, skip — proceed to Step 4 (Quality Checks).
|
|
27
|
+
|
|
28
|
+
## Input Contract
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
input:
|
|
32
|
+
task_number: number
|
|
33
|
+
round_number: number
|
|
34
|
+
phase: 'full' | 'style_only' | 'screenshot_review' # Default 'full'. Phase Gate at Workflow start routes execution.
|
|
35
|
+
files_changed: [{path, action}]
|
|
36
|
+
context:
|
|
37
|
+
checkpoint_goal: string
|
|
38
|
+
round_requirements: string
|
|
39
|
+
e2e_screenshots: # Required for phase 'screenshot_review' or 'full' (when present); empty / omitted for 'style_only'. Sourced from round.context.e2e_output.screenshots (populated by test-e2e-agent at /cbp-round-execute Step 5).
|
|
40
|
+
- test_name: string
|
|
41
|
+
path: string # Repo-relative or absolute path to PNG
|
|
42
|
+
page_or_screen: string
|
|
43
|
+
viewport: 'desktop' | 'mobile' | 'tablet' | 'device'
|
|
44
|
+
is_new: bool # No baseline existed (new screen)
|
|
45
|
+
baseline_diff_pct: number | null # Pixel-diff % vs Playwright baseline
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Output Contract
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
output:
|
|
52
|
+
status: 'completed'
|
|
53
|
+
findings:
|
|
54
|
+
- category: 'tokens' | 'spacing' | 'typography' | 'color' | 'layout' | 'cohesion' | 'rendered_visual' | 'baseline_regression'
|
|
55
|
+
severity: 'critical' | 'warning' | 'suggestion'
|
|
56
|
+
file: string # Source file OR screenshot path (for rendered_visual / baseline_regression)
|
|
57
|
+
line: number | null # null when finding originates from a screenshot
|
|
58
|
+
issue: string
|
|
59
|
+
suggestion: string
|
|
60
|
+
screenshot: string | null # Path to screenshot this finding was derived from (if any)
|
|
61
|
+
files_changed:
|
|
62
|
+
- path: string
|
|
63
|
+
action: 'modified'
|
|
64
|
+
fix_for: string # Which finding this fix addresses
|
|
65
|
+
screenshot_review:
|
|
66
|
+
reviewed: number # Count of e2e_screenshots reviewed
|
|
67
|
+
baseline_regressions: number # Count with baseline_diff_pct > threshold
|
|
68
|
+
new_screens_reviewed: number # Count with is_new == true
|
|
69
|
+
summary:
|
|
70
|
+
total_issues: number
|
|
71
|
+
critical: number
|
|
72
|
+
warnings: number
|
|
73
|
+
suggestions: number
|
|
74
|
+
auto_fixed: number
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The executor writes the output to `round.context.frontend_ui_review` and merges `files_changed` into the round's `files_changed` list.
|
|
78
|
+
|
|
79
|
+
## Reference Material
|
|
80
|
+
|
|
81
|
+
Pattern references this skill consults during review (folded from former standalone skills):
|
|
82
|
+
|
|
83
|
+
- `reference/ui-layout-patterns.md` — navbar height, focus-visible, z-index layering, fluid heights, logo overflow
|
|
84
|
+
- `reference/variant-defaults.md` — `createResolver`, `SIZE_VARIANTS`, `COLOR_VARIANTS`, `CUSTOM_VARIANTS`, lightweight variant maps
|
|
85
|
+
- `reference/ui-label-maps.md` — humanising DB column names + enum values for user-facing UI
|
|
86
|
+
|
|
87
|
+
## Workflow
|
|
88
|
+
|
|
89
|
+
### Phase Gate (preamble)
|
|
90
|
+
|
|
91
|
+
Read `input.phase` (default `'full'` when absent). The gate routes which phases run:
|
|
92
|
+
|
|
93
|
+
| Phase value | Phases that run |
|
|
94
|
+
|-------------|----------------|
|
|
95
|
+
| `'full'` (default) | All phases — 1, 2, 2.5, 3, 4, 5, 6, **6.5** (when `e2e_screenshots[]` non-empty), 7, 8 |
|
|
96
|
+
| `'style_only'` | Phases 1, 2, 2.5, 3, 4, 5, 6, 7, 8 (Phase 6.5 is SKIPPED regardless of `e2e_screenshots[]`) |
|
|
97
|
+
| `'screenshot_review'` | Phases 1 (read changed files only — needed for design-source comparison), **6.5**, 7, 8 (Phase 8 is a no-op for `rendered_visual` / `baseline_regression` — those categories are never auto-fixed) |
|
|
98
|
+
|
|
99
|
+
When `phase === 'screenshot_review'` and `e2e_screenshots[]` is empty, return immediately with `status: 'completed'` and an empty `findings[]` — caller asked for screenshot review but provided no screenshots.
|
|
100
|
+
|
|
101
|
+
When `phase === 'style_only'` and `files_changed[]` contains no UI files (no `*.tsx`, `*.scss`, `*.css`, design-token folders), return immediately with `status: 'completed'` and an empty `findings[]`.
|
|
102
|
+
|
|
103
|
+
### Phase 1: Read Changed Files
|
|
104
|
+
|
|
105
|
+
Read all `.tsx`, `.scss`, and `variants.ts` files from `files_changed`.
|
|
106
|
+
|
|
107
|
+
### Phase 2: Design Token Compliance
|
|
108
|
+
|
|
109
|
+
For each SCSS file:
|
|
110
|
+
- Check that colors use semantic tokens (`$color-brand-*`, `$color-highlight-*`), never raw hex/rgb
|
|
111
|
+
- Check spacing uses fluid tokens (`fluid()`, `$spacing-*`), never hardcoded px for layout
|
|
112
|
+
- Check typography uses type scale tokens (`$font-size-*`, `$font-weight-*`)
|
|
113
|
+
- Check z-index uses layering tokens, never arbitrary numbers — see `reference/ui-layout-patterns.md` "Z-Index Layers"
|
|
114
|
+
|
|
115
|
+
For each TSX file:
|
|
116
|
+
- Check inline styles are not used (should be SCSS modules)
|
|
117
|
+
- Check className uses styles from module imports
|
|
118
|
+
|
|
119
|
+
### Phase 2.5: Design Source Comparison
|
|
120
|
+
|
|
121
|
+
When changed files belong to a page that has design source PNGs at a project-known design-sources path (e.g. `docs/design/`, `docs/development/product/sources/design/`):
|
|
122
|
+
|
|
123
|
+
1. **Read** the design source PNG for the relevant page/component
|
|
124
|
+
2. **Compare** the implemented component structure against the design source
|
|
125
|
+
3. **Flag as critical** any of these mismatches:
|
|
126
|
+
- Wrong control shape (e.g. pill vs flat button, toggle vs stepper)
|
|
127
|
+
- Wrong background color (e.g. grey when design shows white)
|
|
128
|
+
- Missing dividers or separators shown in design
|
|
129
|
+
- Wrong column placement (e.g. controls in label column instead of action column)
|
|
130
|
+
- Wrong row/grid structure (e.g. stacked when design shows inline)
|
|
131
|
+
4. Add findings to output with `category: 'cohesion'` and `severity: 'critical'`
|
|
132
|
+
|
|
133
|
+
If no design source PNGs exist for the changed pages, skip this phase.
|
|
134
|
+
|
|
135
|
+
### Phase 3: Spacing Consistency
|
|
136
|
+
|
|
137
|
+
- Verify consistent spacing patterns within components (same spacing system)
|
|
138
|
+
- Check padding/margin consistency across sibling elements
|
|
139
|
+
- Verify fluid spacing is used for layout, em-based for typography-adjacent
|
|
140
|
+
|
|
141
|
+
### Phase 4: Typography Review
|
|
142
|
+
|
|
143
|
+
- Check font-family assignments use design tokens
|
|
144
|
+
- Verify heading hierarchy (h1 > h2 > h3 in size)
|
|
145
|
+
- Check line-height and letter-spacing use tokens
|
|
146
|
+
- Verify responsive typography uses fluid values
|
|
147
|
+
|
|
148
|
+
### Phase 5: Color Usage
|
|
149
|
+
|
|
150
|
+
- Check color semantics (brand vs highlight vs accent vs interactive)
|
|
151
|
+
- Verify hover/focus states use correct color variants — see `reference/ui-layout-patterns.md` "Focus-Visible" and `reference/variant-defaults.md` "COLOR_VARIANTS"
|
|
152
|
+
- Check dark mode compatibility if applicable
|
|
153
|
+
- Verify sufficient contrast ratios (reference WCAG)
|
|
154
|
+
|
|
155
|
+
### Phase 6: Visual Cohesion
|
|
156
|
+
|
|
157
|
+
- Check that new components match visual patterns of existing components
|
|
158
|
+
- Verify consistent border-radius, shadow, and transition usage
|
|
159
|
+
- Check animation patterns follow existing conventions
|
|
160
|
+
- For variant components: verify `variants.ts` follows the resolver patterns in `reference/variant-defaults.md` (no duplicate defaults, correct layer ordering)
|
|
161
|
+
- For DB-sourced strings rendered in JSX (snake_case enums, trigger types, status codes): verify `LABELS` const maps are used per `reference/ui-label-maps.md`
|
|
162
|
+
|
|
163
|
+
### Phase 6.5: Rendered-Output Visual Review (from E2E screenshots)
|
|
164
|
+
|
|
165
|
+
**Runs only if `e2e_screenshots[]` is non-empty.** This is the critical step that closes the loop — e2e tests proved behavior works; this step checks what the screen actually looks like.
|
|
166
|
+
|
|
167
|
+
For each screenshot in `e2e_screenshots[]`:
|
|
168
|
+
|
|
169
|
+
1. **Read the PNG via the Read tool** (Claude multimodal — the PNG is shown to the model directly). Do not use Bash to inspect bytes.
|
|
170
|
+
2. **Check baseline regression** (`baseline_diff_pct != null`):
|
|
171
|
+
- If `baseline_diff_pct > 0.1%`: emit finding `{category: 'baseline_regression', severity: 'critical', file: {path}, screenshot: {path}, issue: 'Pixel diff vs committed baseline: {diff_pct}%', suggestion: 'Inspect the diff PNG (same folder, -diff suffix). Either fix the regression or, if intentional, run `playwright test --update-snapshots` and commit the new baseline.'}`
|
|
172
|
+
- Do NOT auto-update baselines. The user must explicitly approve via QA.
|
|
173
|
+
3. **Semantic review of rendered output** (both new screens and existing):
|
|
174
|
+
- **Text overflow / truncation** — text clipped, ellipsis in unintended places, buttons cut off
|
|
175
|
+
- **Unstyled elements** — unbranded default fonts, missing styles (flash of unstyled content captured), default blue links
|
|
176
|
+
- **Missing imagery / broken images** — placeholder icons, broken-image glyphs, avatar fallback unstyled
|
|
177
|
+
- **Contrast failures** — text barely visible against background (WCAG fail)
|
|
178
|
+
- **Layout breaks** — overlapping elements, pushed-off-canvas content, unintended scrollbars, empty whitespace where content should be
|
|
179
|
+
- **Loading-state artifacts** — spinner visible in final-state screenshot (data didn't load in time or empty-state is wrong)
|
|
180
|
+
- **Error-boundary leaks** — "Something went wrong" / red error banners that shouldn't be there
|
|
181
|
+
- **Design-source mismatch** (if a design-source PNG exists for the page) — compare rendered to designed
|
|
182
|
+
4. **Emit findings** with `category: 'rendered_visual'`, the screenshot path in `screenshot`, and `line: null`. Severity: `critical` for broken state (overflow, missing images, error banners, contrast fails), `warning` for cosmetic misalignment, `suggestion` for minor polish.
|
|
183
|
+
5. **Cross-viewport consistency**: if the same `page_or_screen` appears at multiple viewports (desktop + mobile), verify responsive behavior is correct (nothing cropped at mobile, desktop isn't mobile-sized). Emit `{category: 'layout', severity: 'warning', …}` on mismatch.
|
|
184
|
+
|
|
185
|
+
Populate `screenshot_review` totals.
|
|
186
|
+
|
|
187
|
+
**Do not attempt to auto-fix `rendered_visual` or `baseline_regression` findings** — they feed into user QA and the fix loop, because the root cause is typically in app code/data, not in the SCSS.
|
|
188
|
+
|
|
189
|
+
### Phase 7: Aggregate Findings
|
|
190
|
+
|
|
191
|
+
Categorize all issues by severity:
|
|
192
|
+
- **Critical**: Hardcoded colors/spacing, missing tokens, broken layout
|
|
193
|
+
- **Warning**: Inconsistent spacing, non-standard patterns
|
|
194
|
+
- **Suggestion**: Minor visual improvements, optional enhancements
|
|
195
|
+
|
|
196
|
+
### Phase 8: Auto-Fix In-Scope Findings
|
|
197
|
+
|
|
198
|
+
For every finding in `category` ∈ {`tokens`, `spacing`, `typography`, `color`, `layout`, `cohesion`} — critical, warning, and suggestion — apply the fix directly when the target file is in `files_changed`:
|
|
199
|
+
|
|
200
|
+
#### Pre-Edit Scope Gate (MANDATORY)
|
|
201
|
+
|
|
202
|
+
Before any Write/Edit:
|
|
203
|
+
|
|
204
|
+
1. Build `allowed = new Set(input.files_changed.map(f => f.path))`.
|
|
205
|
+
2. If the target path is in `allowed`, proceed to apply the fix.
|
|
206
|
+
3. If the target path is NOT in `allowed`, do NOT edit. Instead:
|
|
207
|
+
- Emit the proposed fix as a `findings[]` entry with the appropriate severity and a `suggestion` describing the change verbatim.
|
|
208
|
+
- Set `auto_fixed = false` for that finding.
|
|
209
|
+
- Increment `summary.out_of_scope_fixes`.
|
|
210
|
+
|
|
211
|
+
The skill's auto-fix capability is for in-scope polish, not opportunistic sweeps of unrelated files. The scope gate prevents the round's diff from absorbing visual changes outside the round's contract.
|
|
212
|
+
|
|
213
|
+
**Specifically forbidden** (always out of scope, never edited regardless of `files_changed`):
|
|
214
|
+
|
|
215
|
+
- `.claude/**` — managed infrastructure under user-level governance
|
|
216
|
+
- Project test infrastructure (e.g., `playwright.config.*`, `e2e/**`) — governed by `test-e2e-agent`
|
|
217
|
+
- DB migrations (e.g., `supabase/migrations/**`) — governed by `database-agent`
|
|
218
|
+
- Vendor mirrors and read-only reference trees
|
|
219
|
+
|
|
220
|
+
#### In-Scope Auto-Fix Procedure
|
|
221
|
+
|
|
222
|
+
For findings whose target file IS in `allowed`:
|
|
223
|
+
|
|
224
|
+
1. Read the file at the reported line
|
|
225
|
+
2. Apply the fix (replace hardcoded hex with token, swap px for fluid spacing, improve visual consistency, refine typography, enhance cohesion)
|
|
226
|
+
3. Track in `files_changed` with `fix_for` referencing the finding
|
|
227
|
+
|
|
228
|
+
Go beyond fixing violations — actively improve visual quality. If spacing could be better, improve it. If a component looks inconsistent with siblings, align it. Deliver polished UI, not just compliant UI.
|
|
229
|
+
|
|
230
|
+
**Do not auto-fix** findings with `category` ∈ {`rendered_visual`, `baseline_regression`} — these surface through QA and the fix loop because the root cause is typically in app state/data, not styling.
|
|
231
|
+
|
|
232
|
+
**Do not modify** component logic or behavior — only visual/design work.
|
|
233
|
+
|
|
234
|
+
## Completion Criteria
|
|
235
|
+
|
|
236
|
+
- All changed SCSS/TSX files reviewed
|
|
237
|
+
- Token compliance checked
|
|
238
|
+
- Spacing consistency verified
|
|
239
|
+
- **All `e2e_screenshots` reviewed** (when provided) — rendered output checked for overflow, unstyled elements, missing imagery, contrast, layout breaks, loading/error artifacts, design-source fidelity
|
|
240
|
+
- Baseline regressions surfaced (never auto-accepted)
|
|
241
|
+
- Critical/warning issues auto-fixed where possible (styling only, in-scope only)
|
|
242
|
+
- Findings categorized by severity
|
|
243
|
+
|
|
244
|
+
## Failure Modes
|
|
245
|
+
|
|
246
|
+
| Condition | Status | What to populate |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| No UI files changed AND `e2e_screenshots[]` is empty | `completed` | Empty `findings[]`, `summary.total_issues: 0`, `screenshot_review: {reviewed: 0, ...}` — caller decided to invoke; return clean |
|
|
249
|
+
| Screenshot path in `e2e_screenshots[]` cannot be read (missing file, corrupt PNG) | `completed` | Add `{category: 'rendered_visual', severity: 'warning', file: {path}, issue: 'Screenshot unreadable — visual review skipped for this state'}` and continue with the remaining screenshots |
|
|
250
|
+
| Design source PNG referenced but missing | `completed` | Skip Phase 2.5 for that page; note in finding with `category: 'cohesion', severity: 'suggestion'` that design source should be added |
|
|
251
|
+
| Auto-fix attempt introduces a syntax error in an SCSS/TSX file | `completed` | Revert the auto-fix, keep the finding as a non-fixed entry, do NOT silently leave the file broken |
|
|
252
|
+
|
|
253
|
+
## Integration
|
|
254
|
+
|
|
255
|
+
- **Loaded twice per round** (non-`claude_only` profiles):
|
|
256
|
+
1. `round-executor` Step 3.8 with `phase: 'style_only'` and empty `e2e_screenshots[]` — reviews the just-written code's tokens/spacing/typography/color/cohesion (mandatory when files_changed contains UI / styling files)
|
|
257
|
+
2. `/cbp-round-execute` Step 5b with `phase: 'screenshot_review'` and screenshots from `round.context.e2e_output.screenshots` — runs Phase 6.5 only (rendered-output review + baseline regressions). Skipped when no e2e ran (`claude_only` / `backend` / `has_ui_work === false`).
|
|
258
|
+
- **Also invoked by**: `/cbp-checkpoint-check` (TASK-2 deliverable, future) with screenshots from a whole-checkpoint e2e run
|
|
259
|
+
- **Consumes**: `e2e_screenshots[]` from `round.context.e2e_output.screenshots` (populated by `test-e2e-agent` at `/cbp-round-execute` Step 5)
|
|
260
|
+
- **Output written to**: `round.context.frontend_ui_review` — when invoked twice per round, the second invocation merges with the first
|
|
261
|
+
- **User-QA construction (downstream)**: this skill emits `findings[]` only — it does NOT construct user_qa items. `/cbp-round-end` Step 3b reads `round.context.frontend_ui_review.findings[]` and aggregates baseline-regression + rendered-visual-critical entries into the round's `user_qa[]`. Single source of truth for the user_qa schema lives at round-end.
|
|
262
|
+
- **Paired with**: `frontend-design` (pre-implementation aesthetic decision), `frontend-ux` (interaction-quality self-review, also Step 3.8)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# UI Label Maps
|
|
2
|
+
|
|
3
|
+
Pattern for humanizing DB column names, enum values, and trigger types when displayed in user-facing JSX.
|
|
4
|
+
|
|
5
|
+
When displaying database column names, enum values, trigger types, or status codes in user-facing JSX, define a `LABELS` const map in the same component file (or a shared labels module).
|
|
6
|
+
|
|
7
|
+
## Pattern
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const TRIGGER_LABELS: Record<string, string> = {
|
|
11
|
+
port_changed: "Port Changed",
|
|
12
|
+
repo_created: "Repo Created",
|
|
13
|
+
tech_stack_changed: "Tech Stack Changed",
|
|
14
|
+
port_conflict_detected: "Port Conflict",
|
|
15
|
+
created: "Created",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function formatTriggerLabel(trigger: string | null | undefined): string {
|
|
19
|
+
if (!trigger) return "Event";
|
|
20
|
+
return TRIGGER_LABELS[trigger] ?? trigger;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## When to Apply
|
|
25
|
+
|
|
26
|
+
- Rendering DB enum columns in JSX (status, type, action, trigger)
|
|
27
|
+
- Displaying changed_field arrays in audit log UIs
|
|
28
|
+
- Showing column names from JSONB context fields
|
|
29
|
+
- ANY snake_case string sourced from DB that a user will see
|
|
30
|
+
|
|
31
|
+
## Anti-Pattern
|
|
32
|
+
|
|
33
|
+
NEVER render `{event.trigger}` directly when trigger is a snake_case enum. Users see "PORT_CHANGED" with no context.
|
|
34
|
+
|
|
35
|
+
## Shared vs Local Maps
|
|
36
|
+
|
|
37
|
+
- **Local** (in component file): map is used in 1 component, fewer than ~10 entries
|
|
38
|
+
- **Shared** (`apps/web/src/lib/labels/`): map is used in 2+ components OR has 10+ entries
|
|
39
|
+
|
|
40
|
+
## Source
|
|
41
|
+
|
|
42
|
+
CHK-077 TASK-3 Round 2 required adding `TRIGGER_LABELS` + `FIELD_LABELS` maps to `TaskDetail.tsx` after testing-qa flagged raw snake_case in the event timeline.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# UI Layout Patterns
|
|
2
|
+
|
|
3
|
+
Navbar layout patterns, focus-visible accessibility conventions, and z-index layering. Ensures consistent navbar implementation and proper focus-visible accessibility across all client projects.
|
|
4
|
+
|
|
5
|
+
Apply when working with Navbar components, focus-visible styles, or z-index layering in `.scss` files.
|
|
6
|
+
|
|
7
|
+
## 1. Navbar Height is Fluid
|
|
8
|
+
|
|
9
|
+
```scss
|
|
10
|
+
// layout/_variables.scss
|
|
11
|
+
$navbar-height: fluid(3.5rem, 4.5rem); // 56px -> 72px responsive
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
All navbar molecules use the layout variable:
|
|
15
|
+
|
|
16
|
+
```scss
|
|
17
|
+
@use '../../../../styles/layout' as layout;
|
|
18
|
+
.desktop { height: layout.$navbar-height; }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 2. Logo Sizing Based on Navbar
|
|
22
|
+
|
|
23
|
+
Logo size derives from navbar height:
|
|
24
|
+
|
|
25
|
+
```scss
|
|
26
|
+
$_logo-nav-size: calc(#{layout.$navbar-height} * 0.6);
|
|
27
|
+
|
|
28
|
+
.logo {
|
|
29
|
+
&--nav { font-size: $_logo-nav-size; }
|
|
30
|
+
&--nav-mobile { height: layout.$navbar-height; font-size: $_logo-nav-size; }
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 3. Logo Overflow
|
|
35
|
+
|
|
36
|
+
Logo can overflow navbar with absolute positioning:
|
|
37
|
+
|
|
38
|
+
```scss
|
|
39
|
+
.logo {
|
|
40
|
+
&--nav {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: 50%;
|
|
43
|
+
transform: translateY(-50%);
|
|
44
|
+
z-index: calc(layout.z-index('navbar') + 1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.desktop {
|
|
49
|
+
position: relative; // Positioning context
|
|
50
|
+
overflow: visible; // Allow overflow
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 4. Navbar Variants
|
|
55
|
+
|
|
56
|
+
| Variant | Component | Height | Notes |
|
|
57
|
+
|---------|-----------|--------|-------|
|
|
58
|
+
| Desktop | Desktop molecule | $navbar-height | Logo overflows |
|
|
59
|
+
| Mobile (collapsed) | MobileOpen | $navbar-height | Logo inside |
|
|
60
|
+
| Mobile (expanded) | MobileClose | 100vh | Full-screen overlay |
|
|
61
|
+
|
|
62
|
+
## 5. Z-Index Layers
|
|
63
|
+
|
|
64
|
+
```scss
|
|
65
|
+
layout.z-index('navbar') // Base navbar layer
|
|
66
|
+
layout.z-index('navbar') + 1 // Logo (above navbar)
|
|
67
|
+
layout.z-index('overlay') // Mobile menu overlay
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 6. Focus-Visible: Always Use the Mixin
|
|
71
|
+
|
|
72
|
+
NEVER write inline `:focus-visible` styles. Always use accessibility module mixins:
|
|
73
|
+
|
|
74
|
+
```scss
|
|
75
|
+
@use '../../styles/accessibility' as a11y;
|
|
76
|
+
|
|
77
|
+
// CORRECT
|
|
78
|
+
.button { @include a11y.focus-visible; }
|
|
79
|
+
|
|
80
|
+
// WRONG
|
|
81
|
+
.button { &:focus-visible { outline: 2px solid #00a7b3; } }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 7. Available Focus Mixins
|
|
85
|
+
|
|
86
|
+
| Mixin | Use Case |
|
|
87
|
+
|-------|----------|
|
|
88
|
+
| `focus-visible` | Standard (2px solid, 2px offset) |
|
|
89
|
+
| `focus-visible-offset($offset)` | Custom offset |
|
|
90
|
+
| `focus-visible-inset` | Inset outline (-2px offset) |
|
|
91
|
+
|
|
92
|
+
## 8. Border-Radius Affects Focus Outline
|
|
93
|
+
|
|
94
|
+
Add `border-radius` for rounded focus outline:
|
|
95
|
+
|
|
96
|
+
```scss
|
|
97
|
+
.logo {
|
|
98
|
+
border-radius: 4px;
|
|
99
|
+
@include a11y.focus-visible;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 9. Focus vs Hover Colors
|
|
104
|
+
|
|
105
|
+
Use `:focus-visible` (not `:focus`) — shows only on keyboard navigation. Different colors for hover (`$hover-active`) vs focus (`$focus-outline`).
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Variant Defaults & Button Patterns
|
|
2
|
+
|
|
3
|
+
3-layer variant architecture using `createResolver` with SIZE, COLOR, and CUSTOM variants, lightweight variant maps for simpler components, plus button-specific patterns.
|
|
4
|
+
|
|
5
|
+
Apply when working with `variants.ts` files, components in `src/components/buttons/`, or components using size/color variant props with BEM modifier classes. Ensures correct variant resolution order and prevents duplicate defaults.
|
|
6
|
+
|
|
7
|
+
## 1. 3-Layer Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Props -> createResolver -> CSS Classes
|
|
11
|
+
Resolution Order:
|
|
12
|
+
1. Custom variant (presets)
|
|
13
|
+
2. Size variant (typography/spacing)
|
|
14
|
+
3. Color variant (colors/interactions)
|
|
15
|
+
4. Direct prop overrides (highest priority)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 2. Never Duplicate Defaults
|
|
19
|
+
|
|
20
|
+
Values in SIZE/COLOR/CUSTOM_VARIANTS MUST NOT repeat defaults:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// WRONG
|
|
24
|
+
const defaults = { fontWeight: 'medium', typoPx: 's1' };
|
|
25
|
+
const SIZE_VARIANTS = {
|
|
26
|
+
md: { fontSize: 'body-md', fontWeight: 'medium' }, // duplicates default
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// CORRECT
|
|
30
|
+
const SIZE_VARIANTS = {
|
|
31
|
+
md: { fontSize: 'body-md' }, // only what differs
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 3. Layer Definitions
|
|
36
|
+
|
|
37
|
+
**SIZE_VARIANTS** (typography/spacing):
|
|
38
|
+
```typescript
|
|
39
|
+
const SIZE_VARIANTS = {
|
|
40
|
+
sm: { fontSize: 'body-sm' },
|
|
41
|
+
md: { fontSize: 'body-md' },
|
|
42
|
+
lg: { fontSize: 'body-lg', typoPx: 's15', typoPy: 's1' },
|
|
43
|
+
} as const satisfies Record<string, SizeConfig>;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**COLOR_VARIANTS** (colors/interactions):
|
|
47
|
+
```typescript
|
|
48
|
+
const COLOR_VARIANTS = {
|
|
49
|
+
primary: { bg: 'primary', text: 'inverse' },
|
|
50
|
+
highlight: { bg: 'highlight', text: 'primary' },
|
|
51
|
+
transparent: { bg: 'transparent', text: 'primary' },
|
|
52
|
+
} as const satisfies Record<string, ColorConfig>;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**CUSTOM_VARIANTS** (presets combining size + color + overrides):
|
|
56
|
+
```typescript
|
|
57
|
+
const CUSTOM_VARIANTS = {
|
|
58
|
+
cta: {
|
|
59
|
+
colorVariant: 'highlight',
|
|
60
|
+
fontFamily: 'accent',
|
|
61
|
+
glow: 'sm',
|
|
62
|
+
},
|
|
63
|
+
} as const satisfies Record<string, CustomConfig>;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 4. Type Annotations Required
|
|
67
|
+
|
|
68
|
+
All variant maps MUST use `as const satisfies Record<string, *Config>`.
|
|
69
|
+
|
|
70
|
+
## 5. createResolver Setup
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { createResolver, SizeConfig, ColorConfig, CustomConfig } from '@/utils/variants';
|
|
74
|
+
|
|
75
|
+
export const getVariant = createResolver({
|
|
76
|
+
sizes: SIZE_VARIANTS,
|
|
77
|
+
colors: COLOR_VARIANTS,
|
|
78
|
+
custom: CUSTOM_VARIANTS,
|
|
79
|
+
defaults,
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 6. Button Types
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
src/components/buttons/
|
|
87
|
+
├── Button/ # Form submit, modal triggers, state changes
|
|
88
|
+
├── LinkButton/ # Navigation (links)
|
|
89
|
+
└── IconButton/ # Icon-only actions
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
All buttons use `createResolver`. Each has 5 files: `index.ts`, `Component.tsx`, `Component.module.scss`, `types.ts`, `variants.ts`.
|
|
93
|
+
|
|
94
|
+
## 7. Button Defaults
|
|
95
|
+
|
|
96
|
+
| Button | Default Size | Default Color |
|
|
97
|
+
|--------|--------------|---------------|
|
|
98
|
+
| Button | md | primary |
|
|
99
|
+
| LinkButton | md | transparent |
|
|
100
|
+
| IconButton | md | transparent |
|
|
101
|
+
|
|
102
|
+
## 8. SCSS Modifier Includes
|
|
103
|
+
|
|
104
|
+
All buttons include the standard modifier mixins:
|
|
105
|
+
|
|
106
|
+
```scss
|
|
107
|
+
.button {
|
|
108
|
+
@include typography.font-family;
|
|
109
|
+
@include typography.font-weight;
|
|
110
|
+
@include typography.font-size;
|
|
111
|
+
@include typography.tracking;
|
|
112
|
+
@include typography.leading;
|
|
113
|
+
@include typography.transform;
|
|
114
|
+
@include typography.typo-px;
|
|
115
|
+
@include typography.typo-py;
|
|
116
|
+
@include typography.typo-gap;
|
|
117
|
+
@include borders.border-radius;
|
|
118
|
+
@include borders.border-width;
|
|
119
|
+
@include borders.border-color;
|
|
120
|
+
@include interactive.color-interactive;
|
|
121
|
+
@include shadows.glow;
|
|
122
|
+
@include a11y.focus-visible;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 9. Lightweight Variant Maps (Alternative to createResolver)
|
|
127
|
+
|
|
128
|
+
For components that only need prop-to-BEM-suffix resolution without the full variant pipeline, use simple `Record<TypeUnion, string>` maps or direct prop interpolation.
|
|
129
|
+
|
|
130
|
+
**When to use**: Component has 1-2 variant dimensions (size, color) with no defaults merging, no custom presets, and no resolution priority logic. Common in icon components and simple wrappers.
|
|
131
|
+
|
|
132
|
+
**Pattern A — Direct prop interpolation** (no variants.ts needed):
|
|
133
|
+
```typescript
|
|
134
|
+
// Prop value IS the BEM suffix — no map needed
|
|
135
|
+
styles[`spinner--${size}`]
|
|
136
|
+
styles[`checkmark--text-${color}`]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Pattern B — Explicit variant map** (in variants.ts):
|
|
140
|
+
```typescript
|
|
141
|
+
export const SIZE_VARIANTS: Record<ContainerSize, string> = {
|
|
142
|
+
sm: 'sm',
|
|
143
|
+
md: 'md',
|
|
144
|
+
lg: 'lg',
|
|
145
|
+
}
|
|
146
|
+
// Usage: styles[`container--${SIZE_VARIANTS[size]}`]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Decision rule**: If prop values map 1:1 to BEM suffixes, use Pattern A. If mapping differs from prop names or you want a centralized variants.ts, use Pattern B. If you need defaults merging, resolution priority, or custom presets, use `createResolver` (sections 1-5).
|