jdi-cli 0.1.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/AGENTS.md +209 -0
- package/ARCHITECTURE.md +210 -0
- package/COMMANDS.md +241 -0
- package/CREATE-EXAMPLE.md +385 -0
- package/CREATE.md +315 -0
- package/EXTENSION.md +141 -0
- package/LICENSE +21 -0
- package/MEMORY.md +471 -0
- package/PORTABILITY.md +438 -0
- package/README.md +789 -0
- package/bin/git-hooks/post-commit +16 -0
- package/bin/git-hooks/pre-commit +21 -0
- package/bin/jdi-build.ps1 +381 -0
- package/bin/jdi-build.sh +332 -0
- package/bin/jdi-doctor.ps1 +403 -0
- package/bin/jdi-doctor.sh +400 -0
- package/bin/jdi-install-caveman.ps1 +97 -0
- package/bin/jdi-install-caveman.sh +99 -0
- package/bin/jdi-install-playwright.ps1 +319 -0
- package/bin/jdi-install-playwright.sh +284 -0
- package/bin/jdi-install.ps1 +154 -0
- package/bin/jdi-install.sh +132 -0
- package/bin/jdi-uninstall.ps1 +309 -0
- package/bin/jdi-uninstall.sh +264 -0
- package/bin/jdi-update.ps1 +215 -0
- package/bin/jdi-update.sh +209 -0
- package/bin/jdi.js +460 -0
- package/bin/lib/jdi-monitor.ps1 +66 -0
- package/bin/lib/jdi-monitor.sh +74 -0
- package/bin/lib/jdi-truncate.ps1 +96 -0
- package/bin/lib/jdi-truncate.sh +99 -0
- package/bin/lib/ui.js +197 -0
- package/core/agents/jdi-adopter.md +465 -0
- package/core/agents/jdi-architect.md +894 -0
- package/core/agents/jdi-asker.md +153 -0
- package/core/agents/jdi-bootstrap.md +247 -0
- package/core/agents/jdi-planner.md +254 -0
- package/core/agents/jdi-researcher.md +303 -0
- package/core/commands/jdi-adopt.md +155 -0
- package/core/commands/jdi-bootstrap.md +81 -0
- package/core/commands/jdi-create.md +80 -0
- package/core/commands/jdi-discuss.md +80 -0
- package/core/commands/jdi-do.md +200 -0
- package/core/commands/jdi-loop.md +315 -0
- package/core/commands/jdi-new.md +131 -0
- package/core/commands/jdi-plan.md +73 -0
- package/core/commands/jdi-ship.md +146 -0
- package/core/commands/jdi-verify.md +159 -0
- package/core/skills/clean-code/SKILL.md +261 -0
- package/core/skills/dry/SKILL.md +150 -0
- package/core/skills/frontend-rules/SKILL.md +386 -0
- package/core/skills/frontend-validator/SKILL.md +567 -0
- package/core/skills/kiss/SKILL.md +178 -0
- package/core/skills/solid/SKILL.md +281 -0
- package/core/skills/yagni/SKILL.md +207 -0
- package/core/templates/agent.md +72 -0
- package/core/templates/doer-specialist.md +216 -0
- package/core/templates/reviewer-specialist.md +405 -0
- package/core/templates/skill.md +66 -0
- package/package.json +70 -0
- package/runtimes/antigravity/agents.md +74 -0
- package/runtimes/antigravity/skills/clean-code/SKILL.md +252 -0
- package/runtimes/antigravity/skills/dry/SKILL.md +141 -0
- package/runtimes/antigravity/skills/frontend-rules/SKILL.md +376 -0
- package/runtimes/antigravity/skills/frontend-validator/SKILL.md +559 -0
- package/runtimes/antigravity/skills/jdi-adopt/SKILL.md +155 -0
- package/runtimes/antigravity/skills/jdi-adopter/SKILL.md +436 -0
- package/runtimes/antigravity/skills/jdi-architect/SKILL.md +872 -0
- package/runtimes/antigravity/skills/jdi-asker/SKILL.md +125 -0
- package/runtimes/antigravity/skills/jdi-asker/references/context-template.md +34 -0
- package/runtimes/antigravity/skills/jdi-asker/references/decision-format.md +19 -0
- package/runtimes/antigravity/skills/jdi-asker/scripts/find_phase_dir.sh +25 -0
- package/runtimes/antigravity/skills/jdi-bootstrap/SKILL.md +81 -0
- package/runtimes/antigravity/skills/jdi-create/SKILL.md +80 -0
- package/runtimes/antigravity/skills/jdi-discuss/SKILL.md +80 -0
- package/runtimes/antigravity/skills/jdi-discuss/scripts/run_command.sh +62 -0
- package/runtimes/antigravity/skills/jdi-do/SKILL.md +200 -0
- package/runtimes/antigravity/skills/jdi-loop/SKILL.md +315 -0
- package/runtimes/antigravity/skills/jdi-new/SKILL.md +131 -0
- package/runtimes/antigravity/skills/jdi-plan/SKILL.md +73 -0
- package/runtimes/antigravity/skills/jdi-planner/SKILL.md +225 -0
- package/runtimes/antigravity/skills/jdi-researcher/SKILL.md +274 -0
- package/runtimes/antigravity/skills/jdi-ship/SKILL.md +146 -0
- package/runtimes/antigravity/skills/jdi-verify/SKILL.md +159 -0
- package/runtimes/antigravity/skills/kiss/SKILL.md +169 -0
- package/runtimes/antigravity/skills/solid/SKILL.md +272 -0
- package/runtimes/antigravity/skills/yagni/SKILL.md +198 -0
- package/runtimes/claude/CLAUDE.md +91 -0
- package/runtimes/claude/agents/jdi-adopter.md +430 -0
- package/runtimes/claude/agents/jdi-architect.md +864 -0
- package/runtimes/claude/agents/jdi-asker.md +119 -0
- package/runtimes/claude/agents/jdi-bootstrap.md +213 -0
- package/runtimes/claude/agents/jdi-planner.md +221 -0
- package/runtimes/claude/agents/jdi-researcher.md +269 -0
- package/runtimes/claude/commands/jdi-adopt.md +155 -0
- package/runtimes/claude/commands/jdi-bootstrap.md +81 -0
- package/runtimes/claude/commands/jdi-create.md +80 -0
- package/runtimes/claude/commands/jdi-discuss.md +80 -0
- package/runtimes/claude/commands/jdi-do.md +200 -0
- package/runtimes/claude/commands/jdi-loop.md +315 -0
- package/runtimes/claude/commands/jdi-new.md +131 -0
- package/runtimes/claude/commands/jdi-plan.md +73 -0
- package/runtimes/claude/commands/jdi-ship.md +146 -0
- package/runtimes/claude/commands/jdi-verify.md +159 -0
- package/runtimes/claude/settings.example.json +132 -0
- package/runtimes/claude/skills/clean-code/SKILL.md +247 -0
- package/runtimes/claude/skills/dry/SKILL.md +136 -0
- package/runtimes/claude/skills/frontend-rules/SKILL.md +369 -0
- package/runtimes/claude/skills/frontend-validator/SKILL.md +553 -0
- package/runtimes/claude/skills/kiss/SKILL.md +164 -0
- package/runtimes/claude/skills/solid/SKILL.md +267 -0
- package/runtimes/claude/skills/yagni/SKILL.md +193 -0
- package/runtimes/copilot/agents/jdi-adopter.agent.md +430 -0
- package/runtimes/copilot/agents/jdi-architect.agent.md +864 -0
- package/runtimes/copilot/agents/jdi-asker.agent.md +119 -0
- package/runtimes/copilot/agents/jdi-bootstrap.agent.md +213 -0
- package/runtimes/copilot/agents/jdi-planner.agent.md +221 -0
- package/runtimes/copilot/agents/jdi-researcher.agent.md +269 -0
- package/runtimes/copilot/copilot-instructions.md +80 -0
- package/runtimes/copilot/prompts/jdi-adopt.prompt.md +155 -0
- package/runtimes/copilot/prompts/jdi-bootstrap.prompt.md +81 -0
- package/runtimes/copilot/prompts/jdi-create.prompt.md +80 -0
- package/runtimes/copilot/prompts/jdi-discuss.prompt.md +80 -0
- package/runtimes/copilot/prompts/jdi-do.prompt.md +200 -0
- package/runtimes/copilot/prompts/jdi-loop.prompt.md +315 -0
- package/runtimes/copilot/prompts/jdi-new.prompt.md +131 -0
- package/runtimes/copilot/prompts/jdi-plan.prompt.md +73 -0
- package/runtimes/copilot/prompts/jdi-ship.prompt.md +146 -0
- package/runtimes/copilot/prompts/jdi-verify.prompt.md +159 -0
- package/runtimes/opencode/AGENTS.md +87 -0
- package/runtimes/opencode/agents/jdi-adopter.md +434 -0
- package/runtimes/opencode/agents/jdi-architect.md +861 -0
- package/runtimes/opencode/agents/jdi-asker.md +123 -0
- package/runtimes/opencode/agents/jdi-bootstrap.md +217 -0
- package/runtimes/opencode/agents/jdi-planner.md +225 -0
- package/runtimes/opencode/agents/jdi-researcher.md +273 -0
- package/runtimes/opencode/commands/jdi-adopt.md +155 -0
- package/runtimes/opencode/commands/jdi-bootstrap.md +81 -0
- package/runtimes/opencode/commands/jdi-create.md +80 -0
- package/runtimes/opencode/commands/jdi-discuss.md +80 -0
- package/runtimes/opencode/commands/jdi-do.md +200 -0
- package/runtimes/opencode/commands/jdi-loop.md +315 -0
- package/runtimes/opencode/commands/jdi-new.md +131 -0
- package/runtimes/opencode/commands/jdi-plan.md +73 -0
- package/runtimes/opencode/commands/jdi-ship.md +146 -0
- package/runtimes/opencode/commands/jdi-verify.md +159 -0
- package/runtimes/opencode/opencode.example.jsonc +169 -0
- package/runtimes/opencode/skills/clean-code/SKILL.md +247 -0
- package/runtimes/opencode/skills/dry/SKILL.md +136 -0
- package/runtimes/opencode/skills/frontend-rules/SKILL.md +369 -0
- package/runtimes/opencode/skills/frontend-validator/SKILL.md +553 -0
- package/runtimes/opencode/skills/kiss/SKILL.md +164 -0
- package/runtimes/opencode/skills/solid/SKILL.md +267 -0
- package/runtimes/opencode/skills/yagni/SKILL.md +193 -0
- package/templates-jdi-folder/config.json +18 -0
- package/templates-jdi-folder/registry.md +31 -0
- package/templates-jdi-folder/reviewers.md +33 -0
- package/templates-jdi-folder/skills-registry.md +32 -0
- package/templates-jdi-folder/specialists.md +39 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dry
|
|
3
|
+
description: DRY (Don't Repeat Yourself). 1 source of truth per piece of knowledge. Detects real duplication (same decision in 2+ places) and separates from apparent duplication (same code, different reasons). Applies in any language.
|
|
4
|
+
type: skill
|
|
5
|
+
applies_to: |
|
|
6
|
+
Loaded by doer before writing new code.
|
|
7
|
+
Loaded by reviewer at gate 5 to detect duplication in diff.
|
|
8
|
+
loaded_by:
|
|
9
|
+
- jdi-doer-{slug}
|
|
10
|
+
- jdi-reviewer-{slug}
|
|
11
|
+
runtime_overrides:
|
|
12
|
+
antigravity:
|
|
13
|
+
triggers:
|
|
14
|
+
- "DRY"
|
|
15
|
+
- "code duplication"
|
|
16
|
+
- "Don't Repeat Yourself"
|
|
17
|
+
- "duplicate refactor"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Skill: DRY
|
|
21
|
+
|
|
22
|
+
> Every piece of knowledge has **one** authoritative, unambiguous representation in the system.
|
|
23
|
+
|
|
24
|
+
DRY is not about copying code. It's about **duplicated knowledge** — business rule, formula, format, decision, present in 2+ places and that change **together** when the requirement changes.
|
|
25
|
+
|
|
26
|
+
## Rules
|
|
27
|
+
|
|
28
|
+
### 1. Knowledge duplication != code coincidence
|
|
29
|
+
|
|
30
|
+
**Knowledge duplication (violates DRY):**
|
|
31
|
+
- Tax calculation in 2 places
|
|
32
|
+
- CPF validation in 3 services
|
|
33
|
+
- Date format schema scattered across the app
|
|
34
|
+
- Same timeout constant in 4 files
|
|
35
|
+
|
|
36
|
+
When the rule changes, you change in N places — miss one, system becomes inconsistent.
|
|
37
|
+
|
|
38
|
+
**Code coincidence (does NOT violate DRY):**
|
|
39
|
+
- 2 functions with 5 identical lines but different domains
|
|
40
|
+
- Framework boilerplate repeated (every controller has `[Authorize]`)
|
|
41
|
+
- Similar iteration loop in 2 unrelated contexts
|
|
42
|
+
|
|
43
|
+
Forcing abstraction here couples things that shouldn't be coupled.
|
|
44
|
+
|
|
45
|
+
### 2. Rule of 3
|
|
46
|
+
|
|
47
|
+
- 1 occurrence: leave it
|
|
48
|
+
- 2 occurrences: pay attention, but don't abstract yet
|
|
49
|
+
- 3 occurrences: abstract (probably real knowledge duplication)
|
|
50
|
+
|
|
51
|
+
Skipping this rule produces **premature abstraction** — worse than duplication because callers are already coupled to the wrong interface.
|
|
52
|
+
|
|
53
|
+
### 3. Types of DRY
|
|
54
|
+
|
|
55
|
+
**Code DRY:** reusable functions/classes for repeated logic
|
|
56
|
+
**Data DRY:** 1 source schema (generates DTO + validator + DB + docs)
|
|
57
|
+
**Process DRY:** 1 build script that serves dev + CI + prod
|
|
58
|
+
**Documentation DRY:** docs generated from code, not written in parallel
|
|
59
|
+
|
|
60
|
+
### 4. Single Source of Truth (SSoT)
|
|
61
|
+
|
|
62
|
+
For each piece of knowledge, identify:
|
|
63
|
+
- **Where the truth lives** (DB schema, env config, business rule)
|
|
64
|
+
- **Who derives from it** (DTOs, types, docs, UI)
|
|
65
|
+
- **Derivation tool** (codegen, schema migration, type inference)
|
|
66
|
+
|
|
67
|
+
Never: manually edit both the source and the derived.
|
|
68
|
+
|
|
69
|
+
### 5. When NOT to apply DRY
|
|
70
|
+
|
|
71
|
+
- **Premature abstraction**: 2 similar callers but with diverging future evolutions -> leave duplicated
|
|
72
|
+
- **Cross-boundary**: duplicating across microservices > coupling via shared lib
|
|
73
|
+
- **Test setup**: readable redundant tests > shared magical helpers
|
|
74
|
+
- **Wrong abstraction**: better duplicate than extract wrong (Sandi Metz: "duplication is far cheaper than the wrong abstraction")
|
|
75
|
+
|
|
76
|
+
## Anti-patterns
|
|
77
|
+
|
|
78
|
+
| Anti-pattern | Why it violates |
|
|
79
|
+
|---|---|
|
|
80
|
+
| Copy-paste without extraction after 3rd occurrence | Duplicated knowledge, each caller silently diverges |
|
|
81
|
+
| Utility helper with 15 unrelated functions | "DRY" became ball of mud — bundles unrelated things |
|
|
82
|
+
| Abstraction after 2nd duplication with extension hooks "just in case" | Premature abstraction + YAGNI violated together |
|
|
83
|
+
| Same constant hardcoded in 4 files | Single source of truth absent — extract to config |
|
|
84
|
+
| Validation logic duplicated client + server | Code OK, but should share schema (Zod, Pydantic, JSON Schema) |
|
|
85
|
+
| Comment explaining what code does | Doc duplicates code, will go out of sync |
|
|
86
|
+
|
|
87
|
+
## Procedure
|
|
88
|
+
|
|
89
|
+
### Doer (before writing)
|
|
90
|
+
|
|
91
|
+
1. Is there an identical rule/calculation/constant elsewhere in the codebase? If so, refer/import. Don't duplicate.
|
|
92
|
+
2. Going to create the 2nd occurrence? OK, but mentally mark it.
|
|
93
|
+
3. Going to create the 3rd? Stop. Refactor first into abstraction, then use.
|
|
94
|
+
|
|
95
|
+
### Reviewer (gate 5)
|
|
96
|
+
|
|
97
|
+
Per-language specific greps (examples):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Duplicated magic constants
|
|
101
|
+
grep -RnE '\b86400\b|\b3600\b|\b1024\b' src/
|
|
102
|
+
|
|
103
|
+
# Same string in 3+ places
|
|
104
|
+
sort src/ | uniq -c | sort -rn | head -20 # coarse heuristic
|
|
105
|
+
|
|
106
|
+
# Duplicated email/CPF/etc validation
|
|
107
|
+
grep -RnE 'regex.*@|EmailRegex|CpfValidator|cpf_pattern' src/
|
|
108
|
+
|
|
109
|
+
# Hardcoded URLs/endpoints
|
|
110
|
+
grep -RnE 'https?://[a-z0-9.-]+\.[a-z]{2,}' src/ --include='*.ts' --include='*.cs' --include='*.py'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
3+ matches of the same pattern in different files -> WARN.
|
|
114
|
+
|
|
115
|
+
## Inputs
|
|
116
|
+
|
|
117
|
+
- Diff or content of the modified file
|
|
118
|
+
- Context: project stack for adapted greps
|
|
119
|
+
|
|
120
|
+
## Outputs
|
|
121
|
+
|
|
122
|
+
Does NOT produce a file. Modifies judgement:
|
|
123
|
+
- Doer chooses to reuse/extract
|
|
124
|
+
- Reviewer marks WARN with pointer to refactor
|
|
125
|
+
|
|
126
|
+
## Examples
|
|
127
|
+
|
|
128
|
+
### Example 1: 3 services calculating tax
|
|
129
|
+
|
|
130
|
+
Wrong:
|
|
131
|
+
```
|
|
132
|
+
service A: total * 0.18
|
|
133
|
+
service B: amount * 0.18
|
|
134
|
+
service C: value * 0.18
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Right: extract `TaxCalculator.applyVat(amount)` or `const VAT_RATE = 0.18` in shared config.
|
|
138
|
+
|
|
139
|
+
Reviewer marks WARN: "tax 0.18 hardcoded in 3 services. Extract to `config/tax.{ts,cs,py}`."
|
|
140
|
+
|
|
141
|
+
### Example 2: Code coincidence (does NOT violate DRY)
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
function findUser(id) { return db.query(...) }
|
|
145
|
+
function findOrder(id) { return db.query(...) }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Same structure, different domains. **Don't** extract `findEntity(id, table)` — will force abstraction that will diverge (user has soft-delete, order has cache, etc).
|
|
149
|
+
|
|
150
|
+
Reviewer ignores — code coincidence is OK.
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jdi-frontend-rules
|
|
3
|
+
description: Universal UI/UX and accessibility rules for any web interface. Framework-agnostic - works for React, Vue, Svelte, Solid, Angular, Blazor, Razor, Twig, Jinja, ERB, Blade, and any template engine. Based on WCAG 2.2 AA, Nielsen heuristics, Material/Apple HIG.
|
|
4
|
+
type: skill
|
|
5
|
+
applies_to: |
|
|
6
|
+
Loaded when PROJECT.md has `frontend.has_frontend: true`.
|
|
7
|
+
Doer applies as guide BEFORE writing/editing UI files.
|
|
8
|
+
Reviewer applies as checklist at gate 5 (security/perf rules) before gate 7 (UI live).
|
|
9
|
+
loaded_by:
|
|
10
|
+
- jdi-doer-{slug} (conditional on frontend.has_frontend)
|
|
11
|
+
- jdi-reviewer-{slug} (conditional on frontend.has_frontend)
|
|
12
|
+
runtime_overrides:
|
|
13
|
+
antigravity:
|
|
14
|
+
triggers:
|
|
15
|
+
- "frontend rules"
|
|
16
|
+
- "UI patterns"
|
|
17
|
+
- "web accessibility"
|
|
18
|
+
- "WCAG"
|
|
19
|
+
- "UX best practices"
|
|
20
|
+
- "validate interface"
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Skill: jdi-frontend-rules
|
|
24
|
+
|
|
25
|
+
UI/UX standards that CANNOT be violated - regardless of stack. Concepts > syntax. Works for SPA, SSR, MPA, hybrid, any template engine.
|
|
26
|
+
|
|
27
|
+
## When to apply
|
|
28
|
+
|
|
29
|
+
Whenever code touches a visible human interface:
|
|
30
|
+
|
|
31
|
+
- Files `.tsx, .jsx, .vue, .svelte, .astro, .qwik, .solid` (JS-based components)
|
|
32
|
+
- Files `.razor, .cshtml` (Blazor / Razor Pages / MVC)
|
|
33
|
+
- Files `.html, .twig, .jinja, .j2, .erb, .blade.php, .hbs, .liquid, .mustache, .ejs, .pug` (template engines)
|
|
34
|
+
- CSS/Tailwind/SCSS/Less affecting layout, contrast, focus, or accessibility
|
|
35
|
+
- ARIA / semantic HTML in any language
|
|
36
|
+
|
|
37
|
+
Does NOT apply to: API-only backends, CLI tools, services without UI.
|
|
38
|
+
|
|
39
|
+
## Universal rules (hard gates)
|
|
40
|
+
|
|
41
|
+
### 1. Accessibility - WCAG 2.2 level AA
|
|
42
|
+
|
|
43
|
+
All mandatory. Violation = BLOCK on review.
|
|
44
|
+
|
|
45
|
+
- **Color contrast**:
|
|
46
|
+
- Normal text: minimum 4.5:1 against background
|
|
47
|
+
- Large text (18pt+ or 14pt+ bold): minimum 3:1
|
|
48
|
+
- UI components and graphics: minimum 3:1
|
|
49
|
+
- Verify in hover/focus/disabled states too
|
|
50
|
+
- **Visible focus**: never `outline: none` or `outline: 0` without a replacement. Focus must be perceptible in strong light and on a cheap monitor. `:focus-visible` is the standard
|
|
51
|
+
- **Keyboard navigation**: 100% of interactions reachable via keyboard. Tab follows logical visual order. No trap (modal without Esc, dropdown without Escape/arrows)
|
|
52
|
+
- **Semantic HTML first**:
|
|
53
|
+
- `<button>` for action (even if styled as a link)
|
|
54
|
+
- `<a href>` for navigation (even if styled as a button)
|
|
55
|
+
- `<form>` for forms (Enter submits, native validation works)
|
|
56
|
+
- Headings in order (`h1` -> `h2` -> `h3`, no level skipping)
|
|
57
|
+
- `<nav>, <main>, <header>, <footer>, <aside>, <section>, <article>` when appropriate
|
|
58
|
+
- `<ul>/<ol>` for lists, not repeated `<div>`
|
|
59
|
+
- **ARIA when needed**:
|
|
60
|
+
- Icon-only button: `aria-label="descriptive action"`
|
|
61
|
+
- Form error: `role="alert"` or `aria-live="assertive"`
|
|
62
|
+
- Loading region: `aria-busy="true"` + `aria-live="polite"`
|
|
63
|
+
- Toggle/expand: `aria-expanded="true|false"` + `aria-controls`
|
|
64
|
+
- Modal: `role="dialog"` + `aria-modal="true"` + `aria-labelledby`
|
|
65
|
+
- Tooltip: `aria-describedby`
|
|
66
|
+
- ARIA NEVER REPLACES semantic HTML. ARIA only complements
|
|
67
|
+
- **Skip link**: first tab order offers "Skip to main content"
|
|
68
|
+
- **Minimum touch target**: 44x44 CSS px (Apple HIG / WCAG 2.5.5). Increase on mobile with `padding`, not margin
|
|
69
|
+
- **Color is not the only indicator**:
|
|
70
|
+
- Red error needs icon OR explicit text
|
|
71
|
+
- Colored link needs underline OR different visual weight
|
|
72
|
+
- Active nav state needs border/weight, not just color
|
|
73
|
+
- Color blindness affects 8% of men. Always color + shape + text
|
|
74
|
+
- **Form labels**: every `<input>, <textarea>, <select>` with:
|
|
75
|
+
- Associated `<label htmlFor="id">`, OR
|
|
76
|
+
- `aria-label="..."`, OR
|
|
77
|
+
- `aria-labelledby="id-of-another-element"`
|
|
78
|
+
- Placeholder DOES NOT count as label (disappears when user types)
|
|
79
|
+
- **Associated error**: field error connected via `aria-describedby="error-id"`. Error text has matching `id`
|
|
80
|
+
- **Language**: `<html lang="pt-BR">` declared. Without this screen reader reads English for pt-BR text
|
|
81
|
+
- **prefers-reduced-motion**: respect. Animations should disable via `@media (prefers-reduced-motion: reduce)`
|
|
82
|
+
|
|
83
|
+
### 2. Mandatory states on every UI surface
|
|
84
|
+
|
|
85
|
+
Every screen/component that loads or mutates data must cover all 5:
|
|
86
|
+
|
|
87
|
+
- **Loading**:
|
|
88
|
+
- Skeleton with shape matching real content (avoids layout shift)
|
|
89
|
+
- OR spinner/progress if shape unpredictable
|
|
90
|
+
- Visible minimum 200ms (avoids flash that flickers)
|
|
91
|
+
- Maximum 10s without extra feedback - after that explain "almost there" or offer cancel
|
|
92
|
+
- **Empty**:
|
|
93
|
+
- Never empty screen. Always message + icon + actionable CTA
|
|
94
|
+
- Text orients next step: "Create your first X by clicking Y"
|
|
95
|
+
- Don't confuse empty with error (empty is success, error is failure)
|
|
96
|
+
- **Error**:
|
|
97
|
+
- Specific message: what failed + how to fix
|
|
98
|
+
- NEVER "Something went wrong" / "Unexpected error" as final message to user
|
|
99
|
+
- Visible recovery action: retry, go back, contact support
|
|
100
|
+
- Inline validation errors + general message if needed
|
|
101
|
+
- **Success**:
|
|
102
|
+
- Visible confirmation - toast is OK for non-destructive actions
|
|
103
|
+
- Destructive action (delete, transfer) needs undo OR prior confirmation
|
|
104
|
+
- Toast disappears in 4-6s; destructive actions with undo have 5-10s
|
|
105
|
+
- **Disabled**:
|
|
106
|
+
- ALWAYS with visible reason: tooltip, helper text, or hint
|
|
107
|
+
- Silent disabled = bug ("why can't I click?")
|
|
108
|
+
- Consider alternative: don't disable, let click and show specific error
|
|
109
|
+
|
|
110
|
+
### 3. Feedback timing - Nielsen heuristics
|
|
111
|
+
|
|
112
|
+
- **< 100ms**: feels instant. No indicator needed
|
|
113
|
+
- **100ms to 1s**: acceptable without indicator. Cursor may change to waiting
|
|
114
|
+
- **1s to 10s**: progress indicator required. Spinner or bar
|
|
115
|
+
- **> 10s**: progress + estimated time OR allow cancel
|
|
116
|
+
- **Indeterminate and > 30s**: offer background notification, free up UI
|
|
117
|
+
- **Optimistic UI**: like/save/toggle - update UI immediately, rollback if it fails
|
|
118
|
+
|
|
119
|
+
### 4. Forms - universal patterns
|
|
120
|
+
|
|
121
|
+
- **Validation**:
|
|
122
|
+
- On blur for individual field (after user leaves field)
|
|
123
|
+
- On submit for general validation
|
|
124
|
+
- On change ONLY for positive feedback (e.g., password strength)
|
|
125
|
+
- NEVER on keypress for error ("missing character") - tiring
|
|
126
|
+
- **Inline errors**: next to/below the wrong field, WITH optional general top-of-form message. Never just top
|
|
127
|
+
- **Required**:
|
|
128
|
+
- Red asterisk is NOT enough - add text "(required)" or a clear mark before submit
|
|
129
|
+
- Indicate required at design time, not after the error
|
|
130
|
+
- Modern alternative: mark optionals ("Phone (optional)")
|
|
131
|
+
- **Autocomplete**: correct `autocomplete` attribute: `email, current-password, new-password, name, given-name, family-name, tel, postal-code, etc`. Enables browser autofill
|
|
132
|
+
- **Inputmode + type**:
|
|
133
|
+
- `type="email"` shows keyboard with @ on mobile
|
|
134
|
+
- `inputmode="numeric"` for OTP/PIN/ZIP
|
|
135
|
+
- `type="tel"` for phone
|
|
136
|
+
- `type="url"` for URL
|
|
137
|
+
- `type="date"` for date (with fallback if browser doesn't support)
|
|
138
|
+
- **Submit**:
|
|
139
|
+
- DO NOT disable button before user tries - teaches wrong and hides cause
|
|
140
|
+
- Disable ONLY during in-flight request (avoids double submit)
|
|
141
|
+
- Loading state on the button (text + inline spinner)
|
|
142
|
+
- **Password**:
|
|
143
|
+
- "Show password" toggle (eye icon)
|
|
144
|
+
- Always force HTTPS - never send password in plain HTTP
|
|
145
|
+
- Show requirements before user types (8+ chars, etc)
|
|
146
|
+
- **Destructive confirmation**:
|
|
147
|
+
- Irreversible actions (delete account, drop data) require typing name/word or explicit checkbox
|
|
148
|
+
- Plain "are you sure?" modal is insufficient for truly destructive action
|
|
149
|
+
|
|
150
|
+
### 5. Navigation
|
|
151
|
+
|
|
152
|
+
- **Current location**: active nav highlighted (weight + color + indicator). Breadcrumbs in deep hierarchy
|
|
153
|
+
- **Browser back button**: respect history. Modal doesn't use `pushState` without reason. Single-page nav uses router that emits real history
|
|
154
|
+
- **Custom 404**: friendly page with search or sitemap, not blank screen
|
|
155
|
+
- **Logo links home**: universal convention
|
|
156
|
+
- **Search**: if app has search, keyboard shortcut `/` or `Ctrl+K` (convention)
|
|
157
|
+
|
|
158
|
+
### 6. Responsive - mobile-first
|
|
159
|
+
|
|
160
|
+
- **Design starts at 320px** and grows - not the other way around
|
|
161
|
+
- **Breakpoints based on content**, not devices: point where layout breaks, not "iPhone 12"
|
|
162
|
+
- **No horizontal scroll on mobile** (except intentional carousel). Audit with viewport 375px
|
|
163
|
+
- **Touch-friendly spacing**: minimum 8px between clickable targets
|
|
164
|
+
- **Hover-only is bad on mobile**: anything needing hover needs fallback (long press, tap to reveal)
|
|
165
|
+
- **Density**: mobile needs more space than desktop for the same legibility
|
|
166
|
+
|
|
167
|
+
### 7. UX performance - Core Web Vitals
|
|
168
|
+
|
|
169
|
+
- **CLS < 0.1** (Cumulative Layout Shift):
|
|
170
|
+
- `width` + `height` on every `<img>` (avoids jump on load)
|
|
171
|
+
- `font-display: swap` with metric-compatible fallback
|
|
172
|
+
- Reserve space for ads/embeds/skeletons
|
|
173
|
+
- **LCP < 2.5s** (Largest Contentful Paint):
|
|
174
|
+
- Optimized hero image (WebP/AVIF + responsive `srcset`)
|
|
175
|
+
- Critical CSS inline
|
|
176
|
+
- Preload critical resource (`<link rel="preload">`)
|
|
177
|
+
- **INP < 200ms** (Interaction to Next Paint):
|
|
178
|
+
- No heavy JS blocking main thread during interaction
|
|
179
|
+
- Debounce on input handlers
|
|
180
|
+
- Web Workers for heavy computation
|
|
181
|
+
- **TTFB < 800ms** (Time To First Byte):
|
|
182
|
+
- Static cache, CDN, lazy loading
|
|
183
|
+
- **Optimistic UI**: already mentioned - update immediately
|
|
184
|
+
|
|
185
|
+
### 8. Visual hierarchy
|
|
186
|
+
|
|
187
|
+
- **1 primary action per view**. Multiple = paralyzed decision (Hicks Law)
|
|
188
|
+
- **Secondary visually smaller**: ghost, outline, or link
|
|
189
|
+
- **Whitespace separates groups** - no little box (border) for everything
|
|
190
|
+
- **Fixed spacing scale**: 4/8/16/24/32/48/64 (multiples of 4 or 8). No `margin: 13px`
|
|
191
|
+
- **Type scale**: max 5-6 sizes in the entire app. More than that = visual chaos
|
|
192
|
+
- **Color palette**:
|
|
193
|
+
- 60-30-10 rule: 60% neutral (background), 30% complementary, 10% accent (CTA)
|
|
194
|
+
- Max 1 brand color + 1 or 2 accents
|
|
195
|
+
- States (success/warn/error) are a separate palette
|
|
196
|
+
- **Alignment**: every element aligned to a grid - not "eyeball"
|
|
197
|
+
|
|
198
|
+
### 9. i18n + l10n
|
|
199
|
+
|
|
200
|
+
- **Zero hardcoded string in markup**. Always a translation key
|
|
201
|
+
- JSX/TSX: don't write pt-BR text directly, use `t("key")` or equivalent
|
|
202
|
+
- Templates: use translate tag (`{% trans %}`, `<t>`, `@Localize`)
|
|
203
|
+
- HTML: separate content from markup
|
|
204
|
+
- **RTL ready** (Arabic, Hebrew):
|
|
205
|
+
- Logical properties: `margin-inline-start` instead of `margin-left`
|
|
206
|
+
- `padding-block` instead of `padding-top`
|
|
207
|
+
- `text-align: start/end` instead of `left/right`
|
|
208
|
+
- `dir="auto"` on fields accepting input in any language
|
|
209
|
+
- **Format by locale**:
|
|
210
|
+
- Dates: `Intl.DateTimeFormat` or backend equivalent
|
|
211
|
+
- Numbers: `Intl.NumberFormat`
|
|
212
|
+
- Currency: never hardcoded "$" - currency comes from locale + value
|
|
213
|
+
- **Pluralization**: ICU MessageFormat or equivalent. Languages have 1, 2, 3+ or more forms (Russian has 4)
|
|
214
|
+
|
|
215
|
+
### 10. UI security - overlap with general rules
|
|
216
|
+
|
|
217
|
+
- **Tokens NEVER in localStorage/sessionStorage**:
|
|
218
|
+
- Vulnerable to XSS. Any malicious script reads everything
|
|
219
|
+
- Safe pattern: httpOnly cookie + SameSite=Strict
|
|
220
|
+
- Token in memory with refresh via cookie is OK for SPAs
|
|
221
|
+
- **Strict CSP**:
|
|
222
|
+
- `script-src 'self'` at minimum - no `unsafe-inline`, no `unsafe-eval`
|
|
223
|
+
- `frame-ancestors 'none'` or whitelist - prevents clickjacking
|
|
224
|
+
- **HTTPS only**:
|
|
225
|
+
- Redirect HTTP -> HTTPS on the server
|
|
226
|
+
- HSTS header with `includeSubDomains`
|
|
227
|
+
- No mixed content (HTTP on HTTPS page)
|
|
228
|
+
- **CSRF**:
|
|
229
|
+
- CSRF token on every form with authenticated side-effect
|
|
230
|
+
- SameSite=Strict cookie helps but isn't enough
|
|
231
|
+
- **External links**:
|
|
232
|
+
- `target="_blank"` ALWAYS with `rel="noopener noreferrer"` (prevents tabnabbing)
|
|
233
|
+
- **dangerouslySetInnerHTML / v-html / @Html.Raw**:
|
|
234
|
+
- Never with user input without sanitization (DOMPurify or backend sanitizer)
|
|
235
|
+
- Prefer semantic parsing (markdown -> AST -> render)
|
|
236
|
+
- **External form action**: never accept a user-controllable `action` URL
|
|
237
|
+
|
|
238
|
+
## Anti-patterns - BLOCK list for reviewer
|
|
239
|
+
|
|
240
|
+
Each item below is automatic violation. Reviewer marks BLOCK + cites rule.
|
|
241
|
+
|
|
242
|
+
| Anti-pattern | Why it is BLOCK |
|
|
243
|
+
|---|---|
|
|
244
|
+
| Button that looks like a link / link that looks like a button | Confuses mental model, violates convention |
|
|
245
|
+
| `<div onclick>` instead of `<button>` | No keyboard, no ARIA, no semantics |
|
|
246
|
+
| `<a href="#">` or `<a href="javascript:">` for action | Becomes a destinationless link - use `<button>` |
|
|
247
|
+
| Modal without Esc close | Keyboard trap - WCAG 2.1.2 |
|
|
248
|
+
| Modal without visible close button | Same reason |
|
|
249
|
+
| Infinite spinner without timeout/fallback | User doesn't know if it's stuck |
|
|
250
|
+
| Auto-play media with sound | WCAG 1.4.2 |
|
|
251
|
+
| Toast as ONLY confirmation of destructive action | Toast disappears - destructive needs persistent |
|
|
252
|
+
| Disabled state without visible reason | "Why doesn't it work?" - UX bug |
|
|
253
|
+
| Generic error "Something went wrong" | Not actionable |
|
|
254
|
+
| Color as ONLY state indicator | Color blindness - WCAG 1.4.1 |
|
|
255
|
+
| Required marked ONLY by color (red border) | Same reason |
|
|
256
|
+
| Required shown ONLY after submit | User didn't know it was required |
|
|
257
|
+
| Form without `<label>` or `aria-label` | WCAG 3.3.2 |
|
|
258
|
+
| Placeholder replacing label | Disappears when user types - WCAG 3.3.2 |
|
|
259
|
+
| Heading skip (h1 -> h3 without h2) | WCAG 1.3.1 |
|
|
260
|
+
| `<img>` without `alt` | WCAG 1.1.1 |
|
|
261
|
+
| `<img alt="image">` or redundant alt "image of X" | Good alt describes content, not media |
|
|
262
|
+
| Text over image without overlay/guaranteed contrast | WCAG 1.4.3 |
|
|
263
|
+
| Animation > 400ms on direct interaction | Perceived as slow |
|
|
264
|
+
| `prefers-reduced-motion` ignored | WCAG 2.3.3 |
|
|
265
|
+
| Outline removed without replacement | WCAG 2.4.7 |
|
|
266
|
+
| Arbitrary positive `tabindex` (`tabindex="5"`) | Breaks natural order - only use 0 and -1 |
|
|
267
|
+
| `lang` absent on `<html>` | Screen reader pronounces wrong |
|
|
268
|
+
| Form action or href with direct user input | Risk of XSS/open redirect |
|
|
269
|
+
| `localStorage.setItem('token', ...)` or similar for credential | XSS risk - use httpOnly cookie |
|
|
270
|
+
|
|
271
|
+
## Procedure (use by agent)
|
|
272
|
+
|
|
273
|
+
### Doer (write/edit)
|
|
274
|
+
|
|
275
|
+
#### Step 1: Detect type of change
|
|
276
|
+
If task touches UI files, load checklist in mind before writing.
|
|
277
|
+
|
|
278
|
+
#### Step 2: For each new component/template
|
|
279
|
+
Apply the checklist of rules 1-10. In particular:
|
|
280
|
+
- Does it cover the 5 states (loading/empty/error/success/disabled)?
|
|
281
|
+
- Semantic HTML first?
|
|
282
|
+
- Visible focus maintained?
|
|
283
|
+
- Contrast OK in light/dark states?
|
|
284
|
+
- Strings via i18n key?
|
|
285
|
+
|
|
286
|
+
#### Step 3: When in architectural doubt
|
|
287
|
+
Consult references:
|
|
288
|
+
- Full WCAG: `references/wcag-checklist.md`
|
|
289
|
+
- States: `references/state-coverage.md`
|
|
290
|
+
- Forms: `references/forms-patterns.md`
|
|
291
|
+
- Anti-patterns explained: `references/anti-patterns.md`
|
|
292
|
+
|
|
293
|
+
### Reviewer (gate 5)
|
|
294
|
+
|
|
295
|
+
#### Step 1: For each modified file in frontend
|
|
296
|
+
Run specific greps based on the type:
|
|
297
|
+
|
|
298
|
+
**JSX/TSX/Vue/Svelte:**
|
|
299
|
+
```bash
|
|
300
|
+
# Button without aria-label and without inner text
|
|
301
|
+
grep -RnE '<button[^>]*>(\s*<[^>]+/?>\s*)+</button>' src/
|
|
302
|
+
|
|
303
|
+
# Input without label
|
|
304
|
+
grep -RnE '<input(?![^>]*aria-label)(?![^>]*id=)' src/
|
|
305
|
+
|
|
306
|
+
# href="#" for action
|
|
307
|
+
grep -RnE 'href="#"' src/
|
|
308
|
+
|
|
309
|
+
# localStorage with token
|
|
310
|
+
grep -RnE 'localStorage\.(set|get)Item.*[Tt]oken' src/
|
|
311
|
+
|
|
312
|
+
# Outline removed
|
|
313
|
+
grep -RnE 'outline\s*:\s*(none|0)' src/
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Server-side templates (Razor/Twig/Blade/ERB/Jinja):**
|
|
317
|
+
Similar greps adapted to the syntax.
|
|
318
|
+
|
|
319
|
+
#### Step 2: Classify
|
|
320
|
+
- Match on violation listed in the table above -> BLOCK
|
|
321
|
+
- Suspicious pattern but not certain -> WARN
|
|
322
|
+
- No match -> PASS on gate 5
|
|
323
|
+
|
|
324
|
+
## Expected inputs
|
|
325
|
+
|
|
326
|
+
- Path of the modified file
|
|
327
|
+
- Diff or complete content of the file
|
|
328
|
+
|
|
329
|
+
## Outputs
|
|
330
|
+
|
|
331
|
+
Does NOT produce its own file. Modifies the parent agent's judgement:
|
|
332
|
+
- Doer chooses NOT to introduce a violation - writes correct code from the start
|
|
333
|
+
- Reviewer marks BLOCK/WARN with rule cited
|
|
334
|
+
|
|
335
|
+
## References
|
|
336
|
+
|
|
337
|
+
- `references/wcag-checklist.md` - WCAG 2.2 AA expanded with code examples
|
|
338
|
+
- `references/state-coverage.md` - Patterns for loading/empty/error/success/disabled across engines
|
|
339
|
+
- `references/forms-patterns.md` - Universal patterns for form validation and UX
|
|
340
|
+
- `references/anti-patterns.md` - Gallery of anti-patterns with wrong example + fix
|
|
341
|
+
|
|
342
|
+
## Anti-patterns of this skill
|
|
343
|
+
|
|
344
|
+
- Applying only to JS stacks - rules work for any template engine
|
|
345
|
+
- Making a rule framework-specific (e.g., "use React.useState") - skill is agnostic
|
|
346
|
+
- Replacing human design code review - skill covers the technically broken, not the aesthetically mediocre
|
|
347
|
+
- Blocking MVP for minor a11y - severity matters, minor is INFO/WARN
|
|
348
|
+
|
|
349
|
+
## Examples
|
|
350
|
+
|
|
351
|
+
### Example 1: Doer receives task "add delete button to ItemCard"
|
|
352
|
+
|
|
353
|
+
Applies skill before writing:
|
|
354
|
+
- Destructive action needs: explicit confirmation, focus returns to origin button after modal closes, descriptive label (`aria-label="Delete item Order #123"`), undo if possible
|
|
355
|
+
- Loading state during request
|
|
356
|
+
- Error state with retry
|
|
357
|
+
- Success state with undo (5s timer)
|
|
358
|
+
- Use `<button>`, not `<a>` or `<div>`
|
|
359
|
+
- Tab order: button -> modal opens -> modal buttons navigable -> Esc closes -> focus returns
|
|
360
|
+
|
|
361
|
+
Code written comes out compliant.
|
|
362
|
+
|
|
363
|
+
### Example 2: Reviewer finds `<input>` without label
|
|
364
|
+
|
|
365
|
+
Marks gate 5 as BLOCK:
|
|
366
|
+
```
|
|
367
|
+
[BLOCK] src/components/LoginForm.tsx:42
|
|
368
|
+
Rule: Forms - Form labels (WCAG 1.3.1, 3.3.2)
|
|
369
|
+
Violation: <input type="email" /> without <label>, aria-label, or aria-labelledby
|
|
370
|
+
Fix: <label htmlFor="email">Email</label><input id="email" type="email" />
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Example 3: Reviewer finds `localStorage.setItem('access_token', token)`
|
|
374
|
+
|
|
375
|
+
Marks gate 5 as BLOCK:
|
|
376
|
+
```
|
|
377
|
+
[BLOCK] src/auth/store.ts:18
|
|
378
|
+
Rule: UI Security - Tokens in storage
|
|
379
|
+
Violation: localStorage.setItem with authentication token
|
|
380
|
+
Why: vulnerable to XSS - any malicious script on the page reads the token
|
|
381
|
+
Fix: backend sets httpOnly cookie SameSite=Strict; frontend doesn't touch token
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Example 4: Backend-only API (Python FastAPI)
|
|
385
|
+
|
|
386
|
+
Skill is not loaded. PROJECT.md has `frontend.has_frontend: false`. Nothing happens.
|