make-interfaces-feel-better

작성자: jakubkrehel

인터페이스를 정교하게 만드는 디자인 엔지니어링 원칙. UI 컴포넌트 구축, 프론트엔드 코드 리뷰, 애니메이션 구현, 호버 상태, 그림자, 테두리, 타이포그래피, 마이크로 인터랙션, 진입/퇴장 애니메이션 또는 시각적 디테일 작업 시 사용. UI 폴리시, 디자인 디테일, "더 좋게 만들어", "어색함", 스태거 애니메이션, 테두리 반경, 광학 정렬, 글꼴 스무딩, 표 숫자, 이미지 외곽선, 박스 그림자에서 트리거됨.

npx skills add https://github.com/jakubkrehel/make-interfaces-feel-better --skill make-interfaces-feel-better

Details that make interfaces feel better

Great interfaces rarely come from a single thing. It's usually a collection of small details that compound into a great experience. Apply these principles when building or reviewing UI code. Before suggesting or writing a fix, identify the project's existing styling system and express the change in that system: Tailwind in a Tailwind project, plain CSS in a CSS project, or the established CSS-in-JS approach. Never introduce a second styling system just to apply a polish fix.

When reviewing, slow the interface down: replay motion at 10% speed in the browser's Animations panel and walk every state: hover, focus, active, loading, empty. What feels off at 10% speed is what's subtly wrong at full speed.

Quick Reference

CategoryWhen to Use
TypographyText wrapping, font smoothing, tabular numbers
SurfacesBorder radius, optical alignment, shadows, image outlines, hit areas
AnimationsInterruptible animations, enter/exit transitions, icon animations, scale on press, motion restraint
IconsIcon stroke weight, states via currentColor, outline vs fill, sizing, RTL flipping
PerformanceTransition specificity, will-change usage

Core Principles

1. Concentric Border Radius

Outer radius = inner radius + padding. Mismatched radii on nested elements is the most common thing that makes interfaces feel off.

2. Optical Over Geometric Alignment

When geometric centering looks off, align optically. Buttons with icons, play triangles, and asymmetric icons all need manual adjustment.

3. Shadows for Elevation, Borders for Structure

For buttons, cards, and containers whose border exists only to create depth, prefer layered transparent box-shadow values. Keep borders that communicate structure or state: dividers, layout separators, and selected or focus states.

4. Interruptible Animations

Use CSS transitions for interactive state changes — they can be interrupted mid-animation. Reserve keyframes for staged sequences that run once.

5. Split and Stagger Enter Animations

For an infrequent staged entrance where sequence helps communicate hierarchy, break content into semantic chunks and stagger them by ~100ms instead of animating one container. Do not stagger routine, high-frequency interactions.

6. Subtle Exit Animations

Use a small fixed translateY instead of full height. Exits should be softer than enters. Use ease-out for both enter and exit transitions.

7. Contextual Icon Animations

Animate icons with opacity, scale, and blur instead of toggling visibility. Use exactly these values: scale from 0.25 to 1, opacity from 0 to 1, blur from 4px to 0px. If the project has motion or framer-motion in package.json, match that package's import path (or the established nearby imports when both exist) and use transition: { type: "spring", duration: 0.3, bounce: 0 } — bounce must always be 0. If no motion library is installed, keep both icons in the DOM (one absolute-positioned) and cross-fade with CSS transitions using cubic-bezier(0.2, 0, 0, 1) — this gives both enter and exit animations without any dependency.

8. Font Smoothing

Apply -webkit-font-smoothing: antialiased to the root layout on macOS for crisper text.

9. Tabular Numbers

Use font-variant-numeric: tabular-nums for any dynamically updating numbers to prevent layout shift.

10. Text Wrapping

Use text-wrap: balance on headings. Use text-wrap: pretty for body text to avoid orphans.

11. Image Outlines

Add a subtle 1px outline with low opacity to images for consistent depth. The color must be pure black in light mode (oklch(0 0 0 / 0.1)) and pure white in dark mode (oklch(1 0 0 / 0.1)), never a near-black like slate, zinc, or any tinted neutral. A tinted outline picks up the surface color underneath it and reads as dirt on the image edge.

