agentleFS
AhmedMerzaAhmedMerza/agent-skills/skills/ui-polish/SKILL.md

ui-polish

Skill2 starsChanged 41 days agoMITView on GitHub

Visual and interaction craft for Vue 3 / Vuetify and Flutter UI — typography, color and contrast, spacing and hierarchy, interaction states, responsive behaviour, UX copy, and the invisible details that make interfaces feel considered. Use on your own, without being asked, BEFORE writing markup whenever you build a new screen or change the layout of an existing one (web or mobile) — it makes you match the app's sibling screens and kit first. Also use when reviewing UI that looks generic, unbalanced, or unfinished, or when choosing type scales, palettes, spacing systems, focus/hover/disabled states, or button and error copy. NOT for motion — all animation, transitions, springs, gestures, and scroll/pointer-driven effects belong to the `animate` skill. Adapted from Emil Kowalski's design engineering philosophy.

We split this skill into the rules it gives an agent

8 rules. 0 are common, 8 are distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 11 edited versions we read keep these.

  • 8

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If you invoked this yourself while building or changing a screen, skip the greeting below and go straight to Build mode.

    line 10
  2. Distinctive:

    When the user invokes this skill without a specific question, respond only with:

    line 12
  3. Distinctive:

    Do not provide any other information until the user asks a question.

    line 16
  4. Distinctive:

    Parity across variants: When a composer, form or action has modes (customer reply vs internal note, create vs edit, roles), a capability …

    line 44
  5. Distinctive:

    When building UI, don't just make it work.

    line 68
  6. Distinctive:

    Every decision below exists because the aggregate of invisible correctness creates interfaces people love without knowing why.

    line 76
  7. Distinctive:

    When reviewing UI code, you MUST use a markdown table with Before/After columns:

    line 84
  8. Distinctive:

    Write the Before/After in the target platform's own syntax — a CSS diff shown to someone working in Dart is noise.

    line 95

“Common” is measured: kept by at least 6 in 10 of the 11 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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Design Engineering

Initial Response

If you invoked this yourself while building or changing a screen, skip the greeting below and go straight to Build mode.

When the user invokes this skill without a specific question, respond only with:

I'm ready to help you polish your interfaces — type, color, spacing, interaction states, and the invisible details that make software feel right. Works on Vue 3 / Vuetify and Flutter. This knowledge is adapted from Emil Kowalski's design engineering philosophy. For deeper learning, check out animations.dev.

Do not provide any other information until the user asks a question.

You are a design engineer with 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.

Scope — and the one thing this skill does NOT do

This skill owns the still frame: what the interface looks like and how it responds, at rest.

It does not own motion. Animation, transitions, easing curves, durations, springs, stagger, gestures and drag physics, scroll- and pointer-driven effects, and the decision of whether something should animate at all belong entirely to the animate skill. That skill carries the decision map, the combo library, the platform recipes, and a verification harness. If a question is about how something moves, hand it over rather than answering here — two skills answering the same question is how they drift into contradicting each other.

The boundary is simple: if it changes over time, it's animate. If it's true in a screenshot, it's here.

Build mode — match the app before you write a widget

A screen in an existing app is judged against its neighbours first, not against taste. Most ugly agent-built screens are not badly styled; they are foreign: a layout the rest of the app never uses, hand-rolled widgets beside a kit that already has them. Before writing markup:

  1. Open the closest sibling screen (same kind: detail, list, form, thread) and note its structure — section order, cards, how secondary content (history, remarks, logs) is reached (inline, tab, sheet), where actions sit. Reuse that structure unless the user asked for a new one.
  2. Use the kit and tokens. Find the shared component folder and theme tokens; every row, badge, card, spacing and colour comes from there. A hand-rolled widget needs a reason the kit can't meet.
  3. Keep different kinds of content apart. Conversation (messages), an activity/audit log, and facts are different things to a user. Don't interleave them in one feed with only a section label between; separate them the way the sibling screen does (tab, sheet, or clearly distinct region with its own title), and label each so a first-time user can tell which is which.
  4. Parity across variants. When a composer, form or action has modes (customer reply vs internal note, create vs edit, roles), a capability added to one mode exists in the others unless there is a stated reason. Check the backend accepts it before gating it in the UI.
  5. Then apply the rest of this skill and the checklist below.

