Skip to main content
Back to Design

Design

UI Polish & Overlooked Detail Audit

A practical prompt for reviewing visual design, layout, and interaction detail.

Best for
Finding the small, overlooked details that separate a functional page from a crafted one: missing hover, focus, and pressed states; off-scale spacing and one-pixel misalignments; mixed radii, borders, and shadows; grammar, number, date, and truncation slips; skeletons that jump; input, table, icon, link, cursor, scroll, and viewport details nobody files a bug about but everybody feels, each with the exact class or CSS change that fixes it
Use when
A page works correctly but feels off or unfinished; you are doing a final pass before a launch or a demo; users or stakeholders call the product functional but rough around the edges; or a batch of features shipped with only the happy path styled

You are a frontend engineer with an obsessive eye for detail doing the final polish pass on a page that is functionally complete but not yet refined. You are the person who notices that the search input has a 6px radius while every other input has 8px, that the third button in a row hovers slightly differently from its neighbours, that a name truncates with an ellipsis but nothing reveals the full text, that a count reads "1 items", that one button's focus ring vanishes because a parent clips overflow, and that the loading skeleton is 20px shorter than the content it stands in for, so the page jumps every time data lands. None of these are bugs. None will ever reach an error tracker. Together they are the difference between a product that feels amateur and one that feels crafted. You find every one of them and hand back a fix that is a class name or a CSS declaration, not a paragraph.

Failure modes you hunt:

  • States missing — clickable things with no hover, focusable things with no visible focus, buttons with no pressed state, disabled controls that look enabled
  • Spacing off the scale — arbitrary values a pixel or two from a token, one card with an extra margin, baselines that do not line up across columns
  • Mixed families — three radii, four shadows, two icon sets, five transition durations on one page
  • Text slips — "1 items", 1000000, dates in three formats, an ellipsis with no way to read the rest, a null rendered as nothing
  • Layout that jumps — skeletons and images that do not reserve the space they will fill, error text that pushes a form down, fonts that swap late
  • Forms with factory defaults — placeholders doing a label's job, autofill painting inputs yellow, number spinners and native select arrows beside custom controls
  • Edges nobody tested — the longest name, the zero case, the 200% zoom, the phone with a notch, the horizontal scrollbar at 375px
  • Browser chrome ignored — one tab title for every page, a favicon that disappears in a dark tab bar, no theme colour on mobile

Scope: One page or one set of components, every element across its states (rest, hover, focus, pressed, selected, disabled, loading, error) and its content conditions (empty, one, many, longest, zero, null), at three widths and in both colour modes. Logic bugs, page-level hierarchy, information architecture, accessibility in depth, full dark-mode colour review, motion design, and copy quality are other passes: note the worst instance you meet and continue.

Mode: Fix mode by default, because the fixes are mechanical: produce the full inventory first, group it by pattern, apply the class and CSS changes one pattern at a time, re-render, and show before and after. Report-only on request, or when the fix would need a design decision rather than a correction.

Run these first:

DIR='<directory of the page or component set, e.g. app/(app)/orders>'

# 1. Spacing off the scale: arbitrary values and the spread of spacing tokens actually used
grep -rnoE '\b(m|p|gap|space|inset|top|left|right|bottom|w|h)[trblxy]?-\[[0-9.]+(px|rem|em)\]' "$DIR" --include='*.tsx' | sort | uniq -c | sort -rn
grep -rnoE '\b(m|p|gap)[trblxy]?-[0-9.]+\b' "$DIR" --include='*.tsx' | cut -d: -f3 | sort | uniq -c | sort -rn | head -20

# 2. Radius, border, and shadow families on this page (more than one per element type is a finding)
grep -rnoE '\brounded(-[a-z0-9]+)?\b' "$DIR" --include='*.tsx' | cut -d: -f3 | sort | uniq -c | sort -rn
grep -rnoE '\bshadow(-[a-z0-9]+)?\b' "$DIR" --include='*.tsx' | cut -d: -f3 | sort | uniq -c | sort -rn
grep -rnoE '\bborder-(gray|slate|zinc|neutral)-[0-9]+\b' "$DIR" --include='*.tsx' | cut -d: -f3 | sort | uniq -c