12. Scale on Press

A subtle scale(0.96) on click gives buttons tactile feedback. Always use 0.96. Never use a value smaller than 0.95 — anything below feels exaggerated. Add a static prop to disable it when motion would be distracting.

13. Skip Animation on Page Load

Use initial={false} on AnimatePresence to prevent enter animations on first render. Verify it doesn't break intentional entrance animations.

14. Never Use transition: all

Always specify exact properties: transition-property: scale, opacity. Tailwind's transition-transform covers transform, translate, scale, rotate.

15. Use will-change Sparingly

Only for transform, opacity, filter — properties the GPU can composite. Never use will-change: all. Only add when you notice first-frame stutter.

16. Minimum Hit Area

Interactive elements should prefer a 44×44px hit area for touch or mobile contexts. In dense desktop interfaces, use at least 40×40px. Extend with a pseudo-element if the visible element is smaller. Never let hit areas of two elements overlap.

17. 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; never mix libraries on one surface.

18. One SVG, Recolored per State

Icons use currentColor and get their states (hover, selected, disabled) from CSS color and opacity, never from separate assets. Outline variant is the default; fill variant marks the active state.

19. Motion Restraint

No custom animation on high-frequency interactions: the attention cost repeats on every trigger. Motion is never the only feedback channel; every animated state change also needs a static cue such as color, icon, or label.

Common Mistakes

MistakeFix
Same border radius on parent and childCalculate outerRadius = innerRadius + padding
Icons look off-centerAdjust optically with padding or fix SVG directly
Border used only to fake elevationUse layered box-shadow with transparency; keep structural and state borders
Jarring staged entrance or contextual exitStagger infrequent entrances and keep context-preserving exits subtle
Numbers cause layout shiftApply tabular-nums
Heavy text on macOSApply antialiased to root
Animation plays on page loadAdd initial={false} to AnimatePresence
transition: all on elementsSpecify exact properties
First-frame animation stutterAdd will-change: transform (sparingly)
Tiny hit areas on small controlsExtend with a pseudo-element to 44×44px for touch/mobile, or at least 40×40px in dense desktop UI
Hairline icon beside bold textMatch the stroke width to the text weight
Separate icon assets per stateOne currentColor SVG, states via CSS
Filled icons everywhereOutline as default, fill only for the active state
Entrance animation on every hover or keystrokeInstant feedback or ≤150ms opacity/color transition

Review Output Format

Use full when no review mode is supplied.

ModeCoverageFinding cap
quickPrimary user path and highest-traffic states; report only HIGH and MEDIUM issues5
fullEntire requested scope across typography, surfaces, animations, icons, and performance15

Scope and Coverage

State the mode, exact scope, framework, styling conventions, and any review boundary. Show what was actually inspected:

CategoryEvidence inspectedResult
TypographyFiles, components, states, or checksFindings count, Clear, or Not reviewed with a reason

Include all five Quick Reference categories. Never imply an uninspected surface was reviewed.

Findings

Group findings by principle. Use a markdown table with Severity, Location, Before, After, and Why columns. Include every change made or proposed, not a subset. Never use separate "Before:" / "After:" lines.

  • Severity: HIGH makes an interaction inaccessible, misleading, unreadable, or repeatedly disruptive; MEDIUM creates a noticeable usability or consistency problem; LOW is isolated polish and appears only in full mode.
  • Location: cite path/to/file:line. If the artifact has no source files, cite the exact screen and component instead.
  • Before / After: show the current implementation and an actionable replacement.
  • Why: name the violated principle and explain its user impact.

Consolidate a repeated systemic issue into one row and list every affected location. Omit principles with no findings and never pad the report to reach the cap.

Example

Concentric border radius

SeverityLocationBeforeAfterWhy
LOWsrc/Card.tsx:28rounded-xl on card + rounded-xl on inner button (p-2)rounded-2xl on card (8 + 8 = 16), rounded-lg on inner buttonNested corners should be concentric
LOWsrc/card.css:11border-radius: 16px on both nested surfacesOuter 24px, inner 16px with 8px paddingEqual nested radii make the inner surface look pinched

