kramme-cc-workflow/skills/kramme:code:frontend-authoring/SKILL.md
Build production-quality UI components with sound architecture, accessibility (WCAG 2.1 AA), and anti-AI-aesthetic defaults. Use when creating or modifying user-facing components. Covers component colocation, composition over configuration, state-management tier selection, accessibility patterns, and concrete replacements for AI-aesthetic defaults (purple gradients, rounded-2xl everywhere, oversized padding, shadow-heavy cards).
npx skillsauth add abildtoft/kramme-cc-workflow kramme:code:frontend-authoringInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build production-quality user interfaces that look engineer-crafted, not AI-generated. This is the author-time discipline that keeps "AI defaults" (purple gradients, rounded-2xl on every surface, oversized padding, stock card grids, shadow-heavy layouts) from being baked in and then flagged by review later. The goal: real design-system adherence, real accessibility, real interaction patterns — from the first draft.
Each user-facing change is one pass through this sequence:
SIMPLICITY CHECK marker (see below) stating the simplest UI that satisfies the requirement.<Thing config={...} /> props.references/ai-aesthetic-table.md against your draft.references/accessibility-checklist.md against the component.When you notice something out-of-scope (an adjacent component that's already using a bad default, a typo in a sibling file), emit NOTICED BUT NOT TOUCHING and move on. Do not silently fix adjacent code.
SIMPLICITY CHECK: <one-line summary of the simplest UI that satisfies the requirement>
If the thing you end up building is not the simplest version, add a second line explaining what forced the expansion.
NOTICED BUT NOT TOUCHING: <what you saw>
Why skipping: <out-of-scope / unrelated / deferred>
Keep everything for one component together:
src/components/
TaskList/
TaskList.tsx # Component implementation
TaskList.test.tsx # Tests
TaskList.stories.tsx # Storybook stories (if using)
use-task-list.ts # Custom hook (if complex state)
types.ts # Component-specific types (if needed)
Prefer small components that compose over one large component with many config props.
// Good: composable
<Card>
<CardHeader>
<CardTitle>Tasks</CardTitle>
</CardHeader>
<CardBody>
<TaskList tasks={tasks} />
</CardBody>
</Card>
// Avoid: over-configured
<Card
title="Tasks"
headerVariant="large"
bodyPadding="md"
content={<TaskList tasks={tasks} />}
/>
One component, one responsibility. If a component renders both a filter bar and a list and a pagination control, split it.
// Good: does one thing
export function TaskItem({ task, onToggle, onDelete }: TaskItemProps) {
return (
<li className="flex items-center gap-3 p-3">
<Checkbox checked={task.done} onChange={() => onToggle(task.id)} />
<span className={task.done ? "line-through text-muted" : ""}>
{task.title}
</span>
<Button variant="ghost" size="sm" onClick={() => onDelete(task.id)}>
<TrashIcon />
</Button>
</li>
);
}
Containers handle data shape, loading, error, empty. Presentational components take already-resolved props.
// Container: handles data
export function TaskListContainer() {
const { tasks, isLoading, error, refetch } = useTasks();
if (isLoading) return <TaskListSkeleton />;
if (error)
return <ErrorState message="Failed to load tasks" retry={refetch} />;
if (tasks.length === 0) return <EmptyState message="No tasks yet" />;
return <TaskList tasks={tasks} />;
}
// Presentation: handles rendering
export function TaskList({ tasks }: { tasks: Task[] }) {
return (
<ul role="list" className="divide-y">
{tasks.map((task) => (
<TaskItem key={task.id} task={task} />
))}
</ul>
);
}
Pick the simplest tier that satisfies the requirement. Only move up when the current tier can't express the need.
Local state (useState) → Component-specific UI state
Lifted state → Shared between 2-3 sibling components
Context → Theme, auth, locale (read-heavy, write-rare)
URL state (searchParams) → Filters, pagination, shareable UI state
Server state (React Query, SWR) → Remote data with caching
Global store (Zustand, Redux) → Complex client state shared app-wide
Avoid prop drilling deeper than 3 levels. If you are passing props through components that don't use them, introduce context or restructure the tree.
The single highest-leverage author-time check. Every UI draft must be reviewed against the 9-row anti-pattern table before it ships.
Read references/ai-aesthetic-table.md and confirm none of the 9 anti-patterns describe your draft. If one does, apply the production alternative in the same row before moving on.
Typical catches at this stage:
rounded-2xl to every card, button, and input.Use the project's spacing scale. Do not invent off-scale values.
/* Use the scale: 0.25rem increments (or whatever the project uses) */
/* Good */
padding: 1rem; /* 16px */
/* Good */
gap: 0.75rem; /* 12px */
/* Bad */
padding: 13px; /* Not on any scale */
/* Bad */
margin-top: 2.3rem; /* Not on any scale */
Respect the type hierarchy — don't skip levels, don't use heading styles for non-heading content.
h1 → Page title (one per page)
h2 → Section title
h3 → Subsection title
body → Default text
small → Secondary / helper text
text-primary, bg-surface, border-default), not raw hex values.Every component must meet the standards in references/accessibility-checklist.md. That file inlines the full checklist (keyboard, ARIA, focus, forms, images, motion) — do not defer to other skills or external docs.
The five load-bearing defaults:
<button> over <div role="button">. If you must use a custom element, implement Enter + Space + focus ring.aria-label="Close dialog" on an icon-only button).See the full checklist in references/accessibility-checklist.md before marking the pass done.
Design mobile-first, then expand. Test at the four canonical breakpoints: 320px, 768px, 1024px, 1440px.
<div className="
grid grid-cols-1 /* Mobile: single column */
sm:grid-cols-2 /* Small: 2 columns */
lg:grid-cols-3 /* Large: 3 columns */
gap-4
">
At each breakpoint: does the layout reflow without horizontal scroll, overflow, or clipped content? If not, fix before moving on.
function TaskListSkeleton() {
return (
<div className="space-y-3" aria-busy="true" aria-label="Loading tasks">
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} className="h-12 bg-muted animate-pulse rounded" />
))}
</div>
);
}
When a mutation has a clearly predictable outcome (toggle, like, delete), apply the change immediately and roll back on error.
function useToggleTask() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleTask,
onMutate: async (taskId) => {
await queryClient.cancelQueries({ queryKey: ["tasks"] });
const previous = queryClient.getQueryData(["tasks"]);
queryClient.setQueryData(["tasks"], (old: Task[]) =>
old.map((t) => (t.id === taskId ? { ...t, done: !t.done } : t)),
);
return { previous };
},
onError: (_err, _taskId, context) => {
queryClient.setQueryData(["tasks"], context?.previous);
},
});
}
Before marking a UI slice done, confirm every box:
SIMPLICITY CHECK emitted and honored.NOTICED BUT NOT TOUCHING observations silently fixed.If any box is unchecked, the slice is not done. Fix the gap or split the slice.
kramme:siw:generate-phases) where a phase involves UI work.kramme:code:incremental — each UI slice obeys the six-rule increment loop; this skill adds the UI-specific passes on top.kramme:code:source-driven when the UI slice depends on framework-specific APIs, component-library behavior, or recently changed browser/runtime semantics. This skill owns UI structure, accessibility, and visual defaults; source-driven owns official-doc grounding and citation.kramme:pr:ux-review, the kramme:a11y-auditor agent, and the kramme:deslop-reviewer agent all check, post-hoc, for violations of the same defaults this skill prevents. If a reviewer flags one of the 9 AI-aesthetic patterns, the canonical fix lives in this skill's anti-pattern table.These are the lies you will tell yourself to justify shipping AI-default UI. Each one has a correct response:
rounded-2xl won't hurt." → Consistent radius per surface tier is the point. Pick intentionally per tier.If you notice any of these in your draft, stop and re-author:
any typing on props or event handlers.Before declaring the UI slice done, self-check:
kramme:pr:ux-review or the kramme:deslop-reviewer agent flag any row of the anti-AI-aesthetic table?If any answer is no, finish the gap before declaring done.
tools
Requires Linear MCP. Implements one Linear issue end to end, selects applicable code-review, convention, and PR-refactor gates, runs them to bounded convergence, verifies, and optionally opens the PR and iterates on CI and review feedback until green. Use when the user wants a single Linear issue taken from implementation through a clean Pull Request. Not for implementation-only work, SIW-tracked issues, stacked PRs, existing PR updates, or post-merge rollout.
development
Reviews PR and local changes for convention drift and overcaution against documented rules and mined peer-file practice. Use for new patterns, dependencies, abstractions, or defensive complexity that departs from established practice; every finding cites evidence. Supports --inline. Not for general code quality (use kramme:pr:code-review) or spec review (use kramme:siw:spec-audit --team).
testing
Charts huge or foggy initiatives into a local `.context` decision map and resolves one typed frontier ticket per session until the work is ready for SIW or another execution workflow. Use when the route to a destination cannot fit in one agent session or parallel workspaces need coordinated planning state. Not for clear specs, ordinary issue decomposition, implementation, or Linear-native tracking.
development
Investigates a question against primary sources and saves one cited Markdown artifact. Use for reading legwork: official docs/API facts, source-code or spec checks, standards, and first-party service behavior before planning or implementation. Not for making product or architecture decisions, implementing code, broad web search, secondary blog summaries, or uncited answers.