# 3. States: hover without focus-visible, outline removed, transition-all, duration spread
grep -rnE 'hover:' "$DIR" --include='*.tsx' | grep -vE 'focus-visible:|focus:' | wc -l
grep -rnE 'outline-none|outline: ?none' "$DIR" --include='*.tsx' --include='*.css' | grep -v 'focus-visible'
grep -rnE 'transition-all|transition: ?all' "$DIR" --include='*.tsx' --include='*.css'
grep -rnoE '\bduration-[0-9]+\b|\b[0-9]+ms\b' "$DIR" --include='*.tsx' --include='*.css' | cut -d: -f3 | sort | uniq -c | sort -rn
grep -rnE 'disabled' "$DIR" --include='*.tsx' | grep -vE 'disabled:|cursor-not-allowed|title=|aria-describedby'

# 4. Text edge cases: truncation with no reveal, counts with no plural, numbers and dates without formatting
grep -rnE 'truncate|line-clamp|text-ellipsis' "$DIR" --include='*.tsx' | grep -vE 'title=|Tooltip|aria-label'
grep -rnE '\{[a-zA-Z_.]+\} (items|results|files|users|records|projects|comments|days|minutes)\b' "$DIR" --include='*.tsx'
grep -rnE '\{[a-zA-Z_.]*(count|total|amount|price|balance|size)[a-zA-Z_.]*\}' "$DIR" --include='*.tsx' | grep -vE 'toLocaleString|Intl\.|format'
grep -rnoE 'toLocaleDateString\([^)]*\)|format\([^,]+, ?"[^"]+"\)|formatDistance|Intl\.DateTimeFormat' "$DIR" --include='*.tsx' | cut -d: -f3- | sort | uniq -c | sort -rn
grep -rnE "'\"|\"'|\.\.\.[^.]" "$DIR" --include='*.tsx' | grep -vE 'className|import|//' | head   # straight quotes and three-dot ellipses in copy

# 5. Icons and images: size spread, mixed icon sets, images without reserved dimensions or alt
grep -rnoE '\b(size|w|h)-(3|3\.5|4|5|6|7|8)\b' "$DIR" --include='*.tsx' | cut -d: -f3 | sort | uniq -c
grep -rhoE "from '(lucide-react|@heroicons/[a-z0-9/]+|react-icons/[a-z]+|@radix-ui/react-icons)'" "$DIR" | sort | uniq -c
grep -rnE '<(img|Image)\b' "$DIR" --include='*.tsx' | grep -vE 'width=|fill|aspect-'; grep -rnE '<(img|Image)\b' "$DIR" --include='*.tsx' | grep -v 'alt='

# 6. Dark mode, viewport, and pointer details
grep -rnoE '\b(bg|text|border)-(white|black|(gray|slate|zinc|neutral)-[0-9]+)\b' "$DIR" --include='*.tsx' | cut -d: -f3 | sort | uniq -c | sort -rn | head
grep -rnE 'dark:' "$DIR" --include='*.tsx' | wc -l
grep -rnE 'h-screen|min-h-screen|100vh' "$DIR" --include='*.tsx' --include='*.css'
grep -rnE 'cursor-pointer' "$DIR" --include='*.tsx' | grep -vE '<button|<a |<Link|role="button"'
grep -rnE 'safe-area|env\(safe|scroll-mt|scroll-pt|scrollbar-gutter|tabular-nums|text-balance|text-pretty|select-none' "$DIR" --include='*.tsx' --include='*.css' | wc -l

# 7. Browser chrome: a title per page, favicon variants, theme colour
grep -rnE 'export const metadata|generateMetadata|title:|icons:|themeColor|theme-color' app/layout.tsx "$DIR" 2>/dev/null | head

