diff --git a/.agent/CheatSheet.md b/.agent/CheatSheet.md index 3901008..ca2463b 100644 --- a/.agent/CheatSheet.md +++ b/.agent/CheatSheet.md @@ -1,6 +1,6 @@ # Devran AI Kit — CheatSheet -> **Version**: v5.2.8 | **Quick Reference** for all capabilities +> **Version**: v5.2.9 | **Quick Reference** for all capabilities > **Session**: Start with `/project-status`, end with session-end checklist --- diff --git a/.agent/commands/help.md b/.agent/commands/help.md index 183f33e..db66114 100644 --- a/.agent/commands/help.md +++ b/.agent/commands/help.md @@ -24,7 +24,7 @@ Your complete guide to the Devran AI Kit. Type `/help` for a quick overview, or ## Quick Overview -**Devran AI Kit v5.2.8** — Trust-Grade AI Development Framework +**Devran AI Kit v5.2.9** — Trust-Grade AI Development Framework | Category | Count | Description | |:---------|:------|:------------| diff --git a/.agent/engine/loading-rules.json b/.agent/engine/loading-rules.json index 19ac0de..30b5ba6 100644 --- a/.agent/engine/loading-rules.json +++ b/.agent/engine/loading-rules.json @@ -22,130 +22,484 @@ "domainRules": [ { "domain": "security", - "keywords": ["auth", "security", "vulnerability", "password", "jwt", "oauth", "cors", "xss", "csrf", "injection"], + "keywords": [ + "auth", + "security", + "vulnerability", + "password", + "jwt", + "oauth", + "cors", + "xss", + "csrf", + "injection" + ], "implicitTriggers": [ - "login", "signup", "register", "form", "payment", "checkout", - "user data", "profile", "upload", "download", "admin", "dashboard", - "permission", "role", "token", "session", "cookie" + "login", + "signup", + "register", + "form", + "payment", + "checkout", + "user data", + "profile", + "upload", + "download", + "admin", + "dashboard", + "permission", + "role", + "token", + "session", + "cookie" + ], + "loadAgents": [ + "security-reviewer" ], - "loadAgents": ["security-reviewer"], - "loadSkills": ["security-practices"] + "loadSkills": [ + "security-practices" + ] }, { "domain": "testing", - "keywords": ["test", "coverage", "e2e", "playwright", "jest", "vitest", "tdd", "unit test", "integration test"], - "loadAgents": ["tdd-guide", "e2e-runner"], - "loadSkills": ["testing-patterns", "webapp-testing"] + "keywords": [ + "test", + "coverage", + "e2e", + "playwright", + "jest", + "vitest", + "tdd", + "unit test", + "integration test" + ], + "loadAgents": [ + "tdd-guide", + "e2e-runner" + ], + "loadSkills": [ + "testing-patterns", + "webapp-testing" + ] }, { "domain": "architecture", - "keywords": ["design", "structure", "pattern", "ddd", "hexagonal", "layer", "module", "monolith", "microservice"], - "loadAgents": ["architect"], - "loadSkills": ["architecture", "clean-code"] + "keywords": [ + "design", + "structure", + "pattern", + "ddd", + "hexagonal", + "layer", + "module", + "monolith", + "microservice" + ], + "loadAgents": [ + "architect" + ], + "loadSkills": [ + "architecture", + "clean-code" + ] }, { "domain": "frontend", - "keywords": ["react", "next.js", "vue", "component", "css", "styling", "responsive", "a11y", "accessibility", "ui", "ux"], - "implicitTriggers": ["button", "modal", "form field", "dropdown", "navigation bar", "sidebar", "theme", "dark mode", "animation", "transition", "layout", "grid", "flexbox", "tailwind", "styled"], - "loadAgents": ["frontend-specialist"], - "loadSkills": ["frontend-patterns", "mobile-design"], - "loadRules": ["accessibility"] + "keywords": [ + "react", + "next.js", + "vue", + "component", + "css", + "styling", + "responsive", + "a11y", + "accessibility", + "ui", + "ux" + ], + "implicitTriggers": [ + "button", + "modal", + "form field", + "dropdown", + "navigation bar", + "sidebar", + "theme", + "dark mode", + "animation", + "transition", + "layout", + "grid", + "flexbox", + "tailwind", + "styled", + "polish", + "premium design", + "micro-interaction", + "design tokens", + "motion design" + ], + "loadAgents": [ + "frontend-specialist" + ], + "loadSkills": [ + "frontend-patterns", + "mobile-design", + "emil-design-eng", + "impeccable", + "taste", + "animate", + "improve-animations" + ], + "loadRules": [ + "accessibility" + ] }, { "domain": "backend", - "keywords": ["api", "server", "node", "nestjs", "express", "middleware", "rest", "graphql", "endpoint"], - "implicitTriggers": ["route", "controller", "service layer", "webhook", "queue", "worker", "cron", "batch", "rate limit", "pagination", "validation"], - "loadAgents": ["backend-specialist"], - "loadSkills": ["api-patterns", "nodejs-patterns"] + "keywords": [ + "api", + "server", + "node", + "nestjs", + "express", + "middleware", + "rest", + "graphql", + "endpoint" + ], + "implicitTriggers": [ + "route", + "controller", + "service layer", + "webhook", + "queue", + "worker", + "cron", + "batch", + "rate limit", + "pagination", + "validation" + ], + "loadAgents": [ + "backend-specialist" + ], + "loadSkills": [ + "api-patterns", + "nodejs-patterns" + ] }, { "domain": "database", - "keywords": ["database", "sql", "postgresql", "prisma", "migration", "schema", "query", "orm", "redis"], - "implicitTriggers": ["table", "column", "index", "foreign key", "transaction", "connection pool", "backup", "replica", "sharding", "partition"], - "loadAgents": ["database-architect"], - "loadSkills": ["database-design"], - "loadRules": ["data-privacy"] + "keywords": [ + "database", + "sql", + "postgresql", + "prisma", + "migration", + "schema", + "query", + "orm", + "redis" + ], + "implicitTriggers": [ + "table", + "column", + "index", + "foreign key", + "transaction", + "connection pool", + "backup", + "replica", + "sharding", + "partition" + ], + "loadAgents": [ + "database-architect" + ], + "loadSkills": [ + "database-design" + ], + "loadRules": [ + "data-privacy" + ] }, { "domain": "devops", - "keywords": ["deploy", "ci", "cd", "docker", "kubernetes", "railway", "vercel", "github actions", "pipeline", "terraform", "gitops", "canary", "helm"], - "implicitTriggers": ["environment variable", "container", "image", "registry", "rollback", "blue-green", "feature flag", "infrastructure"], - "loadAgents": ["devops-engineer"], - "loadSkills": ["docker-patterns", "deployment-procedures"], - "loadRules": ["data-privacy"] + "keywords": [ + "deploy", + "ci", + "cd", + "docker", + "kubernetes", + "railway", + "vercel", + "github actions", + "pipeline", + "terraform", + "gitops", + "canary", + "helm" + ], + "implicitTriggers": [ + "environment variable", + "container", + "image", + "registry", + "rollback", + "blue-green", + "feature flag", + "infrastructure" + ], + "loadAgents": [ + "devops-engineer" + ], + "loadSkills": [ + "docker-patterns", + "deployment-procedures" + ], + "loadRules": [ + "data-privacy" + ] }, { "domain": "performance", - "keywords": ["slow", "optimize", "speed", "bundle", "lighthouse", "web vitals", "lcp", "cls", "fid", "cache", "cdn", "latency", "p99", "tracing"], - "loadAgents": ["performance-optimizer"], - "loadSkills": ["performance-profiling"], - "loadRules": ["performance"] + "keywords": [ + "slow", + "optimize", + "speed", + "bundle", + "lighthouse", + "web vitals", + "lcp", + "cls", + "fid", + "cache", + "cdn", + "latency", + "p99", + "tracing" + ], + "loadAgents": [ + "performance-optimizer" + ], + "loadSkills": [ + "performance-profiling" + ], + "loadRules": [ + "performance" + ] }, { "domain": "reliability", - "keywords": ["reliability", "uptime", "monitoring", "sre", "sla", "slo", "sli", "incident", "chaos", "resilience", "error-budget", "golden-signals", "on-call", "capacity"], - "loadAgents": ["reliability-engineer"], + "keywords": [ + "reliability", + "uptime", + "monitoring", + "sre", + "sla", + "slo", + "sli", + "incident", + "chaos", + "resilience", + "error-budget", + "golden-signals", + "on-call", + "capacity" + ], + "loadAgents": [ + "reliability-engineer" + ], "loadSkills": [] }, { "domain": "observability", - "keywords": ["logging", "tracing", "metrics", "monitoring", "alerting", "opentelemetry", "grafana", "prometheus", "datadog", "observability", "dashboard"], - "loadAgents": ["reliability-engineer", "devops-engineer"], + "keywords": [ + "logging", + "tracing", + "metrics", + "monitoring", + "alerting", + "opentelemetry", + "grafana", + "prometheus", + "datadog", + "observability", + "dashboard" + ], + "loadAgents": [ + "reliability-engineer", + "devops-engineer" + ], "loadSkills": [] }, { "domain": "mobile", - "keywords": ["mobile", "react native", "expo", "ios", "android", "app store", "native"], - "loadAgents": ["mobile-developer"], - "loadSkills": ["mobile-design"] + "keywords": [ + "mobile", + "react native", + "expo", + "ios", + "android", + "app store", + "native" + ], + "loadAgents": [ + "mobile-developer" + ], + "loadSkills": [ + "mobile-design" + ] }, { "domain": "documentation", - "keywords": ["docs", "readme", "document", "api docs", "jsdoc", "changelog", "adr", "decision record", "knowledge"], - "loadAgents": ["doc-updater", "knowledge-agent"], + "keywords": [ + "docs", + "readme", + "document", + "api docs", + "jsdoc", + "changelog", + "adr", + "decision record", + "knowledge" + ], + "loadAgents": [ + "doc-updater", + "knowledge-agent" + ], "loadSkills": [] }, { "domain": "planning", - "keywords": ["plan", "roadmap", "sprint", "milestone", "task", "breakdown", "estimate"], - "loadAgents": ["planner", "sprint-orchestrator"], - "loadSkills": ["plan-writing", "brainstorming"] + "keywords": [ + "plan", + "roadmap", + "sprint", + "milestone", + "task", + "breakdown", + "estimate" + ], + "loadAgents": [ + "planner", + "sprint-orchestrator" + ], + "loadSkills": [ + "plan-writing", + "brainstorming" + ] }, { "domain": "debugging", - "keywords": ["bug", "error", "fix", "crash", "exception", "stack trace", "not working"], - "loadAgents": ["build-error-resolver"], - "loadSkills": ["debugging-strategies"] + "keywords": [ + "bug", + "error", + "fix", + "crash", + "exception", + "stack trace", + "not working" + ], + "loadAgents": [ + "build-error-resolver" + ], + "loadSkills": [ + "debugging-strategies" + ] }, { "domain": "refactoring", - "keywords": ["refactor", "clean", "improve", "dead code", "technical debt", "simplify"], - "loadAgents": ["refactor-cleaner"], - "loadSkills": ["clean-code"] + "keywords": [ + "refactor", + "clean", + "improve", + "dead code", + "technical debt", + "simplify" + ], + "loadAgents": [ + "refactor-cleaner" + ], + "loadSkills": [ + "clean-code" + ] }, { "domain": "typescript", - "keywords": ["typescript", "ts", "type safety", "tsconfig", "tsc", "generics", "type guard", "discriminated union"], - "loadAgents": ["typescript-reviewer"], - "loadSkills": ["typescript-expert"] + "keywords": [ + "typescript", + "ts", + "type safety", + "tsconfig", + "tsc", + "generics", + "type guard", + "discriminated union" + ], + "loadAgents": [ + "typescript-reviewer" + ], + "loadSkills": [ + "typescript-expert" + ] }, { "domain": "python", - "keywords": ["python", "django", "flask", "fastapi", "pep8", "mypy", "pytest", "asyncio", "pydantic"], - "loadAgents": ["python-reviewer"], + "keywords": [ + "python", + "django", + "flask", + "fastapi", + "pep8", + "mypy", + "pytest", + "asyncio", + "pydantic" + ], + "loadAgents": [ + "python-reviewer" + ], "loadSkills": [] }, { "domain": "golang", - "keywords": ["go", "golang", "goroutine", "channel", "context", "errgroup", "go mod", "staticcheck"], - "loadAgents": ["go-reviewer"], + "keywords": [ + "go", + "golang", + "goroutine", + "channel", + "context", + "errgroup", + "go mod", + "staticcheck" + ], + "loadAgents": [ + "go-reviewer" + ], "loadSkills": [] }, { "domain": "onboarding", - "keywords": ["onboard", "setup project", "new project", "initialize", "scaffold", "from scratch", "existing project", "greenfield", "brownfield"], - "loadAgents": ["onboarding-specialist"], - "loadSkills": ["onboarding-engine"] + "keywords": [ + "onboard", + "setup project", + "new project", + "initialize", + "scaffold", + "from scratch", + "existing project", + "greenfield", + "brownfield" + ], + "loadAgents": [ + "onboarding-specialist" + ], + "loadSkills": [ + "onboarding-engine" + ] } ], "contextBudget": { @@ -155,53 +509,278 @@ "description": "Limits prevent context overflow. The kit selects only domain-relevant agents and skills for each task, keeping within the LLM context window." }, "workflowBindings": [ - { "workflow": "brainstorm", "loadAgents": [], "loadSkills": ["brainstorming"], "bindingType": "inferred" }, - { "workflow": "quality-gate", "loadAgents": [], "loadSkills": ["brainstorming"], "loadRules": ["quality-gate"], "bindingType": "inferred" }, - { "workflow": "plan", "loadAgents": ["planner"], "loadSkills": ["plan-writing", "brainstorming", "plan-validation"], "bindingType": "explicit" }, - { "workflow": "create", "loadAgents": [], "loadSkills": ["app-builder", "clean-code"], "bindingType": "inferred" }, - { "workflow": "enhance", "loadAgents": [], "loadSkills": ["clean-code", "testing-patterns"], "bindingType": "inferred" }, - { "workflow": "preview", "loadAgents": [], "loadSkills": ["shell-conventions"], "bindingType": "inferred" }, - { "workflow": "ui-ux-pro-max", "loadAgents": [], "loadSkills": ["ui-ux-pro-max", "frontend-patterns", "mobile-design"], "bindingType": "inferred" }, - { "workflow": "test", "loadAgents": [], "loadSkills": ["testing-patterns", "webapp-testing"], "bindingType": "inferred" }, - { "workflow": "review", "loadAgents": [], "loadSkills": ["verification-loop"], "bindingType": "inferred" }, - { "workflow": "pr", "loadAgents": ["pr-reviewer"], "loadSkills": ["git-workflow", "pr-toolkit", "verification-loop"], "bindingType": "explicit" }, - { "workflow": "pr-review", "loadAgents": ["pr-reviewer"], "loadSkills": ["pr-toolkit", "verification-loop"], "bindingType": "explicit" }, - { "workflow": "pr-fix", "loadAgents": ["pr-reviewer"], "loadSkills": ["pr-toolkit", "verification-loop"], "bindingType": "explicit" }, - { "workflow": "pr-merge", "loadAgents": ["pr-reviewer"], "loadSkills": ["pr-toolkit", "verification-loop"], "bindingType": "explicit" }, - { "workflow": "pr-split", "loadAgents": ["pr-reviewer"], "loadSkills": ["pr-toolkit"], "bindingType": "explicit" }, - { "workflow": "deploy", "loadAgents": [], "loadSkills": ["deployment-procedures"], "bindingType": "inferred" }, - { "workflow": "debug", "loadAgents": [], "loadSkills": ["debugging-strategies"], "bindingType": "inferred" }, + { + "workflow": "brainstorm", + "loadAgents": [], + "loadSkills": [ + "brainstorming" + ], + "bindingType": "inferred" + }, + { + "workflow": "quality-gate", + "loadAgents": [], + "loadSkills": [ + "brainstorming" + ], + "loadRules": [ + "quality-gate" + ], + "bindingType": "inferred" + }, + { + "workflow": "plan", + "loadAgents": [ + "planner" + ], + "loadSkills": [ + "plan-writing", + "brainstorming", + "plan-validation" + ], + "bindingType": "explicit" + }, + { + "workflow": "create", + "loadAgents": [], + "loadSkills": [ + "app-builder", + "clean-code" + ], + "bindingType": "inferred" + }, + { + "workflow": "enhance", + "loadAgents": [], + "loadSkills": [ + "clean-code", + "testing-patterns" + ], + "bindingType": "inferred" + }, + { + "workflow": "preview", + "loadAgents": [], + "loadSkills": [ + "shell-conventions" + ], + "bindingType": "inferred" + }, + { + "workflow": "ui-ux-pro-max", + "loadAgents": [], + "loadSkills": [ + "ui-ux-pro-max", + "frontend-patterns", + "mobile-design" + ], + "bindingType": "inferred" + }, + { + "workflow": "test", + "loadAgents": [], + "loadSkills": [ + "testing-patterns", + "webapp-testing" + ], + "bindingType": "inferred" + }, + { + "workflow": "review", + "loadAgents": [], + "loadSkills": [ + "verification-loop" + ], + "bindingType": "inferred" + }, + { + "workflow": "pr", + "loadAgents": [ + "pr-reviewer" + ], + "loadSkills": [ + "git-workflow", + "pr-toolkit", + "verification-loop" + ], + "bindingType": "explicit" + }, + { + "workflow": "pr-review", + "loadAgents": [ + "pr-reviewer" + ], + "loadSkills": [ + "pr-toolkit", + "verification-loop" + ], + "bindingType": "explicit" + }, + { + "workflow": "pr-fix", + "loadAgents": [ + "pr-reviewer" + ], + "loadSkills": [ + "pr-toolkit", + "verification-loop" + ], + "bindingType": "explicit" + }, + { + "workflow": "pr-merge", + "loadAgents": [ + "pr-reviewer" + ], + "loadSkills": [ + "pr-toolkit", + "verification-loop" + ], + "bindingType": "explicit" + }, + { + "workflow": "pr-split", + "loadAgents": [ + "pr-reviewer" + ], + "loadSkills": [ + "pr-toolkit" + ], + "bindingType": "explicit" + }, + { + "workflow": "deploy", + "loadAgents": [], + "loadSkills": [ + "deployment-procedures" + ], + "bindingType": "inferred" + }, + { + "workflow": "debug", + "loadAgents": [], + "loadSkills": [ + "debugging-strategies" + ], + "bindingType": "inferred" + }, { "workflow": "orchestrate", - "loadAgents": ["planner", "explorer-agent"], - "loadSkills": ["parallel-agents", "intelligent-routing"], + "loadAgents": [ + "planner", + "explorer-agent" + ], + "loadSkills": [ + "parallel-agents", + "intelligent-routing" + ], "bindingType": "explicit", - "dynamicAgentPool": ["architect", "backend-specialist", "frontend-specialist", "mobile-developer", "database-architect", "security-reviewer", "tdd-guide", "e2e-runner", "devops-engineer", "performance-optimizer", "reliability-engineer", "refactor-cleaner", "code-reviewer", "typescript-reviewer", "python-reviewer", "go-reviewer"] + "dynamicAgentPool": [ + "architect", + "backend-specialist", + "frontend-specialist", + "mobile-developer", + "database-architect", + "security-reviewer", + "tdd-guide", + "e2e-runner", + "devops-engineer", + "performance-optimizer", + "reliability-engineer", + "refactor-cleaner", + "code-reviewer", + "typescript-reviewer", + "python-reviewer", + "go-reviewer" + ] + }, + { + "workflow": "retrospective", + "loadAgents": [], + "loadSkills": [ + "verification-loop" + ], + "bindingType": "inferred" + }, + { + "workflow": "project-status", + "loadAgents": [], + "loadSkills": [ + "verification-loop" + ], + "bindingType": "inferred" + }, + { + "workflow": "help-kit", + "loadAgents": [], + "loadSkills": [], + "bindingType": "inferred" }, - { "workflow": "retrospective", "loadAgents": [], "loadSkills": ["verification-loop"], "bindingType": "inferred" }, - { "workflow": "project-status", "loadAgents": [], "loadSkills": ["verification-loop"], "bindingType": "inferred" }, - { "workflow": "help-kit", "loadAgents": [], "loadSkills": [], "bindingType": "inferred" }, { "workflow": "greenfield", - "loadAgents": ["onboarding-specialist", "market-researcher"], - "protectedAgents": ["onboarding-specialist"], - "protectedSkills": ["onboarding-engine", "market-intelligence", "doc-generation"], - "loadSkills": ["onboarding-engine", "market-intelligence", "doc-generation", "brainstorming", "research-methodology", "architecture"], + "loadAgents": [ + "onboarding-specialist", + "market-researcher" + ], + "protectedAgents": [ + "onboarding-specialist" + ], + "protectedSkills": [ + "onboarding-engine", + "market-intelligence", + "doc-generation" + ], + "loadSkills": [ + "onboarding-engine", + "market-intelligence", + "doc-generation", + "brainstorming", + "research-methodology", + "architecture" + ], "bindingType": "explicit" }, { "workflow": "brownfield", - "loadAgents": ["onboarding-specialist", "codebase-scanner", "market-researcher"], - "protectedAgents": ["onboarding-specialist", "codebase-scanner"], - "protectedSkills": ["onboarding-engine", "doc-generation"], - "loadSkills": ["onboarding-engine", "market-intelligence", "doc-generation", "research-methodology", "architecture"], + "loadAgents": [ + "onboarding-specialist", + "codebase-scanner", + "market-researcher" + ], + "protectedAgents": [ + "onboarding-specialist", + "codebase-scanner" + ], + "protectedSkills": [ + "onboarding-engine", + "doc-generation" + ], + "loadSkills": [ + "onboarding-engine", + "market-intelligence", + "doc-generation", + "research-methodology", + "architecture" + ], "bindingType": "explicit" } ], "planningMandates": { "description": "Mandatory resources loaded for every /plan invocation regardless of keyword matching. Ensures cross-cutting concerns are never omitted.", - "alwaysLoadRules": ["security", "testing", "coding-style", "documentation", "architecture", "performance", "accessibility", "data-privacy"], - "alwaysLoadSkills": ["security-practices", "testing-patterns"], + "alwaysLoadRules": [ + "security", + "testing", + "coding-style", + "documentation", + "architecture", + "performance", + "accessibility", + "data-privacy" + ], + "alwaysLoadSkills": [ + "security-practices", + "testing-patterns" + ], "crossCuttingSections": [ "security-considerations", "testing-strategy", diff --git a/.agent/engine/plugins-registry.json b/.agent/engine/plugins-registry.json new file mode 100644 index 0000000..a65687a --- /dev/null +++ b/.agent/engine/plugins-registry.json @@ -0,0 +1,18 @@ +{ + "plugins": [ + { + "name": "ui-design-taste", + "version": "1.0.0", + "author": "Emre Dursun", + "installedAt": "2026-09-06T17:06:38.522Z", + "sourcePath": "/sessions/rcw-01mxf1ryzk6cczwu4hv7681p/kit-plugin-ui-design-taste", + "installed": { + "agents": 0, + "skills": 5, + "workflows": 0, + "hooks": 0, + "configs": 0 + } + } + ] +} diff --git a/.agent/manifest.json b/.agent/manifest.json index a320be7..8164827 100644 --- a/.agent/manifest.json +++ b/.agent/manifest.json @@ -1,6 +1,6 @@ { "schemaVersion": "1.0.0", - "kitVersion": "5.2.8", + "kitVersion": "5.2.9", "lastAuditedAt": null, "description": "Devran AI Kit — Trust-Grade AI Development Framework", "repository": "https://github.com/devran-ai/kit", @@ -145,7 +145,7 @@ "directory": "commands/" }, "skills": { - "count": 39, + "count": 44, "items": [ { "name": "api-patterns", @@ -302,6 +302,26 @@ { "name": "doc-generation", "directory": "skills/doc-generation/" + }, + { + "name": "animate", + "directory": "skills/animate/" + }, + { + "name": "emil-design-eng", + "directory": "skills/emil-design-eng/" + }, + { + "name": "impeccable", + "directory": "skills/impeccable/" + }, + { + "name": "improve-animations", + "directory": "skills/improve-animations/" + }, + { + "name": "taste", + "directory": "skills/taste/" } ] }, diff --git a/.agent/plugins/ui-design-taste/plugin.json b/.agent/plugins/ui-design-taste/plugin.json new file mode 100644 index 0000000..d490c3e --- /dev/null +++ b/.agent/plugins/ui-design-taste/plugin.json @@ -0,0 +1,7 @@ +{ + "name": "ui-design-taste", + "version": "1.0.0", + "author": "Emre Dursun", + "description": "Emil Kowalski design/animation principles, Impeccable frontend design language, and Taste (real-site design token extraction) for premium, non-generic UI output.", + "skills": ["animate", "emil-design-eng", "impeccable", "improve-animations", "taste"] +} diff --git a/.agent/session-context.md b/.agent/session-context.md index 6326e48..72dd24e 100644 --- a/.agent/session-context.md +++ b/.agent/session-context.md @@ -32,7 +32,7 @@ **Branch**: — **Repository**: — -**Framework**: Devran AI Kit v5.2.8 +**Framework**: Devran AI Kit v5.2.9 ### Key File Locations diff --git a/.agent/skills/animate/RECIPES.md b/.agent/skills/animate/RECIPES.md new file mode 100644 index 0000000..6744891 --- /dev/null +++ b/.agent/skills/animate/RECIPES.md @@ -0,0 +1,324 @@ +# Animation Recipes + +Ready-to-build implementations for the cases that come up most. Start from the recipe, then adapt — don't rebuild from scratch. + +Curves are the `--ease-out`, `--ease-in-out`, and `--ease-drawer` tokens defined in SKILL.md. + +--- + +## Button press + +Any pressable element. Instant feedback that the interface heard the user. + +```css +.button { + transition: transform 160ms var(--ease-out); +} + +.button:active { + transform: scale(0.97); +} +``` + +`scale()` scales children too — the label and icons come along, which is what makes it read as a physical press. + +No hover gating needed here: `:active` is a real press on touch. Gate any `:hover` styling separately. + +--- + +## Dropdown, popover, menu, select + +Scales out of its trigger, not out of thin air. + +```css +.popover { + transform-origin: var(--transform-origin); /* Base UI supplies this */ + transition: + opacity 200ms var(--ease-out), + transform 200ms var(--ease-out); +} + +.popover[data-starting-style], +.popover[data-ending-style] { + opacity: 0; + transform: scale(0.95); +} +``` + +The `transform-origin` is the whole point — the panel should look like it came out of the thing you clicked. + +--- + +## Tooltip + +Same shape as a popover, faster, plus the detail most implementations miss. + +```css +.tooltip { + transform-origin: var(--transform-origin); + transition: + transform 125ms var(--ease-out), + opacity 125ms var(--ease-out); +} + +.tooltip[data-starting-style], +.tooltip[data-ending-style] { + opacity: 0; + transform: scale(0.97); +} + +/* Once one tooltip is open, neighbours open instantly */ +.tooltip[data-instant] { + transition-duration: 0ms; +} +``` + +The initial delay prevents accidental activation. After that, skipping both the delay and the animation makes the whole toolbar feel faster. + +--- + +## Modal + +The one popover that stays centered. + +```css +.modal { + transform-origin: center; /* exempt — not anchored to a trigger */ + transition: + opacity 250ms var(--ease-out), + transform 250ms var(--ease-out); +} + +.modal[data-starting-style], +.modal[data-ending-style] { + opacity: 0; + transform: scale(0.96); +} + +.backdrop { + transition: opacity 250ms var(--ease-out); +} +``` + +Animate the backdrop's opacity alongside it so they read as one surface. + +--- + +## Drawer / sheet + +```css +.drawer { + transform: translateY(0); + transition: transform 500ms var(--ease-drawer); +} + +.drawer[data-closed] { + transform: translateY(100%); +} +``` + +This is how Vaul hides a drawer before animating it in. + +Add drag and it becomes a gesture problem — see **Drag to dismiss** below. + +--- + +## Toast + +```css +.toast { + opacity: 1; + transform: translateY(0); + transition: + opacity 400ms ease, + transform 400ms ease; + + @starting-style { + opacity: 0; + transform: translateY(100%); + } +} +``` + +- `ease` rather than `ease-out`, slightly slower than typical UI: Sonner reads as elegant partly because its motion is tuned to the component's personality rather than to the generic UI budget. +- If `@starting-style` isn't available, fall back to the mount flag: + +```jsx +useEffect(() => { setMounted(true); }, []); +//
+``` + +When toasts stack and the list reflows, the opacity change has to work against the height change. There's no formula for that pair — adjust until it feels right, then check it again the next day. + +--- + +## Accordion / collapse + +```css +.content { + overflow: hidden; + transition: + height 200ms var(--ease-out), + opacity 200ms var(--ease-out); +} +``` + +Keep it short — this is one of the few animations that costs layout on every frame, so a long duration is expensive as well as sluggish. Measure the content height in JS (or use a headless primitive that supplies it) rather than animating to `auto`. + +--- + +## Stagger a group entrance + +For a list or grid the user sees occasionally — not for a list they scroll past all day. + +```css +.item { + opacity: 0; + transform: translateY(8px); + animation: fadeIn 300ms var(--ease-out) forwards; +} + +.item:nth-child(2) { animation-delay: 50ms; } +.item:nth-child(3) { animation-delay: 100ms; } +.item:nth-child(4) { animation-delay: 150ms; } + +@keyframes fadeIn { + to { + opacity: 1; + transform: translateY(0); + } +} +``` + +Stagger is decorative — it must never block interaction while it plays. + +--- + +## Hold to confirm + +For destructive actions where a plain click is too easy to fire by accident. + +```css +.overlay { + clip-path: inset(0 100% 0 0); + transition: clip-path 200ms var(--ease-out); /* release: snappy */ +} + +.button:active .overlay { + clip-path: inset(0 0 0 0); + transition: clip-path 2s linear; /* press: slow and deliberate */ +} + +.button:active { + transform: scale(0.97); +} +``` + +`linear` is correct here — the fill is a progress indicator, and progress shouldn't ease. + +--- + +## Tab indicator with a color transition + +Timing individual color transitions across a tab list never quite lands. Clip instead. + +Duplicate the tab list. Style the copy as the active state — different background, different text color. Clip the copy so only the active tab shows, and animate the clip on change: + +```css +.tabs-active-copy { + clip-path: inset(0 60% 0 20%); /* driven by the active tab's position */ + transition: clip-path 250ms var(--ease-in-out); +} +``` + +The text and background change together, in perfect sync, because they're one element being revealed rather than two colors being interpolated. + +--- + +## Scroll reveal + +Marketing surfaces only. Don't do this to functional UI a user visits daily. + +```css +.reveal { + clip-path: inset(0 0 100% 0); + transition: clip-path 600ms var(--ease-in-out); +} + +.reveal[data-visible] { + clip-path: inset(0 0 0 0); +} +``` + +Trigger with `IntersectionObserver`, or Motion's `useInView` with `{ once: true, margin: "-100px" }`. Fire it once — re-animating on every scroll-by is an interface fighting its reader. + +--- + +## Drag to dismiss + +The gesture recipe. Springs, not durations, because the user can reverse mid-motion. + +```js +// Dismiss on a flick, not just on distance +const timeTaken = Date.now() - dragStartTime.current; +const velocity = Math.abs(swipeAmount) / timeTaken; + +if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { + dismiss(); +} +``` + +```js +// Set transform on the dragged element directly. +// Driving it through a CSS variable on the parent recalcs styles for every child. +element.style.transform = `translateY(${distance}px)`; +``` + +Four details that separate a good drag from a bad one: + +- **Pointer capture** once the drag starts, so it continues when the pointer leaves the element's bounds. +- **Multi-touch protection** — `if (isDragging) return` on new touch points, or switching fingers mid-drag makes the element jump. +- **Damping past boundaries** — dragging beyond a natural edge moves the element less the further it goes. Real things slow before they stop. +- **Friction, not a wall** — allow the over-drag with rising resistance rather than refusing it. + +Settle with a spring so an interrupted drag keeps its velocity: + +```js +{ type: "spring", duration: 0.5, bounce: 0.2 } +``` + +--- + +## Masking a crossfade that won't settle + +When two states overlap visibly during a transition and no amount of easing or duration tuning fixes it, blur the seam: + +```css +.content { + transition: + filter 200ms ease, + opacity 200ms ease; +} + +.content.transitioning { + filter: blur(2px); + opacity: 0.7; +} +``` + +Without blur the eye reads two distinct objects swapping. Blur blends them into one perceived transformation. Keep it under 20px — heavy blur is expensive, especially in Safari. + +--- + +## Programmatic, without a library + +When the motion needs JS control but not a dependency, WAAPI gives you CSS-grade performance: + +```js +element.animate( + [{ clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0 0)' }], + { duration: 1000, fill: 'forwards', easing: 'cubic-bezier(0.77, 0, 0.175, 1)' } +); +``` + +Hardware-accelerated, interruptible, no bundle cost. diff --git a/.agent/skills/animate/SKILL.md b/.agent/skills/animate/SKILL.md new file mode 100644 index 0000000..159fe07 --- /dev/null +++ b/.agent/skills/animate/SKILL.md @@ -0,0 +1,199 @@ +--- +name: animate +description: Build an animation from scratch, making the decisions in the order that determines whether it feels right — should it animate at all, what purpose, which tool, which properties, which curve and duration, how it interrupts, how it exits. Writes the implementation. Use when asked to animate something, add motion, make a component feel alive, or build a transition. For critiquing existing motion use review-animations; for auditing a whole codebase use improve-animations. +--- + +# Building Animations + +A construction skill. It does ONE thing: turn a request for motion into an implementation that would survive a strict review. It does not audit a codebase (that's `improve-animations`), critique a diff (that's `review-animations`), hunt for places that could animate (that's `find-animation-opportunities`), or build for React Native (that's `animate-expo`). + +## Operating Posture + +You are a senior design engineer building the animation yourself. The bar is Emil Kowalski's animation philosophy — the same bar `review-animations` enforces. Write it so it passes that review the first time. + +Two failure modes, and the first is worse: + +1. **Animating something that shouldn't animate.** The gate below exists to produce zero lines of code sometimes. That's a success, not a dodge. +2. **Animating the right thing with the wrong ingredients** — `ease-in` on an entrance, `scale(0)`, keyframes on a toast, a duration that makes a dropdown feel sluggish. + +Never present motion options as a menu. Make the call, state the reasoning in one line, write the code. + +## Hard Rules + +1. **Run the sequence in order.** Steps 1 and 2 gate everything. Don't reach for a curve before you know whether it animates at all. +2. **No approximated values.** Every curve, duration, and spring config comes from the tables below. Never invent `cubic-bezier(0.4, 0, 0.2, 1)` because it looks familiar. +3. **Extend the codebase's tokens, don't fork them.** If `--ease-out` or a duration scale already exists, use it. Adding a parallel system is a defect. +4. **Reduced motion and hover gating ship with the animation**, not as a follow-up. +5. **Cheapest tool that works.** Don't install a motion library for a fade. + +## The Build Sequence + +### 1. Should this animate at all? + +| Frequency | Decision | +| --- | --- | +| 100+ times/day (keyboard shortcuts, command palette toggle) | **No animation. Ever.** Stop here. | +| Tens of times/day (hover effects, list navigation) | Near-imperceptible only — fast and subtle, or nothing | +| Occasional (modals, drawers, toasts) | Standard animation | +| Rare / first-time (onboarding, success, celebration) | The delight budget lives here | + +**Keyboard-initiated actions are a disqualifier, not a judgment call.** Raycast has no open/close animation — that is correct for something opened hundreds of times a day. + +If the request fails this gate, say so plainly and don't write the animation. Offer the non-motion alternative (instant state change, a static affordance) instead. + +### 2. What is the purpose? + +Name it in one of these words before continuing: + +- **Feedback** — confirming the interface heard the user +- **Spatial consistency** — showing where something came from or went +- **State indication** — making a state change legible +- **Preventing a jarring change** — bridging content that would otherwise teleport +- **Explanation** — demonstrating how something works (marketing/onboarding only) +- **Delight** — allowed *only* at the rare/first-time tier + +Can't name it? Don't build it. "It looks cool" on a frequently-seen element is a reason to stop. + +Also check **function**: data the user is reading or acting on should not move for style. A decorative mouse-tracking effect belongs on a marketing page, not on a graph in a banking app. + +### 3. Pick the tool — cheapest that works + +Walk down; stop at the first that fits. + +| Need | Tool | +| --- | --- | +| Hover, press, color, a state toggle you control with a class or attribute | **CSS transition** | +| Entry animation on mount, no JS state | **CSS `@starting-style`** | +| Predetermined motion that must stay smooth while the page is busy loading | **CSS animation** (runs off the main thread) | +| Programmatic control with CSS performance, no library | **WAAPI** (`element.animate()`) | +| Springs, layout animations, exit animations, gesture-driven values | **Motion** (`motion.dev`) | + +CSS animations beat JS under load — they run off the main thread, while `requestAnimationFrame`-based animation drops frames while the browser loads, scripts, or paints. Use CSS for predetermined motion, JS for dynamic and interruptible motion. + +If the task needs a *component* rather than an animation — a toast, a drawer, a command menu, a dropdown — stop and invoke `pick-ui-library`. Hand-rolling those is how you end up with a `
` dropdown and no focus management. + +### 4. Pick the properties + +- **`transform` and `opacity` only.** They skip layout and paint and run on the GPU. `width`/`height`/`margin`/`padding`/`top`/`left` trigger all three. (`clip-path` is the sanctioned fourth — see RECIPES.md. `height` is tolerated only for accordions, where there's no transform equivalent.) +- **Never `scale(0)`.** Start from `scale(0.9–0.97)` + `opacity: 0`. Nothing in the real world appears from nothing. +- **`transform-origin` at the trigger** for popovers, dropdowns, menus, tooltips — `var(--transform-origin)` in Base UI. **Modals are exempt**; they're not anchored to a trigger, so they stay centered. +- **Percentages in `translate()`** are relative to the element's own size — `translateY(100%)` moves by its own height whatever the content. Prefer over hardcoded pixels. +- **In Motion, use the full transform string.** `x`/`y`/`scale` shorthands are not hardware-accelerated and drop frames under load: + +```jsx + // drops frames under load + // hardware accelerated +``` + +- **Never drive a child's transform from a CSS variable on the parent** — it recalculates styles for every child. Set `transform` on the element directly. + +### 5. Easing and duration — or a spring + +**Easing**, in decision order: + +| Situation | Easing | +| --- | --- | +| Entering or exiting | `ease-out` | +| Moving / morphing on screen | `ease-in-out` | +| Hover / color change | `ease` | +| Constant motion (marquee, progress) | `linear` | +| Default | `ease-out` | + +**Never `ease-in` on UI.** It starts slow, delaying the exact moment the user is watching. `ease-out` at 200ms *feels* faster than `ease-in` at 200ms. + +Built-in CSS easings are too weak. Use these: + +```css +--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */ +--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */ +--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */ +``` + +Need a curve that isn't here? Take it from [easing.dev](https://easing.dev/) or [easings.co](https://easings.co/). Don't hand-roll one. + +**Duration:** + +| Element | Duration | +| --- | --- | +| Button press feedback | 100–160ms | +| Tooltips, small popovers | 125–200ms | +| Dropdowns, selects | 150–250ms | +| Modals, drawers | 200–500ms | +| Marketing / explanatory | Can be longer | + +**UI animations stay under 300ms.** A 180ms dropdown feels more responsive than a 400ms one. + +**Reach for a spring instead** when the motion is drag with momentum, an element that should feel alive, a gesture the user can interrupt or reverse, or decorative mouse-tracking: + +```js +{ type: "spring", duration: 0.5, bounce: 0.2 } // Apple-style — easier to reason about +{ type: "spring", mass: 1, stiffness: 100, damping: 10 } // traditional physics — more control +``` + +Keep bounce at 0.1–0.3, and avoid bounce in most UI — reserve it for drag-to-dismiss and playful interactions. + +### 6. Interruption and exit + +- **Transitions, not keyframes, for anything triggered rapidly** — toasts, toggles, anything a user can fire twice in a second. Transitions retarget from the current value; keyframes restart from zero. +- **Springs for gestures**, because they carry velocity through an interruption. +- **Exit the way it entered.** A toast that slides in from the bottom leaves through the bottom. Symmetric paths are what make swipe-to-dismiss feel obvious. +- **Asymmetric timing where the user is deciding.** Slow on the deliberate phase (a hold-to-confirm press: 2s linear), snappy on the system response (release: 200ms ease-out). + +### 7. Reduced motion and pointer gating + +Ships with the animation, every time. + +```css +@media (prefers-reduced-motion: reduce) { + .element { animation: fade 0.2s ease; } /* keep opacity/color, drop transform-based motion */ +} + +@media (hover: hover) and (pointer: fine) { + .element:hover { transform: scale(1.05); } /* touch fires false hovers on tap */ +} +``` + +```jsx +const reduce = useReducedMotion(); +const closedX = reduce ? 0 : '-100%'; +``` + +Reduced motion means **fewer and gentler** animations, not zero — keep transitions that aid comprehension, remove movement and position changes. + +## Recipes + +For ready-to-build implementations of the common cases — button press, dropdown, tooltip, modal, drawer, toast, accordion, stagger, hold-to-confirm, tab indicator, scroll reveal, drag-to-dismiss — see [RECIPES.md](RECIPES.md). Load it whenever the request matches one of those components; start from the recipe rather than from a blank file. + +## Never Ship + +Self-check before you finish. Each of these is an automatic block in `review-animations`: + +| Never | Instead | +| --- | --- | +| `transition: all` | Name the exact properties | +| `transform: scale(0)` entrance | `scale(0.95)` + `opacity: 0` | +| `ease-in` on a UI element | `ease-out` or a strong custom curve | +| Built-in `ease-out` on a deliberate animation | `cubic-bezier(0.23, 1, 0.32, 1)` | +| Animation on a keyboard shortcut or 100+/day action | No animation | +| UI duration over 300ms with no reason | 150–250ms | +| `transform-origin: center` on a trigger-anchored popover | `var(--transform-origin)` (modals exempt) | +| Keyframes on toasts, toggles, rapidly-triggered elements | CSS transitions | +| Animating `width`/`height`/`margin`/`padding`/`top`/`left` | `transform` / `opacity` | +| Motion `x`/`y`/`scale` props under load | Full `transform` string | +| Ungated `:hover` motion | `@media (hover: hover) and (pointer: fine)` | +| Missing `prefers-reduced-motion` | Gentler variant, not zero | +| Everything entering at once | 30–80ms stagger | + +## Output + +Write the code. Then, in at most a few lines: + +- **The gate result** — frequency tier and the named purpose. If something in the request was rejected, say which and why. +- **The ingredients** — tool, properties, curve, duration or spring config, in one line each. +- **What to feel-check** — if the result depends on feel you can't judge from code (a crossfade, a spring's bounce, the opacity/height balance in an entering list), say so and point at the check: play it at 2–5× duration or in the DevTools animation inspector, step it frame by frame, test gestures on a real device, and look again the next day with fresh eyes. + +Don't pad this into a report. The code is the deliverable. + +## Tone + +Opinionated and brief. When the honest answer is "this shouldn't animate," give it — that answer is the reason this skill exists. When feel genuinely can't be settled from code, say so instead of guessing at a value. diff --git a/.agent/skills/emil-design-eng/SKILL.md b/.agent/skills/emil-design-eng/SKILL.md new file mode 100644 index 0000000..1e14a50 --- /dev/null +++ b/.agent/skills/emil-design-eng/SKILL.md @@ -0,0 +1,674 @@ +--- +name: emil-design-eng +description: This skill encodes Emil Kowalski's philosophy on UI polish, component design, animation decisions, and the invisible details that make software feel great. +--- + +# Design Engineering + +## Initial Response + +When this skill is first invoked without a specific question, respond only with: + +> I'm ready to help you build interfaces that feel right, my knowledge comes from Emil Kowalski's design engineering philosophy. If you want to dive even deeper, check out Emil’s course: [animations.dev](https://animations.dev/). + +Do not provide any other information until the user asks a question. + +You are a design engineer with the craft sensibility. You build interfaces where every detail compounds into something that feels right. You understand that in a world where everyone's software is good enough, taste is the differentiator. + +## Core Philosophy + +### Taste is trained, not innate + +Good taste is not personal preference. It is a trained instinct: the ability to see beyond the obvious and recognize what elevates. You develop it by surrounding yourself with great work, thinking deeply about why something feels good, and practicing relentlessly. + +When building UI, don't just make it work. Study why the best interfaces feel the way they do. Reverse engineer animations. Inspect interactions. Be curious. + +### Unseen details compound + +Most details users never consciously notice. That is the point. When a feature functions exactly as someone assumes it should, they proceed without giving it a second thought. That is the goal. + +> "All those unseen details combine to produce something that's just stunning, like a thousand barely audible voices all singing in tune." - Paul Graham + +Every decision below exists because the aggregate of invisible correctness creates interfaces people love without knowing why. + +### Beauty is leverage + +People select tools based on the overall experience, not just functionality. Good defaults and good animations are real differentiators. Beauty is underutilized in software. Use it as leverage to stand out. + +## Review Format (Required) + +When reviewing UI code, you MUST use a markdown table with Before/After columns. Do NOT use a list with "Before:" and "After:" on separate lines. Always output an actual markdown table like this: + +| Before | After | Why | +| --- | --- | --- | +| `transition: all 300ms` | `transition: transform 200ms ease-out` | Specify exact properties; avoid `all` | +| `transform: scale(0)` | `transform: scale(0.95); opacity: 0` | Nothing in the real world appears from nothing | +| `ease-in` on dropdown | `ease-out` with custom curve | `ease-in` feels sluggish; `ease-out` gives instant feedback | +| No `:active` state on button | `transform: scale(0.97)` on `:active` | Buttons must feel responsive to press | +| `transform-origin: center` on popover | `transform-origin: var(--transform-origin)` | Popovers should scale from their trigger (not modals — modals stay centered) | + +Wrong format (never do this): + +``` +Before: transition: all 300ms +After: transition: transform 200ms ease-out +──────────────────────────── +Before: scale(0) +After: scale(0.95) +``` + +Correct format: A single markdown table with | Before | After | Why | columns, one row per issue found. The "Why" column briefly explains the reasoning. + +## The Animation Decision Framework + +Before writing any animation code, answer these questions in order: + +### 1. Should this animate at all? + +**Ask:** How often will users see this animation? + +| Frequency | Decision | +| ----------------------------------------------------------- | ---------------------------- | +| 100+ times/day (keyboard shortcuts, command palette toggle) | No animation. Ever. | +| Tens of times/day (hover effects, list navigation) | Remove or drastically reduce | +| Occasional (modals, drawers, toasts) | Standard animation | +| Rare/first-time (onboarding, feedback forms, celebrations) | Can add delight | + +**Never animate keyboard-initiated actions.** These actions are repeated hundreds of times daily. Animation makes them feel slow, delayed, and disconnected from the user's actions. + +Raycast has no open/close animation. That is the optimal experience for something used hundreds of times a day. + +### 2. What is the purpose? + +Every animation must have a clear answer to "why does this animate?" + +Valid purposes: + +- **Spatial consistency**: toast enters and exits from the same direction, making swipe-to-dismiss feel intuitive +- **State indication**: a morphing feedback button shows the state change +- **Explanation**: a marketing animation that shows how a feature works +- **Feedback**: a button scales down on press, confirming the interface heard the user +- **Preventing jarring changes**: elements appearing or disappearing without transition feel broken + +If the purpose is just "it looks cool" and the user will see it often, don't animate. + +### 3. What easing should it use? + +Is the element entering or exiting? + Yes → ease-out (starts fast, feels responsive) + No → + Is it moving/morphing on screen? + Yes → ease-in-out (natural acceleration/deceleration) + Is it a hover/color change? + Yes → ease + Is it constant motion (marquee, progress bar)? + Yes → linear + Default → ease-out + +**Critical: use custom easing curves.** The built-in CSS easings are too weak. They lack the punch that makes animations feel intentional. + +```css +/* Strong ease-out for UI interactions */ +--ease-out: cubic-bezier(0.23, 1, 0.32, 1); + +/* Strong ease-in-out for on-screen movement */ +--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); + +/* iOS-like drawer curve (from Ionic Framework) */ +--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); +``` + +**Never use ease-in for UI animations.** It starts slow, which makes the interface feel sluggish and unresponsive. A dropdown with `ease-in` at 300ms _feels_ slower than `ease-out` at the same 300ms, because ease-in delays the initial movement — the exact moment the user is watching most closely. + +**Easing curve resources:** Don't create curves from scratch. Use [easing.dev](https://easing.dev/) or [easings.co](https://easings.co/) to find stronger custom variants of standard easings. + +### 4. How fast should it be? + +| Element | Duration | +| ------------------------ | ------------- | +| Button press feedback | 100-160ms | +| Tooltips, small popovers | 125-200ms | +| Dropdowns, selects | 150-250ms | +| Modals, drawers | 200-500ms | +| Marketing/explanatory | Can be longer | + +**Rule: UI animations should stay under 300ms.** A 180ms dropdown feels more responsive than a 400ms one. A faster-spinning spinner makes the app feel like it loads faster, even when the load time is identical. + +### Perceived performance + +Speed in animation is not just about feeling snappy — it directly affects how users perceive your app's performance: + +- A **fast-spinning spinner** makes loading feel faster (same load time, different perception) +- A **180ms select** animation feels more responsive than a **400ms** one +- **Instant tooltips** after the first one is open (skip delay + skip animation) make the whole toolbar feel faster + +The perception of speed matters as much as actual speed. Easing amplifies this: `ease-out` at 200ms _feels_ faster than `ease-in` at 200ms because the user sees immediate movement. + +## Spring Animations + +Springs feel more natural than duration-based animations because they simulate real physics. They don't have fixed durations — they settle based on physical parameters. + +### When to use springs + +- Drag interactions with momentum +- Elements that should feel "alive" (like Apple's Dynamic Island) +- Gestures that can be interrupted mid-animation +- Decorative mouse-tracking interactions + +### Spring-based mouse interactions + +Tying visual changes directly to mouse position feels artificial because it lacks motion. Use `useSpring` from Motion (formerly Framer Motion) to interpolate value changes with spring-like behavior instead of updating immediately. + +```jsx +import { useSpring } from 'framer-motion'; + +// Without spring: feels artificial, instant +const rotation = mouseX * 0.1; + +// With spring: feels natural, has momentum +const springRotation = useSpring(mouseX * 0.1, { + stiffness: 100, + damping: 10, +}); +``` + +This works because the animation is **decorative** — it doesn't serve a function. If this were a functional graph in a banking app, no animation would be better. Know when decoration helps and when it hinders. + +### Spring configuration + +**Apple's approach (recommended — easier to reason about):** + +```js +{ type: "spring", duration: 0.5, bounce: 0.2 } +``` + +**Traditional physics (more control):** + +```js +{ type: "spring", mass: 1, stiffness: 100, damping: 10 } +``` + +Keep bounce subtle (0.1-0.3) when used. Avoid bounce in most UI contexts. Use it for drag-to-dismiss and playful interactions. + +### Interruptibility advantage + +Springs maintain velocity when interrupted — CSS animations and keyframes restart from zero. This makes springs ideal for gestures users might change mid-motion. When you click an expanded item and quickly press Escape, a spring-based animation smoothly reverses from its current position. + +## Component Building Principles + +### Buttons must feel responsive + +Add `transform: scale(0.97)` on `:active`. This gives instant feedback, making the UI feel like it is truly listening to the user. + +```css +.button { + transition: transform 160ms ease-out; +} + +.button:active { + transform: scale(0.97); +} +``` + +This applies to any pressable element. The scale should be subtle (0.95-0.98). + +### Never animate from scale(0) + +Nothing in the real world disappears and reappears completely. Elements animating from `scale(0)` look like they come out of nowhere. + +Start from `scale(0.9)` or higher, combined with opacity. Even a barely-visible initial scale makes the entrance feel more natural, like a balloon that has a visible shape even when deflated. + +```css +/* Bad */ +.entering { + transform: scale(0); +} + +/* Good */ +.entering { + transform: scale(0.95); + opacity: 0; +} +``` + +### Make popovers origin-aware + +Popovers should scale in from their trigger, not from center. The default `transform-origin: center` is wrong for almost every popover. **Exception: modals.** Modals should keep `transform-origin: center` because they are not anchored to a specific trigger — they appear centered in the viewport. + +```css +/* Base UI */ +.popover { + transform-origin: var(--transform-origin); +} +``` + +Whether the user notices the difference individually does not matter. In the aggregate, unseen details become visible. They compound. + +### Tooltips: skip delay on subsequent hovers + +Tooltips should delay before appearing to prevent accidental activation. But once one tooltip is open, hovering over adjacent tooltips should open them instantly with no animation. This feels faster without defeating the purpose of the initial delay. + +```css +.tooltip { + transition: transform 125ms ease-out, opacity 125ms ease-out; + transform-origin: var(--transform-origin); +} + +.tooltip[data-starting-style], +.tooltip[data-ending-style] { + opacity: 0; + transform: scale(0.97); +} + +/* Skip animation on subsequent tooltips */ +.tooltip[data-instant] { + transition-duration: 0ms; +} +``` + +### Use CSS transitions over keyframes for interruptible UI + +CSS transitions can be interrupted and retargeted mid-animation. Keyframes restart from zero. For any interaction that can be triggered rapidly (adding toasts, toggling states), transitions produce smoother results. + +```css +/* Interruptible - good for UI */ +.toast { + transition: transform 400ms ease; +} + +/* Not interruptible - avoid for dynamic UI */ +@keyframes slideIn { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } +} +``` + +### Use blur to mask imperfect transitions + +When a crossfade between two states feels off despite trying different easings and durations, add subtle `filter: blur(2px)` during the transition. + +**Why blur works:** Without blur, you see two distinct objects during a crossfade — the old state and the new state overlapping. This looks unnatural. Blur bridges the visual gap by blending the two states together, tricking the eye into perceiving a single smooth transformation instead of two objects swapping. + +Combine blur with scale-on-press (`scale(0.97)`) for a polished button state transition: + +```css +.button { + transition: transform 160ms ease-out; +} + +.button:active { + transform: scale(0.97); +} + +.button-content { + transition: filter 200ms ease, opacity 200ms ease; +} + +.button-content.transitioning { + filter: blur(2px); + opacity: 0.7; +} +``` + +Keep blur under 20px. Heavy blur is expensive, especially in Safari. + +### Animate enter states with @starting-style + +The modern CSS way to animate element entry without JavaScript: + +```css +.toast { + opacity: 1; + transform: translateY(0); + transition: opacity 400ms ease, transform 400ms ease; + + @starting-style { + opacity: 0; + transform: translateY(100%); + } +} +``` + +This replaces the common React pattern of using `useEffect` to set `mounted: true` after initial render. Use `@starting-style` when browser support allows; fall back to the `data-mounted` attribute pattern otherwise. + +```jsx +// Legacy pattern (still works everywhere) +useEffect(() => { + setMounted(true); +}, []); +//
+``` + +## CSS Transform Mastery + +### translateY with percentages + +Percentage values in `translate()` are relative to the element's own size. Use `translateY(100%)` to move an element by its own height, regardless of actual dimensions. This is how Sonner positions toasts and how Vaul hides the drawer before animating in. + +```css +/* Works regardless of drawer height */ +.drawer-hidden { + transform: translateY(100%); +} + +/* Works regardless of toast height */ +.toast-enter { + transform: translateY(-100%); +} +``` + +Prefer percentages over hardcoded pixel values. They are less error-prone and adapt to content. + +### scale() scales children too + +Unlike `width`/`height`, `scale()` also scales an element's children. When scaling a button on press, the font size, icons, and content scale proportionally. This is a feature, not a bug. + +### 3D transforms for depth + +`rotateX()`, `rotateY()` with `transform-style: preserve-3d` create real 3D effects in CSS. Orbiting animations, coin flips, and depth effects are all possible without JavaScript. + +```css +.wrapper { + transform-style: preserve-3d; +} + +@keyframes orbit { + from { + transform: translate(-50%, -50%) rotateY(0deg) translateZ(72px) rotateY(360deg); + } + to { + transform: translate(-50%, -50%) rotateY(360deg) translateZ(72px) rotateY(0deg); + } +} +``` + +### transform-origin + +Every element has an anchor point from which transforms execute. The default is center. Set it to match where the trigger lives for origin-aware interactions. + +## clip-path for Animation + +`clip-path` is not just for shapes. It is one of the most powerful animation tools in CSS. + +### The inset shape + +`clip-path: inset(top right bottom left)` defines a rectangular clipping region. Each value "eats" into the element from that side. + +```css +/* Fully hidden from right */ +.hidden { + clip-path: inset(0 100% 0 0); +} + +/* Fully visible */ +.visible { + clip-path: inset(0 0 0 0); +} + +/* Reveal from left to right */ +.overlay { + clip-path: inset(0 100% 0 0); + transition: clip-path 200ms ease-out; +} +.button:active .overlay { + clip-path: inset(0 0 0 0); + transition: clip-path 2s linear; +} +``` + +### Tabs with perfect color transitions + +Duplicate the tab list. Style the copy as "active" (different background, different text color). Clip the copy so only the active tab is visible. Animate the clip on tab change. This creates a seamless color transition that timing individual color transitions can never achieve. + +### Hold-to-delete pattern + +Use `clip-path: inset(0 100% 0 0)` on a colored overlay. On `:active`, transition to `inset(0 0 0 0)` over 2s with linear timing. On release, snap back with 200ms ease-out. Add `scale(0.97)` on the button for press feedback. + +### Image reveals on scroll + +Start with `clip-path: inset(0 0 100% 0)` (hidden from bottom). Animate to `inset(0 0 0 0)` when the element enters the viewport. Use `IntersectionObserver` or Framer Motion's `useInView` with `{ once: true, margin: "-100px" }`. + +### Comparison sliders + +Overlay two images. Clip the top one with `clip-path: inset(0 50% 0 0)`. Adjust the right inset value based on drag position. No extra DOM elements needed, fully hardware-accelerated. + +## Gesture and Drag Interactions + +### Momentum-based dismissal + +Don't require dragging past a threshold. Calculate velocity: `Math.abs(dragDistance) / elapsedTime`. If velocity exceeds ~0.11, dismiss regardless of distance. A quick flick should be enough. + +```js +const timeTaken = new Date().getTime() - dragStartTime.current.getTime(); +const velocity = Math.abs(swipeAmount) / timeTaken; + +if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { + dismiss(); +} +``` + +### Damping at boundaries + +When a user drags past the natural boundary (e.g., dragging a drawer up when already at top), apply damping. The more they drag, the less the element moves. Things in real life don't suddenly stop; they slow down first. + +### Pointer capture for drag + +Once dragging starts, set the element to capture all pointer events. This ensures dragging continues even if the pointer leaves the element bounds. + +### Multi-touch protection + +Ignore additional touch points after the initial drag begins. Without this, switching fingers mid-drag causes the element to jump to the new position. + +```js +function onPress() { + if (isDragging) return; + // Start drag... +} +``` + +### Friction instead of hard stops + +Instead of preventing upward drag entirely, allow it with increasing friction. It feels more natural than hitting an invisible wall. + +## Performance Rules + +### Only animate transform and opacity + +These properties skip layout and paint, running on the GPU. Animating `padding`, `margin`, `height`, or `width` triggers all three rendering steps. + +### CSS variables are inheritable + +Changing a CSS variable on a parent recalculates styles for all children. In a drawer with many items, updating `--swipe-amount` on the container causes expensive style recalculation. Update `transform` directly on the element instead. + +```js +// Bad: triggers recalc on all children +element.style.setProperty('--swipe-amount', `${distance}px`); + +// Good: only affects this element +element.style.transform = `translateY(${distance}px)`; +``` + +### Framer Motion hardware acceleration caveat + +Framer Motion's shorthand properties (`x`, `y`, `scale`) are NOT hardware-accelerated. They use `requestAnimationFrame` on the main thread. For hardware acceleration, use the full `transform` string: + +```jsx +// NOT hardware accelerated (convenient but drops frames under load) + + +// Hardware accelerated (stays smooth even when main thread is busy) + +``` + +This matters when the browser is simultaneously loading content, running scripts, or painting. At Vercel, the dashboard tab animation used Shared Layout Animations and dropped frames during page loads. Switching to CSS animations (off main thread) fixed it. + +### CSS animations beat JS under load + +CSS animations run off the main thread. When the browser is busy loading a new page, Framer Motion animations (using `requestAnimationFrame`) drop frames. CSS animations remain smooth. Use CSS for predetermined animations; JS for dynamic, interruptible ones. + +### Use WAAPI for programmatic CSS animations + +The Web Animations API gives you JavaScript control with CSS performance. Hardware-accelerated, interruptible, and no library needed. + +```js +element.animate([{ clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0 0)' }], { + duration: 1000, + fill: 'forwards', + easing: 'cubic-bezier(0.77, 0, 0.175, 1)', +}); +``` + +## Accessibility + +### prefers-reduced-motion + +Animations can cause motion sickness. Reduced motion means fewer and gentler animations, not zero. Keep opacity and color transitions that aid comprehension. Remove movement and position animations. + +```css +@media (prefers-reduced-motion: reduce) { + .element { + animation: fade 0.2s ease; + /* No transform-based motion */ + } +} +``` + +```jsx +const shouldReduceMotion = useReducedMotion(); +const closedX = shouldReduceMotion ? 0 : '-100%'; +``` + +### Touch device hover states + +```css +@media (hover: hover) and (pointer: fine) { + .element:hover { + transform: scale(1.05); + } +} +``` + +Touch devices trigger hover on tap, causing false positives. Gate hover animations behind this media query. + +## The Sonner Principles (Building Loved Components) + +These principles come from building Sonner (13M+ weekly npm downloads) and apply to any component: + +1. **Developer experience is key.** No hooks, no context, no complex setup. Insert `` once, call `toast()` from anywhere. The less friction to adopt, the more people will use it. + +2. **Good defaults matter more than options.** Ship beautiful out of the box. Most users never customize. The default easing, timing, and visual design should be excellent. + +3. **Naming creates identity.** "Sonner" (French for "to ring") feels more elegant than "react-toast". Sacrifice discoverability for memorability when appropriate. + +4. **Handle edge cases invisibly.** Pause toast timers when the tab is hidden. Fill gaps between stacked toasts with pseudo-elements to maintain hover state. Capture pointer events during drag. Users never notice these, and that is exactly right. + +5. **Use transitions, not keyframes, for dynamic UI.** Toasts are added rapidly. Keyframes restart from zero on interruption. Transitions retarget smoothly. + +6. **Build a great documentation site.** Let people touch the product, play with it, and understand it before they use it. Interactive examples with ready-to-use code snippets lower the barrier to adoption. + +### Cohesion matters + +Sonner's animation feels satisfying partly because the whole experience is cohesive. The easing and duration fit the vibe of the library. It is slightly slower than typical UI animations and uses `ease` rather than `ease-out` to feel more elegant. The animation style matches the toast design, the page design, the name — everything is in harmony. + +When choosing animation values, consider the personality of the component. A playful component can be bouncier. A professional dashboard should be crisp and fast. Match the motion to the mood. + +### The opacity + height combination + +When items enter and exit a list (like Family's drawer), the opacity change must work well with the height animation. This is often trial and error. There is no formula — you adjust until it feels right. + +### Review your work the next day + +Review animations with fresh eyes. You notice imperfections the next day that you missed during development. Play animations in slow motion or frame by frame to spot timing issues that are invisible at full speed. + +### Asymmetric enter/exit timing + +Pressing should be slow when it needs to be deliberate (hold-to-delete: 2s linear), but release should always be snappy (200ms ease-out). This pattern applies broadly: slow where the user is deciding, fast where the system is responding. + +```css +/* Release: fast */ +.overlay { + transition: clip-path 200ms ease-out; +} + +/* Press: slow and deliberate */ +.button:active .overlay { + transition: clip-path 2s linear; +} +``` + +## Stagger Animations + +When multiple elements enter together, stagger their appearance. Each element animates in with a small delay after the previous one. This creates a cascading effect that feels more natural than everything appearing at once. + +```css +.item { + opacity: 0; + transform: translateY(8px); + animation: fadeIn 300ms ease-out forwards; +} + +.item:nth-child(1) { + animation-delay: 0ms; +} +.item:nth-child(2) { + animation-delay: 50ms; +} +.item:nth-child(3) { + animation-delay: 100ms; +} +.item:nth-child(4) { + animation-delay: 150ms; +} + +@keyframes fadeIn { + to { + opacity: 1; + transform: translateY(0); + } +} +``` + +Keep stagger delays short (30-80ms between items). Long delays make the interface feel slow. Stagger is decorative — never block interaction while stagger animations are playing. + +## Debugging Animations + +### Slow motion testing + +Play animations at reduced speed to spot issues invisible at full speed. Temporarily increase duration to 2-5x normal, or use browser DevTools animation inspector to slow playback. + +Things to look for in slow motion: + +- Do colors transition smoothly, or do you see two distinct states overlapping? +- Does the easing feel right, or does it start/stop abruptly? +- Is the transform-origin correct, or does the element scale from the wrong point? +- Are multiple animated properties (opacity, transform, color) in sync? + +### Frame-by-frame inspection + +Step through animations frame by frame in Chrome DevTools (Animations panel). This reveals timing issues between coordinated properties that you cannot see at full speed. + +### Test on real devices + +For touch interactions (drawers, swipe gestures), test on physical devices. Connect your phone via USB, visit your local dev server by IP address, and use Safari's remote devtools. The Xcode Simulator is an alternative but real hardware is better for gesture testing. + +## Review Checklist + +When reviewing UI code, check for: + +| Issue | Fix | +| ------------------------------------------ | ---------------------------------------------------------------- | +| `transition: all` | Specify exact properties: `transition: transform 200ms ease-out` | +| `scale(0)` entry animation | Start from `scale(0.95)` with `opacity: 0` | +| `ease-in` on UI element | Switch to `ease-out` or custom curve | +| `transform-origin: center` on popover | Set to trigger location or use Base UI's `var(--transform-origin)` (modals are exempt — keep centered) | +| Animation on keyboard action | Remove animation entirely | +| Duration > 300ms on UI element | Reduce to 150-250ms | +| Hover animation without media query | Add `@media (hover: hover) and (pointer: fine)` | +| Keyframes on rapidly-triggered element | Use CSS transitions for interruptibility | +| Framer Motion `x`/`y` props under load | Use `transform: "translateX()"` for hardware acceleration | +| Same enter/exit transition speed | Make exit faster than enter (e.g., enter 2s, exit 200ms) | +| Elements all appear at once | Add stagger delay (30-80ms between items) | diff --git a/.agent/skills/impeccable/SKILL.md b/.agent/skills/impeccable/SKILL.md new file mode 100644 index 0000000..c64769e --- /dev/null +++ b/.agent/skills/impeccable/SKILL.md @@ -0,0 +1,84 @@ +--- +name: impeccable +description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. +version: 4.2.1 +user-invocable: true +argument-hint: "[shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" +license: Apache 2.0 +--- + +This skill gives you the tools and permission to create design that earns to be called out-of-distribution craft: Whereas before, your design work would have been safe, timid and measured, you now approach every design task as an award-winning design director with impeccable understanding for what makes exceptional design work: production-grade code, peak creativity, a clear POV, deep understanding of the needs of the client and users, and exceptional craft. + +Core principles: +- Go all out. No hedging, no shortcuts. The deliverable must be complete (except assets the user must provide). +- Dream big and bold. Distinct, beautiful, outstanding and highly inspiring work. +- Verify in bounded passes, not a loop, and the ceiling covers the whole cycle: screenshots, defect scans, micro-edits, and rebuilds alike. Build fully, inspect once with a batched round (desktop and mobile together on the web; the shipped device classes on a native platform), fix everything it shows in one batch, confirm with at most one more round, and stop polishing. Open-ended self-QA burns the user's money doing worse what the finish handoffs do better. + +## Setup + +1. Run `/scripts/impeccable context` once per session, where `` is the loaded base directory the runtime reports for this skill; keep cwd at the user's project. That base directory resolves every `.claude/skills/impeccable/scripts/impeccable ` command in this skill and its references, and `.claude/skills/impeccable/scripts` is the fallback only when the runtime reports no base directory. On a Windows shell without `sh`, call `.claude/skills/impeccable/scripts/impeccable.cmd` instead. The launcher runs a self-contained binary that ships next to it or is downloaded once on first run; no Node or other runtime is required. Pass a named source file or route as `--target `. It loads PRODUCT.md, DESIGN.md, the matching surface brief, and native-platform guidance when applicable; follow its directives and do not rerun it. +2. Load the request's playbook: its Commands-table reference for an explicit/implied sub-command, or [reference/new-work.md](reference/new-work.md) for a new surface or replacement visual world. Inspect target and incumbent visual truth before editing. When the app cannot run, start with committed visual-regression goldens or screenshot fixtures; verify target and freshness against current tokens, CSS, components, or assets, resolve conflicts, and compare theme/variant captures. +3. After analysis and direction are resolved, load [reference/craft-floor.md](reference/craft-floor.md) immediately before editing UI. It carries the quality floor, the absolute bans, and the reflexes no detector catches. Do not load it for planning-only work. + +## How to design + +- **The brief wins.** Honor pinned aesthetics, eras, materials, fonts, and palettes even when they conflict with a saturated-pattern warning. Redirecting a clear brief toward your taste is failure. +- **Refinement preserves; redesign replaces.** Refinement keeps the incumbent identity, behavior, copy, and everything outside scope. Ask before replacing factual copy or adding claims. Redesign keeps product truth, content, function, native affordances, and constraints, but treats the old look as evidence and anti-reference; choose a replacement world in new-work and replace DESIGN.md. Never split the difference into polish on the discarded look. +- **Visual authority is evidence, not a filename.** Missing DESIGN.md alone does not make a project greenfield; new-work decides whether to preserve, expand, or replace the incumbent world. + +## Modes + +The mode names what the visitor's success looks like on this surface. + +- **Persuade:** the visitor decides and acts; design is the product. Landing pages, marketing, campaigns, pricing. Earn attention and action. Ship real imagery when the brief needs it; follow the committed world, not category habit. +- **Operate:** the visitor completes a task. App UI, dashboards, editors, admin, settings, tools. Scanability, consistency, native expectations, and the real usage scene outrank expression. Brand lives in precise details. +- **Read:** the visitor understands something. Docs, articles, guides, help, changelogs. Structure for comprehension, then make the reading experience worth staying in. +- **Experience:** the visitor is inside the work itself. Portfolios, galleries, showcases. Let the artifact lead from the first viewport; the interface recedes. + +Choose the mode from the requested surface, not the product, and persist it only in that surface brief. A tool's landing page is still Persuade; a fashion house's documentation is still Read; a docs index is Read, not Persuade. See [new-work.md](reference/new-work.md) for new surfaces and [operate.md](reference/operate.md) for deeper Operate/Read guidance. + +## Commands + +| Command | Category | Description | Reference | +|---|---|---|---| +| `craft [feature]` | Build | Deprecated alias for an ordinary new-work request | [reference/craft.md](reference/craft.md) | +| `shape [feature]` | Build | Plan UX/UI before writing code | [reference/shape.md](reference/shape.md) | +| `init` | Build | Capture durable product context in PRODUCT.md | [reference/init.md](reference/init.md) | +| `document` | Build | Generate DESIGN.md from existing project code | [reference/document.md](reference/document.md) | +| `extract [target]` | Build | Pull reusable tokens and components into design system | [reference/extract.md](reference/extract.md) | +| `critique [target]` | Evaluate | UX design review with heuristic scoring | [reference/critique.md](reference/critique.md) | +| `audit [target]` | Evaluate | Technical quality checks (a11y, perf, responsive) | [reference/audit.md](reference/audit.md) · native: [reference/audit.native.md](reference/audit.native.md) | +| `polish [target]` | Refine | Final quality pass before shipping | [reference/polish.md](reference/polish.md) | +| `bolder [target]` | Refine | Amplify safe or bland designs | [reference/bolder.md](reference/bolder.md) | +| `quieter [target]` | Refine | Tone down aggressive or overstimulating designs | [reference/quieter.md](reference/quieter.md) | +| `distill [target]` | Refine | Strip to essence, remove complexity | [reference/distill.md](reference/distill.md) | +| `harden [target]` | Refine | Production-ready: errors, i18n, edge cases | [reference/harden.md](reference/harden.md) | +| `onboard [target]` | Refine | Design first-run flows, empty states, activation | [reference/onboard.md](reference/onboard.md) | +| `animate [target]` | Enhance | Add purposeful animations and motion | [reference/animate.md](reference/animate.md) | +| `colorize [target]` | Enhance | Add strategic color to monochromatic UIs | [reference/colorize.md](reference/colorize.md) | +| `typeset [target]` | Enhance | Improve typography hierarchy and fonts | [reference/typeset.md](reference/typeset.md) | +| `layout [target]` | Enhance | Fix spacing, rhythm, and visual hierarchy | [reference/layout.md](reference/layout.md) | +| `delight [target]` | Enhance | Add personality and memorable touches | [reference/delight.md](reference/delight.md) | +| `overdrive [target]` | Enhance | Push past conventional limits | [reference/overdrive.md](reference/overdrive.md) | +| `clarify [target]` | Fix | Improve UX copy, labels, and error messages | [reference/clarify.md](reference/clarify.md) | +| `adapt [target]` | Fix | Adapt for different devices and screen sizes | [reference/adapt.md](reference/adapt.md) · native: [reference/adapt.native.md](reference/adapt.native.md) | +| `optimize [target]` | Fix | Diagnose and fix UI performance | [reference/optimize.md](reference/optimize.md) | +| `live` | Iterate | Visual variant mode: pick elements in the browser, generate alternatives | [reference/live.md](reference/live.md) | + +Routing: + +- **No argument:** read [routing.md](reference/routing.md) and present its context-aware menu; never auto-run a command. +- **Explicit or clearly implied request to run a command:** load its reference (native variant on native platforms) and follow it. Ask once if two commands fit. +- **Workflow or command-selection question:** read [Workflow questions](reference/routing.md#workflow-questions). +- **Otherwise:** treat the request as general design work. Missing PRODUCT.md routes a new surface or replacement world through init, then new-work; a narrow refinement of existing code proceeds on the incumbent implementation as `impeccable context` directs, offering init afterward rather than blocking on it. +- `teach` aliases `init`. `craft` is a deprecated alias for ordinary new-work and adds nothing. `shape` owns task discovery, then enters new-work only for visual-world and surface-concept decisions. + +After init writes PRODUCT.md, resume without rerunning `impeccable context`; init loads the native platform reference itself when the platform it recorded is `ios`, `android`, or `adaptive`. + +**Pin / Unpin:** `.claude/skills/impeccable/scripts/impeccable pin ` creates or removes a standalone `/` shortcut. Report the script's result concisely; relay stderr verbatim on error. + +**Hooks:** `/impeccable hooks ` manages the design detector hook for this project (auto-runs the detector after UI file edits and surfaces findings). Load [reference/hooks.md](reference/hooks.md) when the user invokes it with any argument. + +**Doctor:** `/impeccable doctor` reports and repairs drift between this project's Impeccable artifacts (PRODUCT.md, DESIGN.md and its sidecar, config, surface briefs, the hook) and what this version reads. Load [reference/doctor.md](reference/doctor.md) when the user invokes it, or when they ask what is out of date, stale, or needs refreshing. A `CONTEXT_STALE` directive in Setup's output is the cheap subset of the same report; act on it there per its own instructions rather than running doctor unasked. + +**Never repair drift as a side effect of a design task.** A `CONTEXT_STALE` finding is reported, not acted on, unless the user asks. The one exception is a finding marked `auto`, which the next write to that file performs anyway. \ No newline at end of file diff --git a/.agent/skills/impeccable/reference/adapt.md b/.agent/skills/impeccable/reference/adapt.md new file mode 100644 index 0000000..7f76bbb --- /dev/null +++ b/.agent/skills/impeccable/reference/adapt.md @@ -0,0 +1,312 @@ +> **Additional context needed**: target platforms/devices and usage contexts. + +Adapt an existing design to a different context: another screen size, device, platform, or use case. The trap is treating adaptation as scaling. The job is rethinking the experience for the new context. + +**Web only** (mobile web included). Native platforms (`ios` / `android` / `adaptive`) route to [adapt.native.md](adapt.native.md) instead; if the project is native, switch to it now. + +--- + +## Assess Adaptation Challenge + +Understand what needs adaptation and why: + +1. **Identify the source context**: + - What was it designed for originally? (Desktop web? Mobile app?) + - What assumptions were made? (Large screen? Mouse input? Fast connection?) + - What works well in current context? + +2. **Understand target context**: + - **Device**: Mobile, tablet, desktop, TV, watch, print? + - **Input method**: Touch, mouse, keyboard, voice, gamepad? + - **Screen constraints**: Size, resolution, orientation? + - **Connection**: Fast wifi, slow 3G, offline? + - **Usage context**: On-the-go vs desk, quick glance vs focused reading? + - **User expectations**: What do users expect on this platform? + +3. **Identify adaptation challenges**: + - What won't fit? (Content, navigation, features) + - What won't work? (Hover states on touch, tiny touch targets) + - What's inappropriate? (Desktop patterns on mobile, mobile patterns on desktop) + +**CRITICAL**: Adaptation is rethinking the experience for the new context, not scaling pixels. + +## Plan Adaptation Strategy + +Create context-appropriate strategy: + +### Mobile Adaptation (Desktop → Mobile) + +**Layout Strategy**: +- Single column instead of multi-column +- Vertical stacking instead of side-by-side +- Full-width components instead of fixed widths +- Bottom navigation instead of top/side navigation + +**Interaction Strategy**: +- Touch targets 44x44px minimum (not hover-dependent) +- Swipe gestures where appropriate (lists, carousels) +- Bottom sheets instead of dropdowns +- Thumbs-first design (controls within thumb reach) +- Larger tap areas with more spacing + +**Content Strategy**: +- Progressive disclosure (don't show everything at once) +- Prioritize primary content (secondary content in tabs/accordions) +- Shorter text (more concise) +- Larger text (16px minimum) + +**Navigation Strategy**: +- Hamburger menu or bottom navigation +- Reduce navigation complexity +- Sticky headers for context +- Back button in navigation flow + +### Tablet Adaptation (Hybrid Approach) + +**Layout Strategy**: +- Two-column layouts (not single or three-column) +- Side panels for secondary content +- Master-detail views (list + detail) +- Adaptive based on orientation (portrait vs landscape) + +**Interaction Strategy**: +- Support both touch and pointer +- Touch targets 44x44px but allow denser layouts than phone +- Side navigation drawers +- Multi-column forms where appropriate + +### Desktop Adaptation (Mobile → Desktop) + +**Layout Strategy**: +- Multi-column layouts (use horizontal space) +- Side navigation always visible +- Multiple information panels simultaneously +- Fixed widths with max-width constraints (don't stretch to 4K) + +**Interaction Strategy**: +- Hover states for additional information +- Keyboard shortcuts +- Right-click context menus +- Drag and drop where helpful +- Multi-select with Shift/Cmd + +**Content Strategy**: +- Show more information upfront (less progressive disclosure) +- Data tables with many columns +- Richer visualizations +- More detailed descriptions + +### Print Adaptation (Screen → Print) + +**Layout Strategy**: +- Page breaks at logical points +- Remove navigation, footer, interactive elements +- Black and white (or limited color) +- Proper margins for binding + +**Content Strategy**: +- Expand shortened content (show full URLs, hidden sections) +- Add page numbers, headers, footers +- Include metadata (print date, page title) +- Convert charts to print-friendly versions + +### Email Adaptation (Web → Email) + +**Layout Strategy**: +- Narrow width (600px max) +- Single column only +- Inline CSS (no external stylesheets) +- Table-based layouts (for email client compatibility) + +**Interaction Strategy**: +- Large, obvious CTAs (buttons not text links) +- No hover states (not reliable) +- Deep links to web app for complex interactions + +## Implement Adaptations + +Apply changes systematically: + +### Responsive Breakpoints + +Choose appropriate breakpoints: +- Mobile: 320px-767px +- Tablet: 768px-1023px +- Desktop: 1024px+ +- Or content-driven breakpoints (where design breaks) + +### Layout Adaptation Techniques + +- **CSS Grid/Flexbox**: Reflow layouts automatically +- **Container Queries**: Adapt based on container, not viewport +- **`clamp()`**: Fluid sizing between min and max +- **Media queries**: Different styles for different contexts +- **Display properties**: Show/hide elements per context + +### Touch Adaptation + +- Increase touch target sizes (44x44px minimum) +- Add more spacing between interactive elements +- Remove hover-dependent interactions +- Add touch feedback (ripples, highlights) +- Consider thumb zones (easier to reach bottom than top) + +### Content Adaptation + +- Use `display: none` sparingly (still downloads) +- Progressive enhancement (core content first, enhancements on larger screens) +- Lazy loading for off-screen content +- Responsive images (`srcset`, `picture` element) + +### Navigation Adaptation + +- Transform complex nav to hamburger/drawer on mobile +- Bottom nav bar for mobile apps +- Persistent side navigation on desktop +- Breadcrumbs on smaller screens for context + +**IMPORTANT**: Test on real devices. Device emulation in DevTools is helpful but not perfect. + +**NEVER**: +- Hide core functionality on mobile (if it matters, make it work) +- Assume desktop = powerful device (consider accessibility, older machines) +- Use different information architecture across contexts (confusing) +- Break user expectations for platform (mobile users expect mobile patterns) +- Forget landscape orientation on mobile/tablet +- Use generic breakpoints blindly (use content-driven breakpoints) +- Ignore touch on desktop (many desktop devices have touch) + +## Verify Adaptations + +Test thoroughly across contexts: + +- **Real devices**: Test on actual phones, tablets, desktops +- **Different orientations**: Portrait and landscape +- **Different browsers**: Safari, Chrome, Firefox, Edge +- **Different OS**: iOS, Android, Windows, macOS +- **Different input methods**: Touch, mouse, keyboard +- **Edge cases**: Very small screens (320px), very large screens (4K) +- **Slow connections**: Test on throttled network + +When the adaptation feels native to each context, hand off to `/impeccable polish` for the final pass. + +--- + +## Reference Material + +The sections below were previously `responsive-design.md` and live inline now so the adapt flow has its deep responsive reference in one place. + +### Responsive Design + +#### Mobile-First: Write It Right + +Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first. + +#### Breakpoints: Content-Driven + +Don't chase device sizes; let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints. + +#### Detect Input Method, Not Just Screen Size + +**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard. Use pointer and hover queries: + +```css +/* Fine pointer (mouse, trackpad) */ +@media (pointer: fine) { + .button { padding: 8px 16px; } +} + +/* Coarse pointer (touch, stylus) */ +@media (pointer: coarse) { + .button { padding: 12px 20px; } /* Larger touch target */ +} + +/* Device supports hover */ +@media (hover: hover) { + .card:hover { transform: translateY(-2px); } +} + +/* Device doesn't support hover (touch) */ +@media (hover: none) { + .card { /* No hover state - use active instead */ } +} +``` + +**Critical**: Don't rely on hover for functionality. Touch users can't hover. + +#### Safe Areas: Handle the Notch + +Modern phones have notches, rounded corners, and home indicators. Use `env()`: + +```css +body { + padding-top: env(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); +} + +/* With fallback */ +.footer { + padding-bottom: max(1rem, env(safe-area-inset-bottom)); +} +``` + +**Enable viewport-fit** in your meta tag: +```html + +``` + +#### Responsive Images: Get It Right + +##### srcset with Width Descriptors + +```html +Hero image +``` + +**How it works**: +- `srcset` lists available images with their actual widths (`w` descriptors) +- `sizes` tells the browser how wide the image will display +- Browser picks the best file based on viewport width AND device pixel ratio + +##### Picture Element for Art Direction + +When you need different crops/compositions (not just resolutions): + +```html + + + + ... + +``` + +#### Layout Adaptation Patterns + +**Navigation**: Three stages: hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `
/` for content that can collapse on mobile. + +#### Testing: Don't Trust DevTools Alone + +DevTools device emulation is useful for layout but misses: + +- Actual touch interactions +- Real CPU/memory constraints +- Network latency patterns +- Font rendering differences +- Browser chrome/keyboard appearances + +**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators. + +--- + +**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful. diff --git a/.agent/skills/impeccable/reference/adapt.native.md b/.agent/skills/impeccable/reference/adapt.native.md new file mode 100644 index 0000000..f1ccd65 --- /dev/null +++ b/.agent/skills/impeccable/reference/adapt.native.md @@ -0,0 +1,58 @@ +> **Additional context needed**: target platforms/devices and usage contexts. + +Adapt an existing **native** design (`ios` / `android` / `adaptive`) to a different context: another device class, orientation, platform, or origin. The trap is treating adaptation as scaling. The job is rethinking the experience for the new context, inside the platform conventions of [ios.md](ios.md) / [android.md](android.md); read the target platform's reference before planning if Setup hasn't already. + +## Assess Adaptation Challenge + +1. **Source context**: what was it designed for, and what assumptions did it make? (Phone-only? Portrait-only? One platform's idioms? A website?) +2. **Target context**: which device class (phone, tablet, foldable), orientation, platform, and usage posture (one-handed on the go vs two-handed at rest)? +3. **What breaks**: navigation that doesn't fit the target, layouts that stretch instead of restructure, gestures or controls that don't exist there? + +## Adaptation Strategies + +### Phone → Tablet (iPad / large screens) + +- **Restructure, don't stretch.** A scaled-up phone UI on a tablet is the failure mode. Use size classes (iOS) / window size classes (Android) to switch structure. +- **Navigation changes shape**: tab bar stays or becomes a sidebar on iPad; Android navigation bar becomes a rail or drawer on expanded width. +- **Use the width**: split view / master-detail (list + detail side by side), multi-column grids, popovers where phones used sheets. +- **Multitasking is a size, not an edge case**: iPad Split View and Android multi-window can hand you a phone-width window on a tablet; size-class-driven layout handles both for free. + +### Orientation & foldables + +- Landscape restructures (side-by-side panes, repositioned controls); never clip or letterbox. Lock orientation only when the task truly demands it. +- Foldables (Android): react to posture and hinge via window size classes; test folded, unfolded, and tabletop. + +### Platform → platform (iOS ↔ Android) + +Translate idioms; never transplant them: + +| iOS | Android | +|---|---| +| Tab bar | Navigation bar / rail / drawer | +| Edge-swipe back, back chevron | Predictive Back gesture / button | +| Switch, segmented control, system pickers | Material switch, chips, Material pickers | +| Action sheet | Bottom sheet / Material dialog | +| SF Symbols, SF Pro, Dynamic Type | Material Symbols, Roboto, sp scaling | +| Semantic system colors, materials | Material color roles, tonal elevation | +| System push/sheet transitions | Container transform, shared-axis, fade-through | + +Rebuild navigation and controls in the target's vocabulary; carry over the brand's expressive layer (palette intent, type accent, motion personality) through the target's theming system. + +### Web → native (porting a website or web app) + +Reconform, don't reflow. Replace web navigation with the platform's model, HTML-shaped controls with platform controls, hover affordances with touch-first ones, and px-based type with Dynamic Type / sp. Then treat the result to the full platform reference; the slop test there is the acceptance bar. + +## Implement & Verify + +- Drive structure from **size classes / window size classes**, never from device-model checks. +- Respect safe areas and window insets in every new configuration (notch, hinge, status bar, keyboard). +- Test on simulators for breadth, then real hardware for truth: at least one phone and one tablet per shipped platform, both orientations, split-screen where supported. + +When the adaptation feels native to each context, hand off to `/impeccable polish` for the final pass. + +**NEVER**: +- Ship a stretched phone layout on a tablet +- Port one platform's controls or navigation onto the other +- Hide core functionality on smaller devices (if it matters, make it work) +- Lock orientation to dodge a layout bug +- Trust simulators alone (posture, gestures, and performance need hardware) diff --git a/.agent/skills/impeccable/reference/android.md b/.agent/skills/impeccable/reference/android.md new file mode 100644 index 0000000..1f67a6b --- /dev/null +++ b/.agent/skills/impeccable/reference/android.md @@ -0,0 +1,46 @@ +# Android platform + +For native Android apps: Jetpack Compose, Android Views, React Native, Expo, Flutter shipping to Android hardware. + +On native, the visitor mode narrows what expression may override. Material Design 3 governs structure, navigation, and interaction in every mode; brand expresses through Material's theming (color roles, type scale, shape, motion). A Material-everywhere cross-platform app that also ships to iPhone still owes iOS its OS guarantees on that hardware: safe-area insets, Reduce Motion, edge-swipe back. + +## The Android slop test + +Would a fluent Android user trust this app, or trip on off-spec components? The most common tell is an iOS app wearing Android's skin: a bottom-only navigation copied from iPhone, a back arrow that ignores the system Back gesture, Cupertino-shaped switches and dialogs. Material 3 is the rulebook; follow its components and theme the brand through it. + +## Layout & structure + +- **Material navigation, matched to size.** Navigation bar (bottom, 3–5 destinations) on compact width; navigation rail or drawer on expanded width. Never ship a phone bottom-bar untouched on a tablet. +- **System Back always works.** Honor the predictive Back gesture and Back button; never trap the user or hijack the gesture. +- **Edge-to-edge with window insets.** Apply the status bar, navigation bar, display cutout, and IME insets so content never hides behind system bars or the keyboard. +- **Top app bar for screen context**; pair with a FAB when the screen has a single primary action. + +## Touch targets + +- **48×48 dp minimum** for every touch target, with at least 8 dp between them. + +## Typography + +- **Material type scale.** Display, Headline, Title, Body, Label roles (large/medium/small each). Map text to roles; never hand-pick sizes per screen. +- **Roboto is the system face**; theme a brand face in through the type scale, keeping body, labels, and controls legible and consistent. +- **sp units, never fixed px**, so type follows the system font-size setting. + +## Color & theming + +- **Material color roles** (primary, on-primary, surface, surface-variant, secondary-container, outline, error). Role tokens resolve light/dark and contrast variants automatically; raw hex breaks there. +- **Dynamic Color (Material You)** where it fits: derive the scheme from the user's wallpaper on Android 12+, with a static fallback. +- **Dark theme is a first-class scheme.** Design and test it; never a quick invert. +- **Tonal elevation.** Convey elevation through the standard surface tonal levels (plus shadow where appropriate); no arbitrary drop shadows. + +## Components & motion + +- **Material components.** Buttons (filled / tonal / outlined / text), FAB, switches, chips, snackbars, bottom sheets, Material dialogs, navigation bar/rail/drawer. Never port iOS controls or invent equivalents. +- **One FAB, one primary action.** Never stack FABs or spend one on a secondary task. +- **Snackbars for transient feedback** (actionable when useful, never a toast for that); dialogs only for decisions that must interrupt. +- **Material motion patterns.** Container transform, shared-axis, fade-through, with standard easing and durations; honor the system Remove animations setting with a crossfade or instant cut. + +## Verifying the build + +- **Screenshots come from the emulator or a connected device, never a browser.** Build and install, then capture with `adb exec-out screencap -p > ` (pick a device with `adb -s ` when several are attached). Capture every device class the app ships to, at least one phone and, when tablets are a target, one tablet, and write the files where the review flow expects them. +- **Dark theme and font scale belong in the pass.** `adb shell cmd uimode night yes` flips the theme; `adb shell settings put system font_scale 1.3` (restore `1.0` after) catches the clipped labels a fixed layout hides; with several targets attached, the capture's `-s ` goes on these commands too. +- **Emulators give breadth; gestures, refresh rates, and performance need hardware.** Say which one produced the evidence. diff --git a/.agent/skills/impeccable/reference/animate.md b/.agent/skills/impeccable/reference/animate.md new file mode 100644 index 0000000..4ae4cc5 --- /dev/null +++ b/.agent/skills/impeccable/reference/animate.md @@ -0,0 +1,89 @@ +> **Additional context needed**: performance constraints. + +Use motion to explain state, relationship, and hierarchy, or to create one authored moment the surface has earned. Decoration without purpose is animation debt. + +--- + +## Visitor mode + +- **Persuade + Experience:** motion may carry the voice. Prefer one rehearsed focal sequence to repeated section reveals. +- **Operate + Read:** motion serves feedback, state, and continuity. Keep routine transitions fast and do not make users wait through page-load choreography. +- **Native (`ios` / `android` / `adaptive`):** follow the Motion section of [ios.md](ios.md) or [android.md](android.md), including the platform's Reduce Motion behavior. Do not apply the web tooling below. + +## Find the job + +Inspect the existing motion language, interaction states, target devices, and performance budget. Find only the places where motion would: + +- acknowledge an action; +- make a state change or spatial relationship legible; +- preserve continuity through navigation or layout change; +- direct attention at a meaningful moment; +- embody the selected visual world. + +Ask only when a material constraint cannot be inferred. Do not animate a static area merely because it exists. + +## Set the motion thesis + +Write a short plan before implementation: + +- **Focal moment:** the one sequence or interaction that deserves authorship, if any. +- **Continuity:** the state, layout, or navigation changes that need explanation. +- **Feedback:** the controls and outcomes that need acknowledgment. +- **Budget:** which effects may be expensive and how often they run. + +The focal moment must come from this product and surface concept. A generic fade-and-rise, hover lift, parallax layer, or scroll reveal is not a thesis. + +## Choose material by meaning + +Transform and opacity are reliable foundations, not the entire palette. Choose properties for what the transition communicates: + +- **Continuity and relationship:** shared-element motion, FLIP-style transforms, view transitions, or deliberate spatial movement. +- **Focus and depth:** bounded blur, filter, backdrop, light, or shadow changes. +- **Reveal and composition:** masks, clip paths, cropping, or controlled occlusion. +- **Material and energy:** color, gradient position, texture, distortion, or shader effects when the world and runtime support them. +- **State and feedback:** the smallest change that makes cause and result unmistakable. + +Do not stack techniques for spectacle. One strong material idea, carried through the focal sequence and quiet supporting states, is usually enough. + +Sibling stagger is appropriate when a list appears as a list. Cap the total delay, and never reinterpret every scrolled section as a staggered list. + +## Timing and easing + +Timing should express distance and consequence: + +| Duration | Typical use | +|---|---| +| 100–150 ms | immediate feedback | +| 150–300 ms | routine state change | +| 300–500 ms | layout, overlay, or view transition | +| 500–800 ms | a deliberately authored focal entrance | + +Exit faster than entrance. Use natural deceleration such as `cubic-bezier(0.16, 1, 0.3, 1)` for confident arrivals; do not use bounce or elastic curves by reflex. Long feedback feels like latency. + +## Implement to the runtime + +- Use CSS transitions and keyframes for declarative state and bounded sequences. +- Use Web Animations API or the project's existing motion library for interruption, sequencing, and dynamic values. +- Use View Transitions or shared-element techniques when continuity across states is the point. +- Use scroll-driven motion only when the scroll relationship itself carries meaning, with a robust fallback. +- Do not add a dependency for an effect the existing stack can express cleanly. + +Keep content visible in the default state so failed scripts do not hide the page. Avoid casually animating layout-driving properties such as `width`, `height`, `top`, `left`, and margins; use FLIP, transforms, or grid techniques when appropriate. Bound blur, filter, shadow, canvas, and shader work to isolated regions. Apply `will-change` only during known animation. Measure on target viewports and devices rather than assuming transform means fast. + +## Accessibility and control + +Respect autoplay and sound preferences. Any nonessential loop must stop when offscreen or hidden. + +Every web animation needs a `prefers-reduced-motion` path with an intentional alternative. Remove or reduce spatial movement while preserving opacity, color, and state transitions that carry meaning. Reduced motion means fewer and gentler animations, not disabling all motion; feedback that confirms an action should remain legible. + +## Verify + +- The focal motion is specific to the selected world and surface. +- Every supporting animation explains feedback, state, or relationship. +- Interruption and repeated use behave correctly. +- Desktop, mobile, and keyboard paths remain usable. +- The `prefers-reduced-motion` path reduces movement without erasing meaningful feedback or state changes. +- Expensive effects stay smooth on the target device. +- Removing an animation would lose meaning or authored character, not merely decoration. + +When motion earns its place, hand off to `/impeccable polish` for the final pass. diff --git a/.agent/skills/impeccable/reference/audit.md b/.agent/skills/impeccable/reference/audit.md new file mode 100644 index 0000000..474af41 --- /dev/null +++ b/.agent/skills/impeccable/reference/audit.md @@ -0,0 +1,136 @@ +Run systematic **technical** quality checks and generate a comprehensive report. Don't fix issues; document them for other commands to address. + +This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation. + +**Web only.** Native platforms (`ios` / `android` / `adaptive`) route to [audit.native.md](audit.native.md) instead; if the project is native, switch to it now. + +## Diagnostic Scan + +Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below. + +### 1. Accessibility (A11y) + +**Check for**: +- **Contrast issues**: Text contrast ratios < 4.5:1 (or 7:1 for AAA) +- **Motion sensitivity**: `prefers-reduced-motion` needs an intentional alternative that preserves state change and hierarchy; flag a global `0.01ms` kill that destroys useful feedback, flashing above threshold, and motion that blocks focus, reading, or task completion +- **Missing ARIA**: Interactive elements without proper roles, labels, or states +- **Keyboard navigation**: Missing focus indicators, illogical tab order, keyboard traps +- **Semantic HTML**: Improper heading hierarchy, missing landmarks, divs instead of buttons +- **Alt text**: Missing or poor image descriptions +- **Form issues**: Inputs without labels, poor error messaging, missing required indicators + +**Score 0-4**: 0=Inaccessible (fails WCAG A), 1=Major gaps (few ARIA labels, no keyboard nav), 2=Partial (some a11y effort, significant gaps), 3=Good (WCAG AA mostly met, minor gaps), 4=Excellent (WCAG AA fully met, approaches AAA) + +### 2. Performance + +**Check for**: +- **Layout thrashing**: Reading/writing layout properties in loops +- **Expensive animations**: Casual layout-property animation, unbounded blur/filter/shadow effects, or effects that visibly drop frames +- **Missing optimization**: Images without lazy loading, unoptimized assets +- **will-change overuse**: `will-change` applied broadly or left on at rest (it is a targeted hint for known expensive animations, not a baseline requirement) +- **Bundle size**: Unnecessary imports, unused dependencies +- **Render performance**: Unnecessary re-renders, missing memoization + +**Score 0-4**: 0=Severe issues (layout thrash, unoptimized everything), 1=Major problems (no lazy loading, expensive animations), 2=Partial (some optimization, gaps remain), 3=Good (mostly optimized, minor improvements possible), 4=Excellent (fast, lean, well-optimized) + +### 3. Theming + +**Check for**: +- **Hard-coded colors**: Colors not using design tokens +- **Broken dark mode**: Missing dark mode variants, poor contrast in dark theme +- **Inconsistent tokens**: Using wrong tokens, mixing token types +- **Theme switching issues**: Values that don't update on theme change + +**Score 0-4**: 0=No theming (hard-coded everything), 1=Minimal tokens (mostly hard-coded), 2=Partial (tokens exist but inconsistently used), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works perfectly) + +### 4. Responsive Design + +**Check for**: +- **Fixed widths**: Hard-coded widths that break on mobile +- **Touch targets**: Interactive elements < 44x44px +- **Horizontal scroll**: Content overflow on narrow viewports +- **Text scaling**: Layouts that break when text size increases +- **Missing breakpoints**: No mobile/tablet variants + +**Score 0-4**: 0=Desktop-only (breaks on mobile), 1=Major issues (some breakpoints, many failures), 2=Partial (works on mobile, rough edges), 3=Good (responsive, minor touch target or overflow issues), 4=Excellent (fluid, all viewports, proper touch targets) + +### 5. Implementation Integrity (CRITICAL) + +Run the bundled detector and verify each finding in context. Look for repeated implementation shortcuts, design-system drift, misleading or decorative content, and structure that is interchangeable with an unrelated product. Keep deterministic findings separate from visual judgment and call out false positives. + +**Score 0-4**: 0=systemic drift, 1=major repeated failures, 2=several verified issues, 3=minor isolated issues, 4=coherent and intentional + +## Generate Report + +### Audit Health Score + +| # | Dimension | Score | Key Finding | +|---|-----------|-------|-------------| +| 1 | Accessibility | ? | [most critical a11y issue or "--"] | +| 2 | Performance | ? | | +| 3 | Responsive Design | ? | | +| 4 | Theming | ? | | +| 5 | Implementation Integrity | ? | | +| **Total** | | **??/20** | **[Rating band]** | + +**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues) + +### Implementation Integrity Verdict +**Start here.** Pass/fail: does the implementation express a coherent product-specific system? Cite verified evidence and detector findings. + +### Executive Summary +- Audit Health Score: **??/20** ([rating band]) +- Total issues found (count by severity: P0/P1/P2/P3) +- Top 3-5 critical issues +- Recommended next steps + +### Detailed Findings by Severity + +Tag every issue with **P0-P3 severity**: +- **P0 Blocking**: Prevents task completion. Fix immediately +- **P1 Major**: Significant difficulty or WCAG AA violation. Fix before release +- **P2 Minor**: Annoyance, workaround exists. Fix in next pass +- **P3 Polish**: Nice-to-fix, no real user impact. Fix if time permits + +For each issue, document: +- **[P?] Issue name** +- **Location**: Component, file, line +- **Category**: Accessibility / Performance / Theming / Responsive / Implementation Integrity +- **Impact**: How it affects users +- **WCAG/Standard**: Which standard it violates (if applicable) +- **Recommendation**: How to fix it +- **Suggested command**: Which command to use (prefer: /impeccable adapt, /impeccable animate, /impeccable audit, /impeccable bolder, /impeccable clarify, /impeccable colorize, /impeccable critique, /impeccable delight, /impeccable distill, /impeccable document, /impeccable harden, /impeccable layout, /impeccable onboard, /impeccable optimize, /impeccable overdrive, /impeccable polish, /impeccable quieter, /impeccable shape, /impeccable typeset) + +### Patterns & Systemic Issues + +Identify recurring problems that indicate systemic gaps rather than one-off mistakes: +- "Hard-coded colors appear in 15+ components, should use design tokens" +- "Touch targets consistently too small (<44px) throughout mobile experience" + +### Positive Findings + +Note what's working well: good practices to maintain and replicate. + +## Recommended Actions + +List recommended commands in priority order (P0 first, then P1, then P2): + +1. **[P?] `/command-name`**: Brief description (specific context from audit findings) +2. **[P?] `/command-name`**: Brief description (specific context) + +**Rules**: Only recommend commands from: /impeccable adapt, /impeccable animate, /impeccable audit, /impeccable bolder, /impeccable clarify, /impeccable colorize, /impeccable critique, /impeccable delight, /impeccable distill, /impeccable document, /impeccable harden, /impeccable layout, /impeccable onboard, /impeccable optimize, /impeccable overdrive, /impeccable polish, /impeccable quieter, /impeccable shape, /impeccable typeset. Map findings to the most appropriate command. End with `/impeccable polish` as the final step if any fixes were recommended. + +After presenting the summary, tell the user: + +> You can ask me to run these one at a time, all at once, or in any order you prefer. +> +> Re-run `/impeccable audit` after fixes to see your score improve. + +**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters. + +**NEVER**: +- Report issues without explaining impact (why does this matter?) +- Provide generic recommendations (be specific and actionable) +- Skip positive findings (celebrate what works) +- Forget to prioritize (everything can't be P0) +- Report false positives without verification diff --git a/.agent/skills/impeccable/reference/audit.native.md b/.agent/skills/impeccable/reference/audit.native.md new file mode 100644 index 0000000..fbccb56 --- /dev/null +++ b/.agent/skills/impeccable/reference/audit.native.md @@ -0,0 +1,139 @@ +Run systematic **technical** quality checks on a native app (`ios` / `android` / `adaptive`) and generate a comprehensive report. Don't fix issues; document them for other commands to address. + +This is a code-level audit, not a design critique. Audit from source (SwiftUI / UIKit / Compose / React Native / Flutter); no browser tooling or `impeccable detect` applies. Score against the platform reference(s): [ios.md](ios.md) / [android.md](android.md), both for `adaptive`. Read them before scoring if Setup hasn't already. The report skeleton mirrors [audit.md](audit.md); keep the two in sync when changing it. + +## Diagnostic Scan + +Run comprehensive checks across 5 dimensions. Score each dimension 0-4 using the criteria below. + +### 1. Accessibility (VoiceOver / TalkBack) + +**Check for**: +- **Missing labels**: interactive elements without accessibility labels, traits/roles, or state announcements +- **Reading and focus order**: illogical traversal, unreachable controls, focus lost on navigation +- **Text scaling**: fixed point sizes defeating Dynamic Type (iOS) or px instead of sp (Android); layouts that clip or overlap at large sizes +- **Touch targets**: below 44 pt (iOS) / 48 dp (Android), or crammed without spacing +- **Reduce Motion ignored**: parallax and large slides with no crossfade alternative +- **Contrast**: text failing contrast in either appearance, light or dark + +**Score 0-4**: 0=Screen reader unusable, 1=Major gaps (unlabeled controls, no scaling), 2=Partial (labels exist, order or scaling breaks), 3=Good (minor gaps), 4=Excellent (labeled, ordered, scales cleanly, Reduce Motion honored) + +### 2. Performance + +**Check for**: +- **Slow startup**: heavy work on launch before first frame +- **Unvirtualized lists**: long content without FlatList / LazyColumn / List recycling +- **Main-thread jank**: synchronous work in scroll or gesture paths, dropped frames on 60/120 Hz +- **Wasted rendering**: unnecessary re-renders (React Native) or recompositions (Compose); missing memoization/keys +- **Image handling**: full-size images decoded for thumbnails, no caching +- **App weight**: bloated JS bundle or binary, unused dependencies + +**Score 0-4**: 0=Janky everywhere, 1=Major problems (unvirtualized lists, slow launch), 2=Partial, 3=Good (minor improvements possible), 4=Excellent (fast launch, smooth scroll, lean) + +### 3. Appearance & Theming + +**Check for**: +- **Hard-coded colors**: raw hex instead of semantic system colors (iOS) / Material color roles (Android) / design tokens +- **Broken dark appearance**: missing dark variants, poor contrast in dark, quick inverts +- **Dynamic Color** (Android 12+): no static fallback scheme, or ignored where it fits +- **Off-platform materials**: hand-rolled visual materials where system materials or tonal elevation are expected + +**Score 0-4**: 0=Hard-coded everything, 1=Minimal tokens, 2=Partial (tokens exist, inconsistently used), 3=Good (minor hard-coded values), 4=Excellent (semantic throughout, both appearances first-class) + +### 4. Platform Conformance (CRITICAL) + +Score against the loaded platform reference(s), including their slop tests. **Check for**: +- **Broken system gestures**: edge-swipe back disabled (iOS), predictive Back hijacked (Android) +- **Inset violations**: content under the notch, Dynamic Island, home indicator, status bar, or keyboard +- **Off-platform navigation**: custom global nav, overloaded tab bars, iOS patterns on Android or vice versa +- **Web-shaped controls**: HTML-style buttons, custom toggles, hover-dependent affordances +- **Icon drift**: mixed icon sets instead of SF Symbols / Material Symbols +- **System drift**: repeated shortcuts or decorative patterns that conflict with the product, platform, or established design system + +**Score 0-4**: 0=Web port (nothing native), 1=Heavy violations (3-4 kinds), 2=Some (1-2 noticeable), 3=Mostly conformant (subtle issues), 4=Fully native (a fluent user trusts every screen) + +### 5. Adaptivity + +**Check for**: +- **Stretched phone layouts**: tablet/iPad rendering a scaled-up phone UI instead of using size classes / window size classes +- **Orientation breakage**: landscape clipping, ignored, or locked without reason +- **Keyboard/IME handling**: inputs hidden behind the keyboard, no inset adjustment +- **Multitasking**: iPad Split View / Android multi-window breaking layout +- **Foldables**: hinge-unaware layouts on posture change (Android) + +**Score 0-4**: 0=One screen size only, 1=Major breakage (landscape or tablet broken), 2=Partial, 3=Good (minor edge cases), 4=Excellent (adapts across sizes, orientations, and windowing) + +## Generate Report + +### Audit Health Score + +| # | Dimension | Score | Key Finding | +|---|-----------|-------|-------------| +| 1 | Accessibility | ? | [most critical issue or "--"] | +| 2 | Performance | ? | | +| 3 | Appearance & Theming | ? | | +| 4 | Platform Conformance | ? | | +| 5 | Adaptivity | ? | | +| **Total** | | **??/20** | **[Rating band]** | + +**Rating bands**: 18-20 Excellent (minor polish), 14-17 Good (address weak dimensions), 10-13 Acceptable (significant work needed), 6-9 Poor (major overhaul), 0-5 Critical (fundamental issues) + +### Platform Conformance Verdict +**Start here.** Pass/fail: does this read as a native app or a ported website? List specific violations. Be brutally honest. + +### Executive Summary +- Audit Health Score: **??/20** ([rating band]) +- Total issues found (count by severity: P0/P1/P2/P3) +- Top 3-5 critical issues +- Recommended next steps + +### Detailed Findings by Severity + +Tag every issue with **P0-P3 severity**: +- **P0 Blocking**: Prevents task completion. Fix immediately +- **P1 Major**: Significant difficulty or platform-guideline violation. Fix before release +- **P2 Minor**: Annoyance, workaround exists. Fix in next pass +- **P3 Polish**: Nice-to-fix, no real user impact. Fix if time permits + +For each issue, document: +- **[P?] Issue name** +- **Location**: Screen, file, line +- **Category**: Accessibility / Performance / Theming / Conformance / Adaptivity +- **Impact**: How it affects users +- **Guideline**: The HIG / Material rule it violates (if applicable) +- **Recommendation**: How to fix it +- **Suggested command**: Which command to use (prefer: /impeccable adapt, /impeccable animate, /impeccable audit, /impeccable bolder, /impeccable clarify, /impeccable colorize, /impeccable critique, /impeccable delight, /impeccable distill, /impeccable document, /impeccable harden, /impeccable layout, /impeccable onboard, /impeccable optimize, /impeccable overdrive, /impeccable polish, /impeccable quieter, /impeccable shape, /impeccable typeset) + +### Patterns & Systemic Issues + +Identify recurring problems that indicate systemic gaps rather than one-off mistakes: +- "Hard-coded colors appear in 15+ screens, should use semantic colors" +- "Touch targets consistently below 44 pt throughout the tab bar and list rows" + +### Positive Findings + +Note what's working well: good practices to maintain and replicate. + +## Recommended Actions + +List recommended commands in priority order (P0 first, then P1, then P2): + +1. **[P?] `/command-name`**: Brief description (specific context from audit findings) +2. **[P?] `/command-name`**: Brief description (specific context) + +**Rules**: Only recommend commands from: /impeccable adapt, /impeccable animate, /impeccable audit, /impeccable bolder, /impeccable clarify, /impeccable colorize, /impeccable critique, /impeccable delight, /impeccable distill, /impeccable document, /impeccable harden, /impeccable layout, /impeccable onboard, /impeccable optimize, /impeccable overdrive, /impeccable polish, /impeccable quieter, /impeccable shape, /impeccable typeset. Map findings to the most appropriate command. End with `/impeccable polish` as the final step if any fixes were recommended. + +After presenting the summary, tell the user: + +> You can ask me to run these one at a time, all at once, or in any order you prefer. +> +> Re-run `/impeccable audit` after fixes to see your score improve. + +**IMPORTANT**: Be thorough but actionable. Too many P3 issues creates noise. Focus on what actually matters. + +**NEVER**: +- Report issues without explaining impact (why does this matter?) +- Provide generic recommendations (be specific and actionable) +- Skip positive findings (celebrate what works) +- Forget to prioritize (everything can't be P0) +- Report false positives without verification diff --git a/.agent/skills/impeccable/reference/bolder.md b/.agent/skills/impeccable/reference/bolder.md new file mode 100644 index 0000000..1cad2f1 --- /dev/null +++ b/.agent/skills/impeccable/reference/bolder.md @@ -0,0 +1,33 @@ +> **Additional context needed**: which section is the target, and what must stay untouched. + +An open direction round owns the word first: "bolder" said while a direction decision is on the table is the Bolder hand register steer, a fresh deal of foreign forms (see new-work.md), not this command. This command refines a surface whose world already shipped. + +"Bolder" is an amplification request, and almost always it is scoped to something that already exists. The surrounding page, its system, and its conventions are the given. Your job is to raise one part to the conviction the rest already implies, without rebuilding anything the brief did not name. The reflex answer, reaching for more effects, is the opposite of bold; reject it first. + +## Scope is sovereign + +"Everything else stays" is a literal instruction. Touch only the named target. Do not restyle its neighbors, do not migrate the page to a new idea, do not add colors, fonts, radii, shadows, or system primitives the surface does not already own. If the existing system genuinely cannot express the direction, do not expand it on your own. STOP and call the AskUserQuestion tool to clarify. Name the exact addition and the job it would do. + +## Why it reads flat + +A section usually reads flat for reasons its neighbors have already solved. Look at what the rest of the page does that this section does not: the display type at full strength, the structural devices that carry meaning, the signature motif, the density and pacing. A flat section is typically one that quietly opts out of the system's own strongest moves. The most reliable bolder pass brings the target up to the expressive level its neighbors already reach, in the system's own vocabulary rather than a new one. + +## The amplification + +- **Amplify what the system already owns.** Reuse its motif and its type scale at full strength, turned up for this section rather than invented for it. The bolder version should look more like the same brand, not less. +- **Keep content true.** Existing claims are part of the scope: preserve them unless the user supplies replacements. If real evidence is essential to the direction but absent, ask for it. +- **Commit, then clarify.** Half-measures read as noise. Make the one decisive move completely, then quiet everything around it so the move is legible. If every element got louder, the section got flatter. +- **Give it its own rhythm.** The target should read as a peak in the scroll, a shift in density or pace from what surrounds it, not simply more of the same. + +## The skeleton test + +Strip the copy out of your planned section and study the bare structure. Does the skeleton still say what this section is and why it matters, through hierarchy and the system's devices alone? If it only works once the words return, the boldness is in the text size, not the design. A placeholder for an image or artifact names a job, an anchor and a piece of evidence, not a cue to drop in a decorative photo; fill that job with whatever the subject actually has. + +## Before you finish + +- Everything outside the named target is unchanged. +- No new color, font, or system primitive appeared without being asked for. +- The conventions the section carried, including anything that drives an action, still work the same way. +- The section is unmistakably the same brand, only more sure of itself. + +When the target holds its own without pulling the page apart, hand off to `/impeccable polish` for the final pass. diff --git a/.agent/skills/impeccable/reference/clarify.md b/.agent/skills/impeccable/reference/clarify.md new file mode 100644 index 0000000..3047a9d --- /dev/null +++ b/.agent/skills/impeccable/reference/clarify.md @@ -0,0 +1,94 @@ +> **Additional context needed**: audience knowledge and emotional state. + +Rewrite unclear interface text so users understand what happened, what matters, and what to do next. Preserve factual meaning, product terminology, and brand voice. + +## Audit the language + +Read the entire interaction path, not isolated strings. Identify: + +- ambiguous nouns, verbs, and actions; +- internal jargon or assumed knowledge; +- vague labels, outcomes, and system states; +- missing consequences, recovery, or timing; +- inconsistent terminology and capitalization; +- redundant headings, intros, helper text, and confirmations; +- text that breaks at realistic widths or in translation; +- tone that ignores stress, risk, success, or urgency. + +Infer audience and task from product context and surrounding UI. Ask before changing factual claims, legal meaning, or a term that may be domain-specific. + +## Set the message hierarchy + +For each state, decide: + +1. the one fact the user needs now; +2. the action available next; +3. supporting context that changes the decision; +4. the appropriate tone for this moment. + +Say each idea once. If the heading already explains the state, the introduction should add new information or disappear. + +## Rewrite by function + +### Actions and navigation + +Use a specific verb and object when the outcome is not already obvious. Labels should describe what will happen, not the gesture used to trigger it. Keep the same noun and verb for the same concept throughout the product. + +For destructive actions, name the object and consequence. Prefer undo over confirmation when recovery is safe. When confirmation is necessary, name the action on both the message and button instead of using `Yes`, `No`, `OK`, or `Submit`. + +### Forms + +Use persistent labels; placeholders are examples, not labels. Put format and eligibility requirements before submission. Explain why information is requested only when it is not obvious. Required and optional treatment should be consistent. + +Validation says what needs attention and how to correct it without blaming the user. Keep related instructions near the field and announce errors accessibly. + +### Errors and permissions + +An actionable error answers: + +1. what failed; +2. why, when known and useful; +3. how to recover or what alternative remains. + +Do not expose internal codes as the primary message. Do not promise a cause or resolution the system cannot know. Treat privacy, payment, deletion, access loss, and blocked work seriously; warmth is welcome, jokes are not. + +### Loading, empty, and success states + +Loading text names the real operation and sets an honest expectation when the wait is meaningful. Show determinate progress when available; never invent progress. + +An empty state distinguishes first use, no results, filters, permissions, and failure. Explain the state and provide the next useful action. + +Success confirms the completed outcome and mentions the next consequence only when it changes what the user should do. Routine success should be brief. + +### Help and instructional text + +Helper text answers an implicit question instead of restating the control. Use progressive disclosure for uncommon detail. Link text must make sense out of context; icon-only controls need accessible names. + +## Voice, accessibility, and localization + +Voice stays consistent; tone adapts to the moment. Use plain language without flattening terminology the audience genuinely knows. + +- Write complete translatable messages rather than concatenated fragments. +- Keep variables and numbers structured so translators can reorder them. +- Allow expansion instead of abbreviating prematurely. +- Make alt text convey the image's information; use empty alt for decoration. +- Keep screen-reader names aligned with visible labels and outcomes. +- Do not rely on punctuation, color, or iconography to carry the message alone. + +Maintain a short terminology glossary when inconsistency spans the product. Do not vary words for literary effect in an interface. + +## Verify + +Read the flow in context and test: + +- comprehension without hidden product knowledge; +- actionability at errors, empty states, and decision points; +- factual accuracy and consistent terminology; +- scanability at target widths and 200% zoom; +- long names, localization expansion, pluralization, and dynamic values; +- accessible names and announced state changes; +- tone appropriate to consequence and emotional context. + +The final copy is as short as it can be without removing meaning or recovery. + +When the language reads cleanly, hand off to `/impeccable polish` for the final pass. diff --git a/.agent/skills/impeccable/reference/colorize.md b/.agent/skills/impeccable/reference/colorize.md new file mode 100644 index 0000000..dc45f88 --- /dev/null +++ b/.agent/skills/impeccable/reference/colorize.md @@ -0,0 +1,86 @@ +> **Additional context needed**: existing brand colors. + +Introduce color as hierarchy, meaning, and atmosphere. Preserve confirmed brand and semantic conventions; do not replace a visual world under the guise of colorizing it. + +--- + +## Visitor mode + +- **Persuade + Experience:** color may carry the voice and own large regions when the selected world calls for it. +- **Operate + Read:** color primarily encodes action, selection, status, wayfinding, and reading hierarchy. Rarity gives an accent force. + +## Audit before choosing + +Read DESIGN.md, tokens, assets, current themes, and representative states. Identify: + +- which colors are confirmed brand commitments; +- current surface, text, action, and semantic roles; +- places where grayscale obscures hierarchy or state; +- contrast failures and color-only communication; +- light/dark or data-visualization requirements; +- whether the task asks for more color or a new identity. + +If a new identity is required, use [new-work.md](new-work.md). Ask only when a binding brand decision cannot be inferred. + +## Choose a strategy + +Name the intended emotional temperature, dominant relationship, contrast range, and color dosage before editing. The strategy may be restrained or immersive; it must follow the brief and selected world rather than a fixed percentage rule. + +Build roles, not a bag of swatches: + +- canvas and elevated surfaces; +- primary and secondary text; +- action, focus, and selection; +- borders and separators; +- success, warning, error, and information; +- data categories or scales when needed. + +Use the project's existing color space. For a new web palette, prefer OKLCH because lightness and chroma can be adjusted predictably. Choose hue from product meaning and visual direction, never from a default category association. + +## Apply at system scale + +- Let the strongest color own a deliberate region or role instead of scattering tiny accents. +- Keep the primary action easy to find; do not spend its color on decoration. +- Tint neutrals only when the brand hue genuinely creates cohesion. Neutral gray is valid when it serves the world. +- On colored surfaces, derive secondary text from the foreground or surface hue rather than using washed-out generic gray. +- Keep semantic meanings consistent, but respect platform and domain conventions instead of assuming fixed hues. +- For data, use distinct lightness, chroma, shape, label, or pattern so color is not the only code. +- In dark mode, design surface elevation and contrast explicitly; do not invert the light theme mechanically. +- Define primitive values and semantic tokens when the project has a token system. Theme changes should normally remap semantic roles. + +Decoration without a relationship to hierarchy, state, content, or the visual world is not a color strategy. + +## Contrast and perception + +Verify computed foreground/background pairs: + +| Content | WCAG AA minimum | +|---|---| +| body text | 4.5:1 | +| large text | 3:1 | +| controls, icons, focus indicators | 3:1 | + +Do not rely on eyesight alone. Check interactive states, overlays, text on images, disabled content, and both themes. Simulate common vision deficiencies. Information conveyed by color also needs text, shape, iconography, or position. + +When deriving OKLCH ramps, vary lightness and reduce chroma near white and black. Do not keep high chroma at extreme lightness merely to make the math uniform. Prefer explicit colors over chains of translucent overlays when alpha would make contrast context-dependent. + +## Verify + +- Every color has a stable role or a world-specific atmospheric purpose. +- Attention lands on the intended action, content, or state. +- The palette works across quiet, dense, interactive, error, and empty states. +- Light and dark themes are each composed, not mechanically inverted. +- Contrast and non-color cues pass in all relevant states. +- The result is recognizably this product, not a generic “colorful” treatment. + +When the palette earns its place, hand off to `/impeccable polish` for the final pass. + +## Live-mode signature params + +When invoked from live mode, every variant declares a `color-amount` parameter. Author CSS against `var(--p-color-amount, 0.5)` so the user can move from neutral to the variant's full color strategy without regeneration. + +```json +{"id":"color-amount","kind":"range","min":0,"max":1,"step":0.05,"default":0.5,"label":"Color amount"} +``` + +Add at most two variant-specific parameters, such as palette, temperature, or tint behavior. Follow [live.md](live.md)'s parameter contract. diff --git a/.agent/skills/impeccable/reference/craft-floor.md b/.agent/skills/impeccable/reference/craft-floor.md new file mode 100644 index 0000000..9653b98 --- /dev/null +++ b/.agent/skills/impeccable/reference/craft-floor.md @@ -0,0 +1,44 @@ +# Craft floor + +Load this after the direction is settled, and build without announcing the checklist. A pinned brief or the committed visual world overrides anything here; your own habit does not. When the design hook is active it already enforces the mechanical checks below as you edit: act on its findings instead of re-auditing each rule. + +## Verify + +Each of these is a check on the built result, not an intention. Run them together in the batched inspection rounds, not as separate screenshot trips; the checks share one render. + +- **Contrast:** body and placeholder text ≥4.5:1, large text ≥3:1. On colored surfaces tint secondary text from that hue or the foreground; never gray. +- **Depth:** shadows carry an offset and a soft blur. A zero-offset colored halo is decoration. +- **Spacing:** tight groups, generous separation, more space above a heading than below it. Read the computed values. +- **Type:** body measure 65–75ch, display max 6rem, tracking floor -0.04em, balanced headings, obvious scale and weight steps. Run the real copy at every breakpoint and fix what overflows. +- **Motion:** one authored moment, not scattered effects and not one identical entrance on every section. Exponential ease-out from an already-visible default. Reach past transform and opacity: blur, backdrop-filter, clip-path, mask, and shadow belong to the palette when they stay smooth. +- **States:** hover, disabled, loading, error, empty. Plus real content, working controls, responsive composition, keyboard focus. +- **Browser surfaces:** the parts you did not draw still carry the design. Text selection, the caret, custom scrollbars, focus rings, underline offset, and the numerals in tabular data all ship with browser defaults that belong to no design system. Theme them from the palette. This is the cheapest signal that a page was built rather than assembled, and the one models skip most reliably. +- **Copy:** the product's own language. Controls name their action; errors name the problem and the recovery. +- **Coverage:** every brief requirement present and findable within seconds. + +## Refuse + +These are the category's defaults, not bans: the brief's own words can earn any of them. Reaching for one when the axis is free means you were not deciding; recognizing that means rewriting the element, not softening it. + +Page scaffolds: + +- Same-size cards of icon plus heading plus text as the page structure. Cards are the lazy container; nested cards are always wrong. +- The hero-metric template: big number, small label, supporting stats, accent. +- A kicker or eyebrow above a heading. This one is a ban, not a default: no brief earns it back. The heading carries its own weight; delete the label and let the heading speak. +- Section numbers (01 / 02 / 03) unless the sequence itself carries information the reader needs. +- A modal for a task that needs neither interruption nor protected focus. + +Surface habits: + +- Gradient text. Emphasis comes from weight or size. +- Glass and blur as decoration rather than as a specific effect. +- A colored `border-left` or `border-right` above 1px on cards, list items, callouts, or alerts. +- Hard offset shadows (`box-shadow: 4px 4px 0`) outside a world that is actually neobrutalist. The zero-blur block shadow is a costume, not a depth system; a world that did not choose it never earns it as a default. +- Sparklines, progress rings, and soft-shadowed rounded rectangles standing in for content. +- Monospace as a costume for "technical" rather than for code, data, or measurement. +- A system display face (Impact, Arial Black, the platform sans) as the display voice of an own-world page. Source and self-host a face whose character matches the approved lettering; the closest installed font is a failure, not a fallback. +- Unicode glyphs or emoji standing in for an icon system. Icons are drawn, from a real library or authored SVG, in one consistent stroke and weight. +- Geometric masks standing in for organic contours. A circle, polygon, or radial-gradient cutout approximating a photographic subject's edge is the cheap version of the effect and reads worse than omitting it. Derive an alpha matte from the actual image, or produce a cut-out asset. +- Light or dark picked by category. Pick it from the use scene: who, where, under what ambient light. + +The floor holds the mechanics; it never picks the direction. With every check green, spend the page on the committed world, and when torn between refined and committed, commit. diff --git a/.agent/skills/impeccable/reference/craft.md b/.agent/skills/impeccable/reference/craft.md new file mode 100644 index 0000000..dbbc940 --- /dev/null +++ b/.agent/skills/impeccable/reference/craft.md @@ -0,0 +1,5 @@ +# Craft (deprecated alias) + +`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish. + +Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow. diff --git a/.agent/skills/impeccable/reference/critique.md b/.agent/skills/impeccable/reference/critique.md new file mode 100644 index 0000000..f4a2f3f --- /dev/null +++ b/.agent/skills/impeccable/reference/critique.md @@ -0,0 +1,806 @@ +### Purpose + +Resolve one stable target, run two independent assessments, synthesize a design critique, persist a snapshot, and ask the user what to improve next. The chat response is the primary deliverable; the snapshot is an archive of that run. + +### Hard Invariants + +- Assessment A (design review) and Assessment B (detector/browser evidence) are both required. +- Assessment A and B MUST run as two isolated sub-agents whenever a sub-agent/Task tool is exposed. Running them inline in this context is "possible" but is NOT permitted; it is a degraded run. Inline is allowed ONLY when no sub-agent tool exists (or the user declined, on harnesses that ask). +- If you degrade for any reason, the report's first line MUST be a banner: `⚠️ DEGRADED: single-context ()`. A silent degraded critique is a failed critique. +- Assessment A must finish before detector findings enter the parent synthesis context. Detector output is deterministic, but it still anchors judgment. +- A skipped detector is a failed critique run unless `impeccable detect` is missing or crashes after a real attempt. +- Viewable targets require browser inspection when available. +- Any local server started only for critique visualization must run in the background, have a recorded stop method, and be stopped before final reporting unless the user asks to keep it. +- Do not claim a user-visible overlay exists unless script injection succeeded and the detector ran in the page. +- The question is the LAST thing in the response. Write the entire report out first, then ask; nothing follows the question. Prose emitted after a structured question is withheld until the user answers it, so a report written after the question reads as if the critique never ran. +- A run that ends with neither the targeted questions nor a literal `Questions skipped: ` line is an incomplete run. The report is not the finish; the close is. + +### Setup + +1. **Resolve the target** to a concrete file path or URL. Prefer a source path over a dev-server URL when both identify the same surface; ports drift, paths do not. + - "the homepage" -> `site/pages/index.astro` or `index.html` + - "the settings modal" -> the primary component file + - "this page" -> the current URL or source file +2. **Confirm the target slugs cleanly**: + ```bash + .claude/skills/impeccable/scripts/impeccable critique-storage slug "" + ``` + Every later command also accepts the resolved target directly and derives the same slug internally; never hand-write a slug. If this exits non-zero, skip persistence and trend for this run, but continue the critique. +3. **Read `.impeccable/critique/ignore.md`** if it exists. Drop matching findings silently; it is the only prior-run input critique consumes. + +### Assessment Orchestration + +Delegate Assessment A and Assessment B to separate sub-agents. They must not see each other's output. Do not show findings to the user until synthesis. + +Sub-agent gate (all harnesses): +- Unless a harness-specific gate below overrides this, spawn A and B as two isolated, parallel sub-agents whenever a sub-agent/Task tool is exposed. This is the default and is mandatory; do not run them inline because it is faster. +- "Unavailable" means exactly one thing: no sub-agent/Task tool is exposed in this session (or, on harnesses that ask, the user declined). It does not mean inconvenient. +- If and only if sub-agents are unavailable, fall back sequentially: finish and record Assessment A, then run Assessment B, then synthesize, and emit the degraded banner. +- Whichever path you take, declare it in the report header (see Report header provenance). Skipping sub-agents without the banner is the most common failure of this command. + +If browser automation is available, each assessment creates its own new tab. Never reuse an existing tab, even if it is already at the right URL. + +### Assessment A: Design Review + +Read relevant source files and visually inspect the live page when browser automation is available. Think like a design director. + +Evaluate: +- **Design specificity**: Is the composition, interaction, and visual language grounded in this product, or could an unrelated product use it unchanged? Make this judgment before seeing detector output. +- **Holistic design**: hierarchy, IA, emotional fit, discoverability, composition, typography, color, accessibility, states, copy, and edge cases. +- **Cognitive load**: consult the [Cognitive Load Assessment](#cognitive-load-assessment) section below; report checklist failures and decision points with >4 visible options. +- **Emotional journey**: peak-end rule, emotional valleys, reassurance at high-stakes moments. +- **Nielsen heuristics**: consult the [Heuristics Scoring Guide](#heuristics-scoring-guide) section below; score all 10 heuristics 0-4, marking any heuristic the mode-applicability rule allows as `n/a` instead of forcing a number. + +Return: design-specificity verdict, heuristic scores, cognitive load, emotional journey, 2-3 strengths, 3-5 priority issues, persona red flags, minor observations, and provocative questions. + +### Assessment B: Detector + Browser Evidence + +Run the bundled detector and browser visualization evidence. Assessment B is mandatory and must remain isolated from Assessment A until both are complete. + +CLI scan: +```bash +.claude/skills/impeccable/scripts/impeccable detect --json [target] +``` + +- Pass markup files/directories as `[target]`; do not pass CSS-only files. +- For URLs, skip CLI scan and use browser visualization. +- For very large trees (500+ scannable files), narrow scope or ask. +- Exit code 0 = clean; 2 = findings. +- If the detector entrypoint is missing or fails to load, report deterministic scan unavailable and continue with browser/manual review. + +Browser visualization is required for a viewable target when browser automation is available. Use a localhost dev/static URL for local files; avoid `file://` unless the available browser explicitly supports this workflow. Overlay flow: + +1. Create a fresh tab and navigate. Prefer the harness's native/browser-canvas screenshot path before hand-rolling a Playwright/Puppeteer script; only fall back to a custom script when no native browser tool is exposed. +2. Preflight mutable injection by setting `document.title` and appending a `