Tabular numbers

SeverityLocationBeforeAfterWhy
MEDIUMsrc/Counter.tsx:17<span>{count}</span><span className="tabular-nums">{count}</span>Proportional digits cause changing values to shift
LOWsrc/timer.css:8Default numerals on a timerAdd font-variant-numeric: tabular-nums to the timerEqual-width digits keep the timer stable

Scale on press

SeverityLocationBeforeAfterWhy
LOWsrc/Button.tsx:19<button className="...">Add active:scale-[0.96] transition-transformPress feedback makes the control feel responsive
MEDIUMsrc/button.css:24scale(0.9) on pressRaise to scale(0.96)Anything below 0.95 feels exaggerated

Considered but Rejected

Include 1–3 real candidates in quick mode and 2–5 in full mode:

LocationCandidateRejected because
src/Card.tsx:28Increase the shadowExisting depth matches the shared surface token; changing one card would reduce consistency

Do not invent filler. If the scope contains fewer borderline candidates, include the ones that exist and say so.

Verification and Verdict

After the findings:

  1. Verification: list the exact commands or interactions run and their observed results. Walk every relevant state and inspect motion at 10% speed when animation is involved. If a check was not run, label it Not verified and state what remains.
  2. Verdict: Block if any HIGH finding remains, Needs changes if only MEDIUM or LOW findings remain, and Approve only when no actionable findings remain. List every unverified check beside the verdict.

When there are no findings, omit the findings table, state "No actionable interface-polish findings", report verification and rejected candidates, and end with Approve.

관련 스킬

posthog-onboarding
posthog
기존 PostHog 고객이 PostHog 인스턴스를 개선할 수 있도록 도와줍니다. "help [customer] improve their PostHog setup", "audit [company]'s PostHog…"에서 트리거됩니다.
official
configuring-experiment-rollout
posthog
PostHog 실험의 롤아웃 형태를 구성합니다 — 변형 분할(50/50, 80/20, A/B/C 비율), 사용자를 제한하는 전체 롤아웃 비율…
official
notion-meeting-intelligence
firecrawl
Notion 컨텍스트와 Codex 리서치를 활용해 회의 자료를 준비합니다. 컨텍스트 수집, 안건/사전 자료 작성, 참석자 맞춤 자료 제작 시 사용하세요.
official
finding-sessions-to-watch
posthog
사용자가 "녹화본을 보고 싶지만 어떤 걸 봐야 할지 모르겠다"는 상태에서 시청할 가치가 있는 짧고 유용한 세션 목록을 제공하도록 안내합니다. 사용자가 어떤 것을 물어볼 때 사용하세요.
official
docs-editor
openai
OpenAI Cookbook 노트북, Markdown 및 MDX를 검토하고 편집하여 기술적 정확성, 명확성, 문법, 일관성, 실행 가능한 예제 및 저장소…
official
launchdarkly-flag-cleanup
launchdarkly
프로덕션 동작을 유지하면서 코드에서 기능 플래그를 안전하게 제거합니다. 사용자가 코드에서 플래그를 제거하거나, 플래그 참조를 삭제하거나...
official
turborepo
vercel
지능형 작업 캐싱, 병렬 실행, 의존성 그래프 오케스트레이션을 갖춘 모노레포 빌드 시스템입니다. 작업 출력을 캐싱하고 선언된 의존성에 따라 작업을 병렬로 실행하며, CI/CD 워크플로를 위한 원격 캐싱을 지원합니다. --affected를 사용하여 변경된 패키지만 필터링 및 실행하거나, --filter를 사용하여 사용자 정의 패키지를 선택할 수 있습니다. turbo.json에서 dependsOn, outputs, inputs, env 키를 사용하여 작업을 구성하며, 패키지 구성을 통해 패키지별 재정의를 지원합니다. 모노레포를 강제합니다...
official
huggingface-lora-space-builder
huggingface
사용자가 제공한 LoRA를 위한 Gradio 데모를 Hugging Face Spaces에 구축하고 게시합니다. 누군가 Space, 데모 등을 생성, 제작, 배포 또는 게시하도록 요청할 때 사용하세요.
official