Sets the visual direction for UI work, avoiding generic AI aesthetics. Use for any user-facing UI change: new surfaces, screenshot-driven feedback, copy/density cleanup, settings, control placement, or a "make this look good" pass. Do not load it only for purely mechanical wiring or formatting.
64
80%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.agents/skills/frontend-design/SKILL.mdThis skill guides creation of product-specific, production-grade interfaces. Distinctive means the product has a clear point of view, not that the screen is busy. Implement real working code with strong product judgment and excellent accessibility.
The user may ask for a component, page, full app, dashboard, marketing surface, or restyle. Before coding, understand the audience and pick a direction that fits the product instead of defaulting to generic SaaS polish.
Before coding, decide:
operate, read,
persuade, or experience?Write that contract in one line before coding:
<mode> / <user> / <job> / <next action>.
If the surface has two primary jobs, split the route, step, or state. Do not solve an information-architecture problem with more cards, tabs, helper copy, or buttons. Then implement the smallest cohesive, accessible, responsive surface that proves the contract.
The default state is a calm starting point, not a catalog of everything the feature can do. It should show only:
On actionable surfaces, use one primary action and at most two visible secondary actions. Put rare, advanced, destructive, diagnostic, history, and provider controls in a menu, disclosure, focused dialog, or later step. If a task has more than four visible choices, group or stage them before styling it. If setup, results, feedback, history, and advanced configuration all appear together, the information architecture is wrong - separate the states.
Prefer one visible input group for one job. If several fields are genuinely required, keep them in one focused form or stage them; do not expose separate setup, feedback, and configuration forms on the same default surface.
Every visible element must earn one of four jobs: orient, show work/state, provide required input, or enable the next decision. Remove or disclose anything else. More visible UI is not more capability; routes, actions, menus, keyboard commands, and the agent surface can preserve capability without competing in the first viewport.
Do not add a new input, button, icon, badge, card, or sentence merely because a capability exists. First ask whether an existing row action, menu, composer, agent handoff, or progressive disclosure already owns it. Default to no icon when text is clear; use an icon only when it adds recognition or saves space.
When a screenshot feels busy, delete or defer elements before changing color, radius, shadow, or typography. Whitespace is a valid outcome. A focused tool is better than a miniature control panel.
For a new app or an explicit visual redesign, define its product mode, audience,
visual world, palette family, type treatment, composition, shape language, and
anti-references in DESIGN.md. Read references/visual-direction.md for the
vocabulary. For a routine screen or cleanup, do not invent a mood-board
exercise: inspect the incumbent tokens and components, choose one fitting
direction, and get back to the user's job.
Preserve an existing brand system and component library. When no brand exists, choose a deliberate direction based on the domain and compare sibling apps before selecting its accent family. Shared behavior and semantic token names stay consistent; visual variation is a reasoned product choice, not a demand to make every screen novel.
Use the Nielsen response-time limits, Google RAIL, and the Doherty threshold as product defaults:
This is the most repeated correction in this repo, tracked as text-heavy-ui in
node scripts/agent-friction-report.mjs. Every item below has been asked for by
name more than once, usually right after a previous surface was corrected for the
same thing. So treat the list as the default shape you apply, not a tradeoff you
weigh per surface. If the user wants one of these, they will ask.
Density comes from useful data, not from chrome. A table, inbox, canvas, or editor may be information-dense while its controls stay quiet. Never translate "operational" or "dense" into more cards, buttons, icons, inputs, or visible configuration. If the user came to complete one task, a focused surface with empty space is the correct default.
Keep on-screen text and badging minimal: show the shortest label needed for the next action, remove decorative or duplicate status chips, and put context in a tooltip or progressive disclosure when it is not needed to decide.
Do not add, unless asked:
The explanation goes in a tooltip, a Manage popover, a menu, or nowhere.
Settings use one shape: a compact row with the label and its current state, one
action on the right, and Manage revealing the form once something is
configured. Do not spell every input out on the default surface.
Chrome is not content. These rules govern strings you write into JSX. A
description the user typed and the app stored is data — render it, and render
nothing when it is empty. Never add a || "No description yet." fallback; an
empty field is empty, not an opening to explain the feature.
For genuinely sequential setup, prefer one decision per step, a clear progress cue, smart defaults, and advanced detail behind a labeled disclosure. Use a full-screen takeover only when the task deserves uninterrupted focus; use a dialog or inline row for a small task. Do not copy one approved flow onto every surface. Choose the smallest composition that matches the user's actual job.
templates/forms/ is a reference for quiet settings: no repeated page title,
breadcrumbs, eyebrows, card descriptions, or form fields before the user asks
to create or manage something. User-authored field content is the exception.
Before shipping, verify collapsed, expanded, loading, empty, error, and
narrow-width states. The default state fails review when its first viewport
carries explanatory paragraphs, several unrelated forms, or controls for another
task. guard:no-default-chrome checks the structural half of this on lines your
branch adds; it cannot see a sentence you wrote, so the list above is still
yours to apply.
transition-all — list the properties that actually change (e.g. transition-[opacity,transform]). Use the shared easing tokens defined in packages/core/src/styles/agent-native.css instead of hand-typing curves: var(--ease-drawer) (260ms, drawers/app chrome), var(--ease-collapse) (200ms, expand/collapse), var(--ease-out-strong) (snappy entrances) — in Tailwind, ease-[var(--ease-collapse)]. Enter/exit with ease-out, never ease-in. Overlays that zoom in must set the Radix origin var (e.g. origin-[--radix-popover-content-transform-origin]). Animate transform/opacity, not width/height/padding/box-shadow. Gate looping or large-movement animations with motion-reduce:. Command palettes and keyboard-triggered actions get no animation.Avoid convergence by choosing with purpose. A named direction is useful for a new or explicitly redesigned surface, not as ceremony for every row or dialog. Pair a reference with the reason it fits, then commit to one direction. Only deal multiple visual worlds when the brief is genuinely open and the choice is worth the cost. Never average several directions into extra decoration.
@tabler/icons-react, but an app may register a different
company design system in app/design-system.ts.app/design-system.ts, ToolkitProvider, and the local UI adapter directory
before choosing a primitive. Use shadcn primitives when they are the active
adapter; use the registered company components when they are not.shadcn-ui if it exists. That skill covers components.json, CLI docs, component composition, theming, and registry workflows.app/components/ui/ before importing a shadcn component. If a primitive is missing, add it from the app root with pnpm dlx shadcn@latest add <component>, then review the generated file.@/components/ui/*. Never import
@agent-native/toolkit/ui/* directly in app product code.@agent-native/toolkit/design-system. Their props express intent, emphasis,
size, controlled values, and behavior; they do not require Tailwind, CVA, or
className.window.alert, window.confirm, window.prompt). Use AlertDialog, Dialog, or app-specific confirmation UI.IconPartyPopper or IconConfettiCannon feel
plausible but don't exist and crash Vite with a "Named export not found" error. Before importing
an icon you haven't used elsewhere in this app, confirm it exists by grepping
node_modules/@tabler/icons-react/dist/tabler-icons-react.d.ts (or the package's icon list)
for the exact name, and pick the closest real match if your first guess isn't there.size-3 (12px) or smaller than the adjacent text. Preserve a larger hit area on the trigger, not the glyph. Use guard:allow-large-help-icon only for deliberate heading documentation or menu action exceptions. Do not add help glyphs to every row; use one contextual explanation where it is needed.useActionQuery and useActionMutation from @agent-native/core/client for action-backed UI. Standard CRUD should go through actions, not custom /api/ routes./automations, /block, or
/workflow. Preserve the starter's full-page chat route (/ or /chat/*)
when it exists; do not replace it with a domain form while leaving the shell
configured as if it were chat.AgentSidebar for contextual AI when the app has one
and the task benefits from judgment. A button that sends work to
sendToAgentChat must open or focus that sidebar and leave the user on the
current domain surface. Do not add a second prompt box or a sidebar merely to
make AI visible. Use full-page chat for chat-first work, not as a hidden
transport for a domain button.sendToAgentChat with
bounded context, openSidebar: true, and the intended submit mode. Prefer
one contextual handoff over a row of AI buttons. A deterministic local action
is useful, but label it local, preview, or analyze rather than implying it
invoked an agent.Before shipping a new app or a substantial redesign, review the surface as an operator would use it repeatedly:
variant, size) before overriding classes.bg-background, text-muted-foreground, border-border, bg-primary) instead of raw Tailwind colors for app chrome and reusable components.gap-* in flex/grid layouts instead of space-x-* or space-y-*.size-* when width and height are equal, and truncate instead of spelling out overflow/ellipsis/nowrap.cn() from the local utils alias for conditional classes.sr-only only when the visible design already communicates the title.SelectGroup, DropdownMenuGroup, CommandGroup, and equivalents.CardHeader, CardTitle, CardContent, and CardFooter. Do not add CardDescription, and do not hand-roll a muted <p> under a CardTitle — a card gets a title or a description, never both.ToggleGroup for small option sets, Switch for binary settings, Checkbox for multi-select, RadioGroup for one-of-many, and Slider/inputs for numeric values.Field, FieldGroup, or InputGroup primitives are installed or appropriate to add, use them instead of raw layout divs.Skeleton geometry. Do
not show generic "Loading..." text for content loads; reserve Spinner for
brief mutations, uploads, and progress actions. Use the app's existing
loading primitive when it is a genuine design-system adapter. Empty states
should communicate the state and offer the appropriate next step or small set
of choices.Avoid patterns that add visual noise, obscure state, or compete with the user's task without a clear product reason:
Match verification effort to the size of the change. For one component, one
form, one page, or a restyle, run the app's existing checks — formatter,
pnpm typecheck, existing tests — and stop there.
Escalate to browser verification only when the user asks for it, or when the change is a multi-step user-visible flow that cannot be confirmed any other way. Never author a new Playwright/Puppeteer script, add a browser-automation dependency, or stand up an e2e harness to check work the user did not ask you to test that way; use an available browser tool, or say what you could not verify.
For substantial frontend work:
@agent-native/toolkit/conformance, including mixed-overlay focus,
portalContainer, and z-index stacking checks.DESIGN.md against the rendered
surface and run the anti-slop audit in references/visual-direction.md.useActionQuery/useActionMutation hooks for frontend data fetchinga941a2e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.