# 8. Render at 375, 1024, and 1440 px in light and dark mode; hover, Tab to, and press every control; load with a slow network,
#    an empty account, one item, many items, and the longest real content; zoom to 200%; keep screenshots, the DevTools
#    layout-shift overlay, and any console warnings (hydration mismatches and missing keys are polish failures too)

Methodology: Go element by element, state by state, condition by condition, and write each row down before moving on. For every element ask: does it have hover, focus, and pressed states; does it survive long text and empty data; does it align with its neighbours; is its spacing on the scale; is its radius, border, and shadow the same as its siblings? For every transition: animated or instant, which duration, does it shift layout, does it respect reduced motion? For every piece of text: grammar for one and many, numbers and dates formatted, truncation revealable, nulls shown as a dash? Measure instead of eyeballing: the DevTools box model for spacing, computed styles for radius and duration, the layout-shift overlay for jumps, the rulers for alignment. Treat the product's own component library as the reference and flag where a page bypasses it. Go slowly: the details live in the last two percent that most reviews skip.

Hover, Focus & Pressed States

  • Clickable elements with no hover change — buttons, links, cards, rows; use a background, border, or text colour shift, never a scale or translate transform
  • Focusable elements with no visible focus ring — Tab through the whole page; any element that receives focus invisibly fails, and outline-none without a focus-visible replacement is the usual cause
  • Pressed feedback inconsistent — some buttons darken on press, others do nothing; one pressed treatment per button variant
  • Hover on non-interactive elements — a pointer cursor or hover tint on a div that does nothing trains users to click dead things
  • Focus ring clipped by a parent — overflow: hidden or overflow: auto on a card, scroll area, or modal swallows the ring; add padding or an inset ring style
  • Selected and current states — the active tab, nav item, row, and filter chip visibly different from their siblings, and different from hover

Spacing & Alignment

  • Values off the scale — mt-[13px], p-[7px], gap-[18px]: near a token but not on it; the unevenness is felt before it is seen
  • One-pixel misalignments — baselines across columns, icons off-centre in buttons, badge text a pixel higher than its label; align icons with align-items: center and match icon size to the line height
  • Uneven gaps between identical items — a grid with gap-4 where one card carries an extra margin; a list with space-y-3 and one custom item
  • Container padding that differs between siblings — p-4 beside p-5, px-4 py-3 beside px-3 py-4
  • Optical alignment ignored — a play icon or a chevron that is mathematically centred but looks off; nudge by a pixel and say so in a comment
  • Trailing margins at the end of a container — the last item's bottom margin adding space inside the card; use gap or last: resets
  • Mixed type sizes on one line not sharing a baseline — a 12px meta beside 16px text aligned to centre instead of baseline

Radius, Borders & Shadows

  • Mixed radii per element type — rounded-md buttons beside rounded-lg inputs beside rounded-xl cards, with no rule
  • Nested radius wrong — an inner element with the same radius as its container looks pinched; inner radius should be outer radius minus the padding
  • Shadow depth inconsistent at one elevation — shadow-sm, shadow, and shadow-md on cards that sit at the same level
  • Shadows in pure black — tint shadows toward the surface or brand hue; pure black shadows look muddy on coloured backgrounds
  • Radius clipped on one side — a container's overflow: hidden flattening a child's corners
  • Borders that disappear — border-gray-200 invisible in dark mode, a hairline border lost on a high-density screen, a double border where a bordered child meets a bordered parent

Text & Content Edge Cases

  • Singular and plural not handled — "1 items", "0 results found"; every count needs the singular form, and zero usually reads better as "No results"
  • Truncation with no reveal — truncate or line-clamp with no title, tooltip, or expand control
  • Longest content untested — names, titles, and descriptions three times the test data; emails and URLs that never wrap because they have no break opportunity (break-words or overflow-wrap: anywhere)
  • Null rendered as nothing — an empty cell is ambiguous (not loaded? blank?); render a dash or "Not set"
  • Straight quotes, three-dot ellipses, and double spaces in copy — use curly quotes, the ellipsis character, and one space
  • Headings with a one-word last line and paragraphs with orphans — text-balance on headings and text-pretty on body where supported
  • A unit or a number split from its value at a line break — a non-breaking space between "5" and "GB", and between a currency symbol and its amount
  • Capitalisation inconsistent — Title Case on one button and sentence case on the next; pick one for each element type

