# Better UI > Use when UI details like border radius, alignment, depth and hit areas need polish. Polishes and improves the UI in your project. Covers concentric border radius, optical alignment, surface depth, contextual icons, hit areas and more. ## Facts - Type: Agent skill - Discipline: Design - Author: Jakub Krehel (https://github.com/jakubkrehel) - Upstream: https://github.com/jakubkrehel/skills/blob/main/skills/better-ui/SKILL.md - Source file: https://raw.githubusercontent.com/jakubkrehel/skills/main/skills/better-ui/SKILL.md (7152 bytes, sha256:2482b7b89d851a3128d36edf5367f3223e113a4ef421b7358c7e6de3f2c83faf) - Licence: MIT - Upstream copy last changed: 2026-09-17 - Status: listed, not yet ruled on by the curator ## Use it I want to use the Better UI skill by Jakub Krehel. This is what it does, in the author's own words: Polishes and improves the UI in your project. Covers concentric border radius, optical alignment, surface depth, contextual icons, hit areas and more. Download the whole folder at https://github.com/jakubkrehel/skills/tree/main/skills/better-ui, SKILL.md and every file beside it, into your skills directory: ~/.claude/skills/better-ui/ for Claude Code, ~/.agents/skills/better-ui/ for Codex, ~/.cursor/skills/better-ui/ for Cursor. Get it from the author's repo, not from startupskills.dev. Then tell me the path you installed it to and how to invoke it. Nothing here is vendored: install from the author's repo, not from this site. A verbatim copy of the author's file follows for reading, under the licence reproduced after it; the current file is in the author's repo. ## Links - Page: https://startupskills.dev/jakubkrehel/better-ui - Author: https://startupskills.dev/jakubkrehel - Index: https://startupskills.dev/llms.txt ## The file itself --- name: better-ui description: Polishes and improves the UI in your project. Covers concentric border radius, optical alignment, surface depth, contextual icons, hit areas and more. --- # UI polish Polish comes from a pile of small details that compound. This skill is the reference for which are worth having and what values they take. When reviewing, slow the interface down. What feels off at 10% speed is what is subtly wrong at full speed. Keep the project's component library, tokens and density, and match its motion language except where a rule below prescribes an exact interaction. Every duration, curve, scale and blur below is a specific value, not a range to approximate. `cubic-bezier(0.2, 0, 0, 1)` is not `cubic-bezier(0.4, 0, 0.2, 1)`, and `0.96` is not `0.95`. Use what is written. Text wrapping, font rendering, tabular numbers and text spacing belong to `better-typography`. Hit areas, focus, keyboard support, ARIA and reduced motion belong to `better-accessibility`. Grouping, section spacing, breakpoints and spatial RTL belong to `better-layout`. ## Concentric border radius Outer radius = inner radius + padding. Mismatched radii on nested elements is the most common thing that makes an interface feel off. Radius, shadow and outline recipes are in [surfaces.md](surfaces.md). ## Optical over geometric alignment When geometric centering looks off, align optically. Buttons with icons, play triangles and asymmetric icons all need a manual nudge. ## Shadows for elevation, borders for structure Where a border exists only to create depth, prefer layered transparent `box-shadow` values. Keep borders that communicate structure or state: dividers, separators and selected or focus states. ## Interruptible animations Use CSS transitions for interactive state changes, because they can be interrupted mid-animation. Reserve keyframes for staged sequences that run once. ## Split and stagger enter animations For an infrequent staged entrance where sequence communicates hierarchy, break the content into semantic chunks and stagger them by ~100ms. Animating one container gets you less for the same cost. Leave high-frequency interactions unstaggered. See [enter-exit.md](enter-exit.md). ## Subtle exit animations Use a small fixed `translateY` rather than full height. Exits should be softer than enters. Use `ease-out` for both directions. ## Contextual icon animations Animate icons with `opacity`, `scale` and `blur` rather than toggling visibility. Use exactly these values: scale `0.25` to `1`, opacity `0` to `1`, blur `4px` to `0px`. With a motion library (`motion` or `framer-motion` in `package.json`), match that package's import path, or nearby imports where both exist. Use `transition: { type: "spring", duration: 0.3, bounce: 0 }`. Bounce is always `0`. Without one, keep both icons in the DOM with one absolutely positioned, and cross-fade with `cubic-bezier(0.2, 0, 0, 1)`. That gives you enter and exit with no dependency. Both recipes are in [icon-transitions.md](icon-transitions.md). ## Image outlines Give images a `1px` outline at low opacity for consistent depth. Pure black in light mode (`oklch(0 0 0 / 0.1)`), pure white in dark (`oklch(1 0 0 / 0.1)`). Never a near-black like slate or zinc and never a tinted neutral. A tinted outline picks up the surface underneath and reads as dirt on the image edge. ## Scale on press A `scale(0.96)` on click gives a button tactile feedback. Always `0.96`; anything below `0.95` feels exaggerated. Add a `static` prop to switch it off where motion would distract. See [recipes for CSS, Tailwind and Motion](animations.md#scale-on-press). ## Skip animation on page load Use `initial={false}` on `AnimatePresence` to keep enter animations off the first render. Check that it leaves intentional page entrances intact. ## Suppress transitions on theme switch A theme flip changes color, background, border and shadow on nearly every element at once. Every transition on those properties fires together and the switch smears instead of snapping. Inject `*,*::before,*::after{transition:none !important}`, force a reflow, then remove it on the next frame. See the [recipe](animations.md#suppress-transitions-on-theme-switch). ## Transition only what changes Always name the exact properties: `transition-property: scale, opacity`. Tailwind's `transition-transform` covers `transform, translate, scale, rotate`. ## Use `will-change` sparingly Only for `transform`, `opacity` and `filter`, which the GPU can composite. Never `will-change: all`. Add it when you see first-frame stutter, not before. See [performance.md](performance.md). ## Match icon stroke to text weight An icon next to text carries the text's optical weight: `1.5px` stroke beside regular (400) text, `2px` beside semibold (600). One stroke weight per icon set and one icon library per surface. Sizing and RTL flipping are in [icons.md](icons.md). ## One SVG, recolored per state Icons use `currentColor` and take hover, selected and disabled states from CSS color and opacity, never from separate assets. Outline is the default variant; fill marks the active state. ## Motion restraint Give high-frequency interactions instant feedback, or a transition of `150ms` or less on opacity and color. A custom animation there charges its attention cost on every trigger. Every animated state change also needs a static cue: color, an icon, or a label. Motion is never the only feedback channel. ## Before you finish | Mistake | Fix | | --- | --- | | Icons look off-center | Nudge optically with padding, or fix the SVG | | Jarring staged entrance or exit | Stagger infrequent entrances; keep exits subtle | | Theme toggle crossfades the whole page | Disable transitions for the swap, force a reflow, restore on the next frame | | `transition: all` on elements | Specify exact properties | | First-frame animation stutter | Add `will-change: transform` (sparingly) | | Hairline icon beside bold text | Match the stroke width to the text weight | ## Reporting **Severity.** `HIGH` breaks an interaction, makes motion unusable, or leaves a state change visible only while the animation runs. `MEDIUM` is a visible inconsistency in surfaces, icons, or motion. `LOW` is isolated polish. **Verification.** Without a browser: every state the component defines, meaning hover, focus, active, loading and empty, plus motion durations and easings read from the code. With one: walk each state, and replay motion at 10% speed in the browser's Animations panel. Report every check you could not run as `Not verified`. **Format.** Group findings under the principle each violates, ordered by severity, one row per root cause listing every location it appears in: | Severity | Location | Before | After | Why | | --- | --- | --- | --- | --- | `Location` is `path/to/file:line`. `Why` names the principle and the user impact. End with `Block` when any `HIGH` remains, `Approve` otherwise, leaving the rest in the table as work to do. Never `Approve` coverage you did not inspect. With nothing to report, state "No actionable UI-polish findings" and report verification. ## Licence of the file above The file above is https://raw.githubusercontent.com/jakubkrehel/skills/main/skills/better-ui/SKILL.md, by Jakub Krehel, reproduced without changes. - Source: https://github.com/jakubkrehel/skills/blob/main/skills/better-ui/SKILL.md - Copyright: Copyright (c) 2026 Jakub Krehel - Licence: MIT, https://raw.githubusercontent.com/jakubkrehel/skills/main/LICENSE The repo's licence file, verbatim from https://raw.githubusercontent.com/jakubkrehel/skills/main/LICENSE: MIT License Copyright (c) 2026 Jakub Krehel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.