Interface Cheat Sheet
A mix of best practices and personal preferences that help build better interfaces.
User Interface
When nesting elements, make their border radius concentric. The outer radius equals the inner radius plus the padding between the elements.
Prioritize optical alignment over geometric alignment.
A button with text and an icon gets slightly smaller padding on the side of the icon.
Use a layered
box-shadowto give elements depth instead of using borders.Give images a
1pxoutline, offset by-1px: black at8%opacity in light mode, white at8%in dark mode.

Match the icon’s stroke to the text next to it.
Animation
Make elements animate from the trigger rather than the center. Set
transform-originbased on the trigger’s position.For menus people open often, skip the opening animation and only animate when they close.
Enter and exitNot recommendedExit onlyRecommendedRight-click in each areaMake exit animations more subtle than entrances. Move the element by a shorter distance as it fades out with
opacityand a4pxblur.Not recommendedRecommendedName the exact properties you want to animate in a transition. Never use
transition: all.Not recommendedtransition-allRecommendedtransition-colorsMake buttons scale down slightly when pressed. Use a scale between
0.95and0.98withtransition: scale 200ms ease-out.When icons swap, fade one out as the other fades in. Animate the new icon’s scale from
0.25to1, opacity from0to1and blur from4pxto0px. Reverse these for the old icon.Use CSS transitions for interactions so the animation can change direction halfway through. Use keyframes for sequences that only run once.
Disable all transitions when switching between light and dark mode.
If an element randomly shifts by 1–2px while animating, add
will-change: transformto it. This is especially useful in Safari on iOS.Hover meWhen elements enter, animate them in small groups with a short delay between each group instead of animating one giant block.
Build software that never breaksInterfere is the experience layer for modern product teams.Prevent elements from animating on page load, unless it’s intentional.
Keep frequent interactions instant or very fast, like an item changing color when you hover it.
Typography
Always use
.woff2font files on the web, never.ttfor.otf.FormatNameSizeUsage.woff2Web Open Font Format 2All modern browsersUse this.woffWeb Open Font FormatLegacy fallbackFallback.ttfTrueTypeDesktop and old browsersAvoid.otfOpenTypeDesktop and old browsersAvoidUse
font-variant-numeric: tabular-numsin timers, counters, prices and tables. It gives every digit the same width which prevents layout shifts when the values change. Skip it if you’re already using a monospace font.Keep lines in articles and other long text to 60–75 characters. Long form text that is wider than that is difficult to read.
Not recommendedLines that run the full width of a wide container, well past 75 characters.
RecommendedThe same text with its measure capped, so each line stays between 60 and 75 characters.
Use
text-wrap: balanceto even out headings andtext-wrap: prettyon descriptions to prevent orphaned words. Use neither for long form text.wrapDesigning interfaces that feel natural and intuitive
Great design is invisible. It guides users without them ever noticing.
Not recommendedbalance + prettyDesigning interfaces that feel natural and intuitive
Great design is invisible. It guides users without them ever noticing.
RecommendedWidth220pxKeep long words, links and IDs inside their container with
overflow-wrap: break-word. Usewhite-space: nowrapto prevent labels and badges from wrapping.Set
-webkit-font-smoothing: antialiasedand-moz-osx-font-smoothing: grayscaleon the root layout to make text appear sharper.Write text with normal capitalization. Use
text-transformwhen you want to display it in uppercase or lowercase.Typographic detailnoneTypographic detailuppercaseTypographic detaillowercaseTypographic detailcapitalizeUse smart punctuation: curly quotes, an en dash (–) for ranges, an em dash (—) for asides and an ellipsis (…) instead of three dots.
"web type"“web type”Quotes2010-20202010–2020En dashtype - cleartype—clearEm dashloading...loading…EllipsisKeep underlines from crossing the tails of letters like g and y. Use
text-underline-position: from-fontwithtext-decoration-skip-ink: auto.Typography enjoys gravityWhen you shorten text with an ellipsis, let people read the full text in a tooltip or an expanded view.
Designing interfaces that feel natural and intuitive for everyone who uses them.
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.
gray-100page backgroundgray-200gray-300component hovergray-400gray-500bordergray-600gray-700gray-800gray-900solid fillgray-1000gray-1100gray-1200body textComponents 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.Name color tokens by purpose:
--color-accent-solidinstead of--color-blue-buttonor--color-sidebar-gray.--color-accent-solidRecommendedThe name still makes sense when the color changes.
--color-blue-buttonNot recommendedIt's called 'blue' even though the color is now violet.
--color-sidebar-grayNot recommendedChanging the sidebar color now changes it across the app.
Reserve
accentfor the brand color soprimarynever means both the brand and the main body text.Measure contrast against the background directly behind an element.
This text renders on the surface, not the page.
page 1.3:1Not recommendedsurface 4.8:1RecommendedText lightness90%Create a separate color palette for dark mode instead of inverting the light mode palette.
Choose one way to switch themes:
prefers-color-schemeor a.darkclass. Don’t mix both.You can define how colors blend in a gradient. Use
in oklabfor even brightness,in oklchfor more vivid colors in the middle orin srgbfor more muted midtones.
Accessibility
Use native HTML elements:
<button>for buttons and<a>for links, for example. Native elements already have a lot of accessibility and other expected behavior built in.Try a middle click, a right click and a Cmd-click on each oneStyle
:focus-visibleinstead of:focus. Don’t remove the outline without a replacement.Press Tab to move focus through the buttonsOnly use
tabindex="0"andtabindex="-1". Positive values change the expected order of the elements.Click in and press Tab to cycle through itemsGive buttons that only have an icon a descriptive
aria-label. Never putaria-hidden="true"on an element that can be focused.Write alternative text that explains the image’s purpose and what it shows. Give decorative images
alt="".Give every input a visible label using
<label>. Set itstypeandinputmodeto match what people should enter.Never block paste. People paste in things like passwords and one-time codes.
Copy the code and paste it into both fieldsKeep the submit button enabled until the request starts. Check for errors when people submit. Mark invalid fields with
aria-invalid="true", connect each error message witharia-describedbyand move focus to the first invalid field.Make hit areas at least
24x24px. Aim for44x44pxon touch screens and40x40pxon desktop where possible. Make sure they never overlap.Use
pointer-events: noneon decorative elements like glows and gradients so events are never swallowed.Presses: 0
Put hover styles inside
@media (hover: hover). On touch screens,:hoverstays active after a tap and makes an item look selected.Respect people’s reduced motion setting. Put animations inside
@media (prefers-reduced-motion: no-preference)so they only play for people who don’t have motion reduced.Use
role="status"to announce routine updates to screen readers. Saverole="alert"for urgent errors.Never use color alone to show a status change. Add an icon, a label or an underline too.
Layout
Use
scroll-margin-topto leave space above headings when people follow links to them.Click Pricing in each headerLeave at least twice as much space between groups as between items in a group. For example, use
8pxbetween items and16pxor more between groups.
Writing
Start button labels with a verb: “Save draft” or “Delete project”, never “OK!” or a bare “Yes”.
Make confirmation buttons say what will happen: “Delete project” next to “Cancel”.
Use the same label to move to the next step throughout a flow. Choose “Continue” or “Next” and use it for every step.
Describe where a link goes.
Capitalize buttons, headings and labels the same way everywhere. Sentence case, like “Save changes”, is the safer default.
Label toggles with what happens when they’re on: “Send read receipts”, not “Disable read receipts”.
When a view is empty, explain what belongs there and give people one action to get started instead of nothing.
Address the reader as “you”, not “the user”.