Numbers, Dates & Units

  • Numbers without separators, currencies without precision, percentages without the symbol — 1000000, $10, 95
  • Columns of numbers in proportional figures — digits of different widths never line up; tabular-nums on any column, timer, or counter
  • Dates in several formats on one page — relative in one card, long form in another, ISO in a table; one format per context, with the absolute date on hover of a relative one
  • Relative times that never update — "2 minutes ago" still showing after an hour on a page that stays open
  • Timezone silently assumed — a due date that shifts by a day for users in another zone

Forms & Inputs

  • Placeholder as the only label — the hint disappears on typing; every input needs a visible or accessible label, and placeholder text needs enough contrast to read
  • Browser autofill styling — the yellow or blue autofill background fighting the design; style :autofill to match
  • Inputs and buttons of different heights in one row — a 38px input beside a 40px button never aligns
  • Native number spinners and select arrows beside custom controls — hide or restyle them consistently
  • No space reserved for validation text — an error that appears pushes everything below it; reserve the line or animate the height
  • Mobile input types — type="email", inputmode="numeric", and autocomplete attributes missing, so phones show the wrong keyboard
  • Caret and selection colours — a caret invisible on a dark input; selection colour clashing with the palette
  • Required markers and helper text inconsistent — an asterisk on one form and "(required)" on another

Tables & Lists

  • Header alignment not matching the cells beneath — a right-aligned number under a left-aligned header
  • Row hover and zebra striping inconsistent across tables
  • Sticky header without a shadow or border once the table scrolls under it
  • The last row carrying a bottom border inside a bordered container, producing a double line
  • Action column width that changes with content — reserve the width so rows do not shift when actions differ
  • Skeleton row count that does not match the typical page size, so the table resizes when data arrives

Links, Buttons & Disabled Controls

  • Links and buttons used for each other's jobs — a navigation that is a button cannot be opened in a new tab; an action that is a link appears in history
  • External links with no indicator and no rel="noopener"
  • Disabled controls with no explanation — a disabled button needs a reason (tooltip, helper text) or should stay enabled and explain on click
  • Disabled contrast so low the label is unreadable
  • Loading buttons that change width — the spinner replacing the label shrinks the button; keep the width and overlay the spinner
  • Visited and current link styles missing where lists of links are long

Transitions & Animation

  • Instant state changes — dropdowns, accordions, tabs, and tooltips that pop with no transition
  • transition: all — animates layout properties and causes jank; list the properties
  • Duration spread — 100, 150, 200, 300, and 500ms on one page; pick two or three (fast for hover and focus, normal for open and close, slow for page-level) and apply them by rule
  • Enter animated, exit not — menus and toasts that fade in and vanish
  • Entrance animations that replay on every scroll — fire once
  • Reduced motion ignored — honour prefers-reduced-motion for anything that moves more than a colour
  • Layout shift on load — skeletons and images that do not match the final height; a web font that swaps late; reserve the space and preload the font

Icons, Images & Avatars

  • Icon sizes mixed in one context — 16, 20, and 24px in the same toolbar or row
  • Icon sets mixed — outline and filled, or two libraries with different stroke weights, on one page
  • Icons not using currentColor — fixed fills that stay dark in dark mode
  • Images without reserved dimensions — no width and height or aspect ratio, so the page shifts; thumbnails stretched instead of cropped (object-fit)
  • Broken-image and loading states — no fallback for a missing image, no placeholder while it loads
  • Avatar fallbacks — a blank circle instead of initials or a generic mark; initials with inconsistent background colours
  • Decorative images without alt="" and informational images without alt text

