ui-audit
Skill2 starsChanged 41 days agoMITView on GitHub
Run technical UI quality checks across accessibility, performance, theming, responsive design, interaction states, and anti-patterns. Generates a scored report with severity ratings and actionable fixes. Covers Vue 3 + Vuetify + Inertia.js and Flutter. Invoke only when the user types /ui-audit or asks for a UI/design audit, or when /ship-check's UI step calls it on a UI diff — never on your own initiative mid-task.
We split this skill into the rules it gives an agent
14 rules. 0 are common, 5 are distinctive, and 9 are very specific to this project.
0
Common
Most of the 10 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 8
Run systematic technical quality checks on frontend code and generate a comprehensive report.
- Distinctive: line 32
Run checks across 7 dimensions.
- Very specific: line 44
Focus management: After route change or dialog close, is focus returned properly?
- Very specific: line 76
transition: all: Must specify exact properties (transition: transform 200ms ease-out) - Very specific: line 80
ease-inon entering elements: Should be ease-out (ease-in feels sluggish) - Very specific: line 81
scale(0)entry animations: Should start from scale(0.95) with opacity: 0 - Distinctive: line 83
Keyframes on rapidly-triggered elements: Should use CSS transitions for interruptibility
- Very specific: line 90
Reduced motion ignored: no check of MediaQuery.disableAnimationsOf(context).
- Very specific: line 121
Ad-hoc
BoxShadow: instead of token shadows; inconsistent light direction across cards. - Very specific: line 142
Look for a form column with no SingleChildScrollView / resizeToAvoidBottomInset handling.
- Distinctive: line 264
Missing polish: No favicon, no meta tags, no 404 page, no skip-to-content link, no form validation
- Very specific: line 266
Flat surfaces: No texture, no depth, generic box-shadow with pure black, inconsistent light direction
- Distinctive: line 279
The test: If someone said "AI made this," would they believe it immediately?
- Very specific: line 288
Test coverage (that's what /review covers)
“Common” is measured: kept by at least 6 in 10 of the 10 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
UI Audit
Run systematic technical quality checks on frontend code and generate a comprehensive report. Don't fix issues — document them for the developer to address.
This is a code-level audit, not a design critique. Check what's measurable and verifiable in the implementation.
How to Use
Invoke with an optional target: /ui-audit [page, component, or feature]
Without a target, audit the most recently changed frontend files.
Detect the stack first. Each dimension below lists checks for Vue 3 / Vuetify and for
Flutter. Run only the set that matches — reporting a missing :focus-visible on a Dart file
wastes the reader's attention and discredits the findings next to it. The dimensions and
scoring are identical either way; only the mechanics differ.
Scope — one page, not the repo
This audits a target: a page, component, or feature. If the question is whether the design
system is adopted across the whole codebase — token ratios, a type scale that was never
built, two competing button kits — that's design-drift, which reads the whole tree. A
per-page audit is structurally blind to duplication and absence.
Diagnostic Scan
Run checks across 7 dimensions. Score each 0-4 (0=critical failures, 4=excellent).
1. Accessibility (A11y)
Vuetify handles baseline a11y (ARIA roles on components, keyboard nav on v-tabs/v-menu/v-dialog). Focus on what Vuetify does NOT handle:
Check for:
- Contrast on custom elements: Text/icons outside Vuetify components — do they meet 4.5:1 (body) / 3:1 (large text, UI components)?
- Missing labels: Custom inputs without
<label>, icon-onlyv-btnwithoutaria-label - Images without alt:
<img>tags orv-imgwithout meaningfulalt(decorative images should havealt="") - Heading hierarchy: Skipped heading levels (
h1→h3), or noh1on the page - Color-only indicators: Status shown only by color (needs icon or text too)
- Focus management: After route change or dialog close, is focus returned properly? Custom interactive elements without
:focus-visiblestyles? - Touch targets: Custom clickable elements < 44x44px (Vuetify buttons handle this; check custom elements)
Flutter — check for:
- Missing
Semantics: Custom tappable widgets (GestureDetector, bareInkWell) with noSemantics(label:, button: true). Unlike HTML there is no implicit role — a tappableContaineris invisible to TalkBack/VoiceOver unless you say otherwise. - Icon-only buttons without
tooltip:— onIconButtonthe tooltip doubles as the semantic label, so omitting it costs both affordance and accessibility. - Decorative widgets not excluded: images/icons that repeat adjacent text should be wrapped
in
ExcludeSemantics, and compound rows merged withMergeSemanticsso they read as one node. - Contrast on custom surfaces — same WCAG thresholds, no devtools; compute from the token values rather than eyeballing a screenshot.
- Text scaling ignored: hardcoded
height/widthon text containers that clip underMediaQuery.textScalerOf(context). OS font scaling on mobile is far more aggressive than browser zoom, and this is the most common real a11y break in a Flutter app. - Colour-only status — same rule; a coloured dot needs an icon, label, or shape too.
- Touch targets < 48dp: check
minimumSize/MaterialTapTargetSizeon custom buttons.
Score: 0=Inaccessible (fails WCAG A), 1=Major gaps, 2=Partial (some effort, significant gaps), 3=Good (WCAG AA mostly met), 4=Excellent (WCAG AA fully met)
2. Animation & Motion
Consult the animate skill for principles — its decision map holds the laws (frequency budget, tone rule, enter-slow-exit-fast, reduced-motion) and its web recipes hold the fixes. ui-polish no longer carries motion guidance.
To check the items below mechanically rather than by reading code, run the animate skill's harness against the page — it reports layout-triggering properties, missing reduced-motion cues, and non-interruptible motion directly:
node ~/.claude/skills/animate/scripts/verify-motion.mjs --url <page> --selector <css> --trigger <css> --pw <playwright dir>Check for:
transition: all: Must specify exact properties (transition: transform 200ms ease-out)- Animating layout properties:
width,height,padding,margin,top,leftinstead oftransform/opacity - Durations > 300ms on UI feedback (buttons, menus, tooltips)
- Missing
prefers-reduced-motion: Any animation without a reduced-motion alternative ease-inon entering elements: Should beease-out(ease-in feels sluggish)scale(0)entry animations: Should start fromscale(0.95)withopacity: 0- Hover animations without
@media (hover: hover): Touch devices trigger hover on tap - Keyframes on rapidly-triggered elements: Should use CSS transitions for interruptibility
- Vuetify default transitions left unmodified where polish matters
Flutter — check for: (recipes live in the animate skill's flutter reference.
The verify-motion.mjs harness above is Playwright-based and does not apply here — check by reading.)
AnimationControllernot disposed indispose()— a leak, and the most common Flutter motion bug.Curves.easeInon entering elements — should beeaseOut/easeOutCubic; same law as web.- Reduced motion ignored: no check of
MediaQuery.disableAnimationsOf(context). - Implicit where explicit is needed (or the reverse):
AnimatedContainerfor a one-shot entrance is fine; for interruptible, gesture-driven motion you need a controller. - Animating layout: rebuilding a subtree every frame instead of using
AnimatedBuilder'schildparameter to hoist the static part out of the animation. - Durations > 300ms on UI feedback — identical budget.
Opacityin an animation — preferFadeTransition/AnimatedOpacity; the plain widget forces asaveLayerevery frame.
Score: 0=Broken/janky animations, 1=Major issues (layout property animations, no reduced motion), 2=Partial, 3=Good (mostly correct, minor issues), 4=Excellent (polished, accessible, performant)
3. Theming & Design Tokens
Check for:
- Hard-coded colors: Hex/RGB/HSL values in templates or
<style>instead of Vuetify theme variables or CSS custom properties - Hard-coded spacing: Pixel values instead of Vuetify spacing utilities (
pa-4,ma-2,ga-3) - Inconsistent elevation: Mix of custom
box-shadowand Vuetifyelevation-* - Dark mode issues: Elements that don't update on theme switch, poor contrast in dark mode
- VTooltip contrast: Missing
bg-surface text-on-surfaceclasses - Typography bypass: Raw CSS font sizes instead of Vuetify's
text-h6,text-body-1, etc.
Flutter — check for:
- Hard-coded colours:
Color(0xFF…)/Colors.blueinstead of the app'sThemeExtensionorTokensclass. DiscountColors.white/black/transparenton overlays — those are usually legitimate, and flagging them buries the real findings. - Static const colour constants that bypass the runtime theme entirely. Worse than a stray literal: they hardcode one mode, so the screen silently ignores light/dark and accent switching. Always state that consequence, not just the count.
- Hard-coded spacing: bare numbers in
EdgeInsets/SizedBoxinstead of the spacing scale. - Typography bypass:
TextStyle(fontSize: N)instead of the app's type scale. If no scale exists at all, that's adesign-driftfinding, not a per-page one — say so and move on. - Ad-hoc
BoxShadowinstead of token shadows; inconsistent light direction across cards. - Theme switch: does every surface on this page actually change? Screens built on static constants look correct in the default mode and wrong in the other one.
Score: 0=No theming (hard-coded everything), 1=Minimal tokens, 2=Partial (tokens exist but inconsistent), 3=Good (tokens used, minor hard-coded values), 4=Excellent (full token system, dark mode works)
4. Responsive Design
Consult responsive-design reference.
Check for:
- Fixed widths: Hard-coded
widthvalues that break on mobile - Missing responsive cols:
v-colwithout responsive breakpoint props (cols="12" md="6") - Touch targets: Interactive elements < 44x44px on mobile
- Horizontal overflow: Content that causes horizontal scroll on narrow viewports
- Navigation: Does the layout adapt? Is
v-navigation-drawerset totemporaryon mobile? - Tables:
v-data-tablewithout mobile consideration - Hover-dependent functionality: Features that only work with hover (breaks on touch)
Flutter — check for:
- Keyboard overflow: the classic
RenderFlex overflowedwhen the soft keyboard opens. Look for a form column with noSingleChildScrollView/resizeToAvoidBottomInsethandling. This is the single most common responsive defect in a Flutter form. - Missing
SafeArea: content under the notch, status bar, or home indicator. - Text scaling: fixed-height containers around text that clip at large
textScalervalues. - Fixed pixel widths that assume a phone; no
LayoutBuilder/MediaQuery.sizeOfswitch for tablet, when the app ships tablet layouts. MediaQuery.of(context).sizewhereMediaQuery.sizeOf(context)would do — the former rebuilds on every metric change, including keyboard open.- Orientation: no
OrientationBuilderon screens that clearly need one. - Hover-only affordances — on a touch build hover never fires, so anything hidden behind it is unreachable.
Score: 0=Desktop-only, 1=Major breakage on mobile, 2=Partial (mostly works, gaps), 3=Good (responsive, minor issues), 4=Excellent (mobile-first, adapts to input method)
5. Interaction States
Consult interaction-design reference.
Check for:
- Missing loading states: API calls without skeleton/spinner feedback
- Missing error states: Forms or data fetching without error handling UI
- Missing empty states: Lists/tables that show nothing when empty
- Disabled without visual cue: Interactive elements disabled via JS but not visually
- No optimistic updates where appropriate (low-stakes actions like toggles)
- Confirmation dialogs where undo would work better
- Missing hover/active feedback on custom clickable elements
Flutter — check for:
- Disabled faked with an empty closure:
onPressed: () {}looks enabled and does nothing.onPressed: nullis the disabled state and styles itself correctly. FutureBuilder/StreamBuilderwith nohasErrorbranch — the snapshot's error case silently renders an empty or stuck-loading UI.- Missing empty states on
ListView.builderwhenitemCountis 0. GestureDetectorwith no visual feedback — useInkWellfor ripple, or supply your own pressed state; a tap that looks identical to no-tap reads as broken.- Splash/highlight suppressed (
splashColor: Colors.transparent) with nothing replacing it. - No focus affordance on custom widgets — Flutter draws none by default, so keyboard and D-pad users get nothing unless you build it.
Score: 0=No state handling, 1=Major gaps (no loading or error states), 2=Partial, 3=Good (most states covered), 4=Excellent (all 8 states designed for all interactive elements)
6. Performance
Check for:
- Reactive overhead in animations:
ref()updated on every pixel during drag/scroll (should use template refs + direct DOM) - Missing lazy loading: Images without
loading="lazy", heavy components not wrapped indefineAsyncComponent - Unnecessary watchers:
watchwithdeep: trueon large objects - v-for without v-memo: Large lists that re-render entirely on unrelated state changes
- Expensive computed in templates: Complex calculations inline instead of cached
computed - Missing
will-changeon animating elements (or worse,will-changeon everything) - Layout thrashing: Reading + writing layout properties in loops
Flutter — check for:
- Missing
constconstructors on static subtrees. This is the highest-leverage perf lever in Flutter — aconstwidget is skipped entirely on rebuild. Look forconst-able widgets that aren't marked. setStateat the wrong altitude: calling it on a screen-levelStateto update one chip rebuilds the whole tree. Push state down to the leaf, or use a scoped listenable.- Work inside
build(): sorting, filtering, date formatting, orRegExpconstruction that runs on every frame.build()can be called many times per second. ListView(children: [...])for long lists — builds every child eagerly;ListView.builderis lazy. Same forColumninsideSingleChildScrollViewon long content.- Controllers/listeners not disposed (
TextEditingController,ScrollController,AnimationController, stream subscriptions) — leaks that compound across navigation. - Images without
cacheWidth/cacheHeight— decoding a 4000px asset into a 100px avatar costs memory proportional to the source, not the display size. MediaQuery.of(context)for one property — rebuilds on every metric change; the scopedsizeOf/paddingOf/textScalerOfaccessors don't.Opacity/ClipRRectin hot paths — both can force asaveLayer.
Score: 0=Severe issues, 1=Major problems, 2=Partial, 3=Good, 4=Excellent
Report Format
Output a summary table, then detail each finding:
## UI Audit Report: [Target]
| Dimension | Score | Issues |
|-----------|-------|--------|
| Accessibility | 3/4 | 2 issues |
| Animation & Motion | 2/4 | 4 issues |
| Theming & Tokens | 3/4 | 1 issue |
| Responsive | 4/4 | 0 issues |
| Interaction States | 2/4 | 3 issues |
| Performance | 3/4 | 1 issue |
| AI Slop & Design | 3/4 | 1 issue |
| **Overall** | **20/28** | **12 issues** |
### Findings
#### [P0] Critical — [Category]
**File**: `path/to/file.vue:42` (or `.dart:42`)
**Issue**: [Description]
**Fix**: [Actionable fix]
#### [P1] Major — [Category]
...Severity Levels
| Level | Meaning | Examples |
| P0 | Broken/inaccessible | No keyboard nav, broken on mobile, fails WCAG A |
| P1 | Major UX problem | No loading state, layout property animations, no error handling |
| P2 | Quality gap | Hard-coded colors, untouched framework defaults, missing hover/press states |
| P3 | Polish opportunity | Could add stagger animation, tooltip delay grouping, reduced motion |
7. AI Slop & Design Quality
Consult anti-patterns reference.
Does this look like generic AI-generated UI? Check for the telltale signs:
Check for:
- Generic color choices: Purple/blue AI gradients, pure black
#000, oversaturated accents, mixing warm/cool grays - Default fonts: Inter, Roboto, Open Sans used without intentional reason
- Lazy layout: Three equal card columns, everything centered, no max-width container, uniform border-radius everywhere
- Card overuse: Cards wrapping everything, nested cards, cards where spacing alone would work
- Content tells: Generic placeholder names ("John Doe"), round numbers (
99.99%), AI copywriting cliches ("Elevate", "Seamless"), Lorem Ipsum - Missing polish: No favicon, no meta tags, no 404 page, no skip-to-content link, no form validation
- Component cliches: Pill badges everywhere, accordion FAQ, modals for everything, footer link farm with 4 columns
- Flat surfaces: No texture, no depth, generic
box-shadowwith pure black, inconsistent light direction
Flutter tells specifically:
- Untouched Material defaults: stock
ThemeData()indigo/purple, a defaultAppBarwith default elevation,Cardwith default radius and shadow — the "I ranflutter createand kept going" look. Cardwrapping everything, including things that only needed spacing.- Default
FloatingActionButtonplaced because the template had one, not because the screen has a primary action. - Material 3 defaults unchanged — stock
ColorScheme.fromSeed()output, which has a recognisable pastel cast. - Stock
Icons.everywhere when the design language calls for a specific icon set.
The test: If someone said "AI made this," would they believe it immediately? If yes, that's a P2.
Score: 0=Obvious AI output, 1=Many generic patterns, 2=Some tells remain, 3=Distinctive with minor tells, 4=Indistinguishable from human-designed
What NOT to Audit
- Backend code, API design, database queries
- Business logic correctness
- Code style or formatting (that's what Pint/ESLint/
dart formatandflutter analyzeare for) - Test coverage (that's what
/reviewcovers) - Whole-repo design-system health — token ratios, missing scales, duplicate component kits
(that's
/design-drift)
This audit is purely about the user-facing quality of the frontend implementation.
Other versions12 projects use a version of this.
Word for word · 1
- AhmedMerza/claude-skills★ 2100% same
Edited · 0
None found.
Wrote their own version · 11
- bagofwords1/bagofwords★ 463same name
- rustfs/console★ 205same name
- nahid-sparktales/agent-dispatcher★ 59same name
- HK-hub/AgentSkills★ 6same name
- GlamgarOnDiscord/claude-saas-blueprint★ 2same name
- LiriRaid/waymark★ 2same name
- chieaid24/skills★ 0same name
- idimsh/tdds-business-skills★ 0same name
- jfkconstruct/claude-skills★ 0same name
- lohi-ai/babysit★ 0same name
1 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository53 other files it gives its agents.
- animateSkill
- api-docs-completeSkill
- best-optionSkill
- browseSkill
- changelog-generateSkill
- checkpointSkill
- claimSkill
- commitSkill
- courtSkill
- design-driftSkill
- designSkill
- expandSkill
- explainSkill
- fix-reviewSkill
- grill-meSkill
- handover-listSkill
- handover-resumeSkill
- handover-saveSkill
- i18n-syncSkill
- issueSkill
- leadSkill
- mr-approveSkill
- mr-assignSkill
- mr-createSkill
- mr-guideSkill
- mr-listSkill
- mr-mergeSkill
- mr-review-requestSkill
- mr-reviewSkill
- mr-statusSkill
- persona-walkthroughSkill
- ponytailSkill
- prove-the-testSkill
- qa-crawlSkill
- qa-sweepSkill
- reflectSkill
- reviewSkill
- root-causeSkill
- scoutSkill
- second-opinionSkill
- ship-checkSkill
- skill-auditSkill
- skill-compareSkill
- spinoffSkill
- story-siteSkill
- tool-compareSkill
- ui-polishSkill
- usage-weekSkill
- ux-auditSkill
- validate-planSkill
- video-teardownSkill
- wait-whatSkill
- where-were-weSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