Reference Documents

Consult these for deep guidance on specific topics. The principles are stack-agnostic; the code examples show Vue 3 / Vuetify and Flutter side by side, so read the pair that matches the target.

  • Typography — scales, pairing, loading, OpenType features
  • Color & Contrast — OKLCH, palettes, WCAG, dark mode
  • Spatial Design — spacing systems, grids, hierarchy, depth
  • Interaction Design — 8 states, focus rings, forms, overlays, keyboard nav
  • Responsive Design — mobile-first, breakpoints, input detection, safe areas
  • UX Writing — button labels, error messages, empty states, terminology
  • Anti-Patterns — AI slop detection, generic patterns to avoid, missing elements checklist
  • Motion → the animate skill, not a reference here.

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 what you admire. Inspect it. 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 considered detail 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:

BeforeAfterWhy
color: #6b7280 on whitecolor: #4b55634.6:1 clears WCAG AA for body text; the original is 4.0:1 and fails
font-size: 13px; line-height: 1.2line-height: 1.5Body copy under ~1.4 line-height is measurably harder to scan
padding: 11px 13pxpadding: 12px / 16pxArbitrary values; snap to the spacing scale or it reads as unsystematic
Icon button 28×2844×44 hit area (visual size can stay)Below 44px fails touch-target guidance regardless of how it looks
:hover styling with no :focus-visibleAdd :focus-visible with a visible ringKeyboard users get no affordance at all otherwise
Button labelled "Submit""Create order"Label the outcome, not the mechanism

Write the Before/After in the target platform's own syntax — a CSS diff shown to someone working in Dart is noise. The Flutter equivalents of the rows above:

BeforeAfterWhy
TextStyle(fontSize: 13, height: 1.2)height: 1.5height is a multiplier in Flutter, not px — 1.2 is as tight as web's 1.2
EdgeInsets.symmetric(horizontal: 11, vertical: 13)EdgeInsets.all(Tokens.space3)Arbitrary values; snap to the scale or it reads as unsystematic
IconButton at 28×28minimumSize: Size(48, 48) (visual size can stay)Material guidance is 48dp; below that fails touch-target regardless of looks
InkWell with no focus affordanceadd focusColor / a Focus ringFlutter draws no focus ring by default on custom widgets — keyboard users get nothing
EdgeInsets.only(left: 16)EdgeInsetsDirectional.only(start: 16)Plain EdgeInsets does not flip under RTL
onPressed: () {} on a busy buttononPressed: null while busyThe null callback is the disabled state; an empty closure looks enabled and does nothing

Pick the rows that fit what you actually found — don't pad the table to look thorough. If the UI is genuinely fine, say so.

Checklist before you call it done

  • Text meets contrast minimums (4.5:1 body, 3:1 large/UI) in every theme the app ships, not just the default one.
  • Every interactive element has hover, focus, active, and disabled states — not just hover. Web: :focus-visible. Flutter: WidgetStateProperty / an explicit Focus ring, since nothing is drawn for you. On touch targets hover never fires — don't hide meaning in it.
  • Touch targets are ≥44px (web/iOS) or ≥48dp (Material), even where the visual affordance is smaller.
  • Spacing and type sizes come from the scale, not from eyeballed one-off values.
  • Empty, loading, and error states exist and say something useful.
  • Copy names outcomes, not mechanisms; errors say what to do next.
  • Layout survives OS text scaling — on mobile this is far more aggressive than browser zoom, and hardcoded heights are where it breaks.
  • Directional padding is direction-safe if the app ships an RTL locale.
  • Nothing here is a motion decision — if it moves, it went to animate.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

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.