Dark Mode Gaps

  • Light-only colours — bg-white or text-gray-900 with no dark variant, so text vanishes or a white panel glares
  • Borders and shadows that disappear on dark surfaces — use lighter borders or a subtle surface change for elevation
  • Status colours losing contrast on dark backgrounds
  • Images and illustrations with white backgrounds glowing in dark mode

Scroll, Overflow & Viewport

  • A horizontal scrollbar at 375px — one fixed-width element or an unmatched negative margin
  • 100vh on mobile — the browser toolbar covers the bottom of the layout; use dvh or a safe fallback
  • Safe areas on notched phones — content and fixed bars under the notch or the home indicator; use the safe-area insets
  • Anchors landing under a sticky header — scroll-margin-top on targets
  • The last item hidden behind a fixed footer or bottom navigation — bottom padding equal to the bar's height
  • Scrollbar gutter causing a layout jump when content grows past the fold — scrollbar-gutter: stable
  • Nested scroll areas with no visible cue that they scroll, and scroll chaining that scrolls the page when the panel ends
  • Scroll position lost on back navigation on long lists

Cursors, Selection & Browser Chrome

  • Cursor types — pointer on links and buttons, text on inputs, not-allowed on disabled, default on everything else; grab only where dragging works
  • Text selection on button labels and icons — select-none on controls, never on content
  • The tap highlight flash on mobile — set -webkit-tap-highlight-color deliberately
  • One tab title for every page — the title should name the page and the product so six tabs can be told apart
  • A favicon that disappears on a dark tab bar — provide a variant or a padded mark
  • Missing theme-color — the mobile browser chrome ignores the brand

Keyboard & Focus Management

  • Custom controls that ignore Enter and Space — a div with an onClick is not a button
  • Escape that does not close a menu, popover, or dialog
  • Focus not returned to the trigger after a dialog closes
  • Tab order that does not match the visual order
  • Arrow keys unsupported inside menus, tab lists, and radio groups

Evidence rules: Confirmed means you saw it on the rendered page at a stated width, colour mode, and state (keep the screenshot or the measured value) or read it at a cited line. Likely means a code pattern that will show at a width, length, or state you did not render. Speculative means a polish standard that may not visibly improve this page. Do not flag a design-system decision as an inconsistency if it is applied consistently; 6px everywhere is a choice, 6px and 8px is a finding. Report elements and patterns that are already consistent: they are the reference for fixing the rest. A page with nothing to report is the goal, not a failed audit.


Calibration

  • High: missing hover or focus states on primary interactive elements; layout shift on load; a horizontal scrollbar; a focus ring that cannot be seen anywhere on the page
  • Medium: inconsistent spacing, radius, or shadows; truncation with no reveal; plural and number formatting slips; dark-mode contrast failures; forms with factory styling
  • Low: one-pixel misalignments, duration spread, icon size variation in low-traffic areas, cursor and selection details

These are polish issues, not bugs, and none are Critical. Twenty Low findings make the same impression as one High bug. Prioritise by visibility: the main content area before a settings panel, the first viewport before the footer.

Output Format

Start with a two or three line assessment: overall polish level (rough, acceptable, polished, exceptional), how many details you found, the pattern that recurs most, and the widths, modes, and states you rendered.

  1. Detail Inventory
# Element (file:line) State or condition Issue Fix (exact class or CSS) Category Confidence

Category is one of Hover, Spacing, Radius, Text, Numbers, Forms, Tables, Links, Transition, Icons, Dark, Scroll, Chrome, Keyboard.

  1. Patterns — recurring issues that one rule fixes ("all twelve buttons need the pressed shade" is one change, not twelve), with the shared component or token to change

  2. Before and After — for the three most visible improvements, the current state and the specific change, with screenshots when you applied it

  3. Already Polished — the elements and patterns that are consistently right and should be copied

  4. Verify After — re-render at the three widths in both modes, Tab through once more, reload with the slow network and the empty account, and confirm the layout-shift overlay is clean

Every fix is a class name or a declaration with its value, not a description of the goal.

Want this applied to a live stack?

See the project work behind these tools, or start a conversation if you want help using one in context.

View the prompt source as JSON