Interface Cheat Sheet
User Interface
- Use concentric border radius on nested elements.
- Align for optical alignment, not geometric alignment.
- Give images a
1pxoutline, offset by-1px: black at8%opacity in light mode, white at8%in dark mode.
Animation
- Never use
transition: all, name the exact properties that change instead. - Slightly scale down buttons to a value between
0.95and0.98when pressed withtransition: scale 200ms ease-out. - Cross-fade icons when they swap. The entering icon scales
0.25→1, opacity0→1and blur4px→0px. The exiting one reverses the same animation. - Use CSS transitions for interactions, because they can be interrupted. Use keyframes for sequences that only run once.
- Disable all transitions when changing theme from light to dark and vice versa.
- Use
will-changeonly for properties that are actually changing.transform,opacityandfilter. - When you run into an issue with an element shifting randomly by 1-2px when animating, especially in Safari on iOS, add
will-change: transformto the element. - When animating entrance, stagger elements by group or by invidivual element. Stagger staged entrances by around
100ms. - Don't animate high-frequency interactions such as color change of an item on hover in a list.
Typography
- Always use
.woff2on the web, never.ttfor.otf. - Use
font-variant-numeric: tabular-numson every value that changes and in tables: timers, counters, prices, data columns. Skip if you're already using a monospace font. - Cap long-form text at 60–75 characters per line.
- Use
text-wrap: balanceon headings,text-wrap: prettyon descriptions, neither in long-form text. - Use
overflow-wrap: break-wordwhere long words, links or IDs can escape;white-space: nowrapon labels and badges. -webkit-font-smoothing: antialiasedand-moz-osx-font-smoothing: grayscaleonce on the root, never per component.- Store copy in natural case and control the presentation with
text-transform. - Smart punctuation: curly quotes, an en dash for ranges, an em dash for asides, the single ellipsis character.
text-underline-position: from-fontwithtext-decoration-skip-ink: auto, so underlines clear the descenders.- Truncated text keeps the full value reachable in a tooltip or an expanded view.
Colors
- Every step in a color palette should have a purpose: page background, component hover, border, solid fill, body text. Don't add steps that nothing uses.
- Components should use semantic tokens (
--color-text-secondary), never primitives (--blue-500). The primitive is the raw value, the token is how the value is used. - Never name a token for its appearance or its first use:
--color-accent-solid, not--color-blue-buttonor--color-sidebar-gray. - Reserve
accentfor the brand color soprimarynever means both the brand and the main body text. - Don't reuse a token from another role just because it's the right color. When that role's color changes your element changes with it, so add a token for the new role instead.
- Measure contrast against the background the element actually renders on, not the page background.
- Dark mode palette is not the light palette reversed.
- Pick one theme switching mechanism:
prefers-color-schemeor a.darkclass and use it for every token. - You can define a gradient's interpolation space: use
in oklabfor even brightness,in oklchfor more vivid middle tones or neither which falls back to sRGB with a classic muted midpoint.
Accessibility
- Use semantically correct native elements:
<button>for buttons,<a>for links, never a plain<div>when you can use a native element. - Style
:focus-visible; don't useoutline: nonewithout a replacement. - Only use
tabindex="0"andtabindex="-1"— positive values break the natural tab order. - Give icon-only buttons a descriptive
aria-labeland never putaria-hidden="true"on a focusable element. - Write alt text by purpose:
alt="Search"on a search button, notalt="magnifying glass". Decorative images getalt="". - Give every input a real
<label>,typeandinputmode. - Never block paste; people paste passwords and one-time codes.
- A tooltip on a
disabledcontrol never opens for keyboard or touch. Put the explanation in visible text next to it, or usearia-disabled="true"to keep the control focusable. - Keep submit enabled until the request starts, then validate on submit:
aria-invalid="true",aria-describedbypointing at the error, focus on the first invalid field. - Use at least a
24x24pxhit-area,44x44pxon touch and40x40pxon desktop where possible. Make sure extended hit areas never overlap. - Use
pointer-events: noneon decorative elements like glows and gradients so they never swallow clicks meant for control. - Put hover styling behind
@media (hover: hover). On touch,:hoversticks after a tap and looks selected. - Wrap motion in
@media (prefers-reduced-motion: no-preference)so it only plays for people who haven't asked to reduce it. - Use
role="status"for routine updates androle="alert"only for urgent errors. - For status changes add an icon, a label or an underline. Status changes should never use color alone.
- Make sure the skip-to-content link is the first focusable element and add
scroll-margin-topon anchored headings.
Layout
- The gap between groups is at least twice the gap inside one:
8pxwithin,16px+ between. - Use logical properties like
margin-inline-startandpadding-inline-endinstead of left and right. - Don't use fixed widths or heights on text containers.
Writing
- Start button labels with a verb: “Save draft” or “Delete project”, never “OK!” or a bare “Yes”.
- Repeat the consequence in confirmation buttons: “Delete project” next to “Cancel”.
- Pick one word per flow and keep it for every step: “Continue” or “Next”, never both.
- Describe the destination in link text: “Read docs”, never “Click here”.
- Capitalize buttons, headings and labels the same way everywhere. Sentence case is the safer default.
- Label toggles with the state they turn on: “Send read receipts”, never “Disable read receipts”.
- Orient the reader in empty states and offer one next action instead of “No results”.
- Address the reader as “you”, not “the user”.
interfaces.devinterface-cheat-sheet