Cheat sheet
Rules of thumb I design and build by, each with a live example.
Interface
Long reads: How to nest rounded corners, Why centered icons look off-center and Shadows vs borders for depth.
- Align things by eye, not by the math. A play icon centered geometrically reads as off; nudge it until it looks centered.Not recommendedRecommended
- Nested corners should share a center. Inner radius = outer radius minus the padding between them, otherwise the corners fight each other.Not recommendedRecommended
- Reach for a stacked
box-shadowbefore a border. A hairline plus a soft drop gives depth a flat 1px line never will. - An icon’s stroke should weigh the same as the text beside it. A 1px icon next to 14px medium text looks thin; 1.5px matches.Not recommendedRecommended
- When a button carries an icon, trim the padding on the icon side. Icons bring visual air of their own, so equal padding looks lopsided.Not recommendedRecommended
- Photos and thumbnails get a
1pxinset outline: black at8%on light backgrounds, white at8%on dark. It keeps bright images from bleeding into the page.
Motion
Long reads: How fast UI animations should be, Animating entrances and exits and CSS transitions vs keyframes.
- Let buttons give a little when pressed: scale to
0.95to0.98withtransition: scale 200ms ease-out.Not recommendedRecommended - Nothing should animate on page load unless you mean it to. A toast that slides in on every refresh is a bug, not delight.Draft savedNot recommendedDraft savedRecommended
- Popovers should grow out of the thing that opened them. Set
transform-originto the trigger’s corner, not the center. - Exits are quieter than entrances. On the way out, move a shorter distance, fade the
opacityand add a4pxblur.Not recommendedRecommended - When one icon replaces another, cross-fade them: the new one scales from
0.25to1, opacity0to1, blur4pxto0px. Mirror it for the outgoing icon. - Transitions can reverse mid-flight, keyframes cannot. Use transitions for anything a person can toggle back; keep keyframes for one-shot sequences.Not recommendedRecommended
- List the properties you actually animate.
transition: allcatches things you did not intend, and it costs more.Not recommendedtransition-allRecommendedtransition-colors - Bring content in as a few small groups with a short delay between them. One giant block sliding in reads as heavy.Ship interfaces that feel finishedNotes on the small decisions that make a product feel considered.
- Everyday interactions stay instant or close to it. A hover color change should not take half a second.
- Menus people open dozens of times a day should appear instantly and only animate on the way out.Right-click in each areaEnter and exitNot recommendedExit onlyRecommended
- If something jitters by a pixel while it moves, give it
will-change: transform. Safari on iOS is the usual culprit.Hover me - Turn every transition off while switching themes. Otherwise each element changes color on its own schedule.AppearanceSyncs with your system.Not recommendedSaveAppearanceSyncs with your system.RecommendedSave
Type
The long read: CSS typography for interfaces.
- Balance headings with
text-wrap: balanceand tidy descriptions withtext-wrap: prettyso no word is left alone on the last line. Skip both for long articles.wrapInterfaces that feel obvious the first time
Good motion explains itself. Nobody should have to notice it.
Not recommendedbalance + prettyInterfaces that feel obvious the first time
Good motion explains itself. Nobody should have to notice it.
RecommendedWidth220px - Numbers that change, such as timers, counters and prices, need
font-variant-numeric: tabular-nums. Every digit gets the same width, so nothing shifts as the value ticks. Monospace fonts already do this.1000Not recommended1000Recommended - Serve fonts as
.woff2only..ttfand.otfare desktop formats and download far heavier.FormatNameSizeUsage.woff2Web Open Font Format 2All modern browsersUse this.woffWeb Open Font FormatLegacy fallbackFallback.ttfTrueTypeDesktop and old browsersAvoid.otfOpenTypeDesktop and old browsersAvoid - Cap reading text at roughly 60 to 75 characters per line. Any wider and the eye loses its place on the way back.Not recommended
Lines 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.
- Underlines should not slice through descenders. Set
text-underline-position: from-fontandtext-decoration-skip-ink: auto.Gravity pulls every glyph - Long tokens, links and IDs need
overflow-wrap: break-wordso they stay inside their box. Badges and labels getwhite-space: nowrapso they never break in half.break-wordNot recommendedYour token tk_7Qm4xRz9vL2pWd8nKcYb3sH is live.
RecommendedYour token tk_7Qm4xRz9vL2pWd8nKcYb3sH is live.
nowrapNot recommendedUpdated 3 min agoRecommendedUpdated 3 min ago - If you cut text with an ellipsis, offer the full text somewhere: a tooltip, or a way to expand it.
Interfaces that feel obvious the first time, for everyone who opens them.
- Set
-webkit-font-smoothing: antialiasedand-moz-osx-font-smoothing: grayscaleonce at the root. Text comes out crisper and lighter on macOS.Subpixel text
macOS smooths text with subpixel antialiasing by default.
Antialiased text
Grayscale antialiasing renders lighter, crisper text.
- Write copy in sentence case and let
text-transformhandle uppercase or lowercase when the design calls for it. The source text stays readable.Considered detailsnoneConsidered detailsuppercaseConsidered detailslowercaseConsidered detailscapitalize - Use real typographic marks: curly quotes, an en dash (–) for ranges, an em dash (—) for asides and a single ellipsis (…) rather than three periods."good type"“good type”Quotes1999-20041999–2004En dashfast - simplefast—simpleEm dashsaving...saving…Ellipsis
Color
The long read: Structuring color tokens.
- Components consume semantic tokens like
--color-text-secondary, never raw primitives like--green-500. The primitive is a value; the token is a job.Not recommended--green-500Recommended--green-500--color-accent-solid - Name tokens after their role, not their color:
--color-accent-solidstill makes sense after a rebrand,--color-green-buttondoes not.--color-accent-solidRecommendedStill true after a rebrand, whatever the color becomes.
--color-green-buttonNot recommendedSays green, renders violet. The name lied the day the brand changed.
--color-panel-grayNot recommendedRetint one panel and every surface that borrowed the token moves with it.
- Every step of a gray ramp should earn its place: page background, hover, border, solid fill, body text. Delete the steps nothing uses.gray-100page backgroundgray-200gray-300component hovergray-400gray-500bordergray-600gray-700gray-800gray-900solid fillgray-1000gray-1100gray-1200body text
- Check contrast against the surface the text actually sits on, not the page behind it. Same text, different ratio.
This label sits on a card, not on the page.
page 1.3:1Not recommendedsurface 4.8:1RecommendedText lightness90% - Call the brand color
accent. Ifprimarymeans both the brand and the darkest text, nobody knows which one you mean.- heading
- --color-primary
- body
- --color-secondary
- button
- --color-primary
- heading
- --color-text-primary
- body
- --color-text-secondary
- button
- --color-accent-solid
- Design a dark palette on its own terms. Inverting the light one gives you pure black and glaring white.Not recommendedMonthly notesOne email, once a month.FollowRecommendedMonthly notesOne email, once a month.Follow
- Pick a single switch for themes,
prefers-color-schemeor a.darkclass, and apply it to every token. Mixing them leaves half the UI in the wrong mode.Not recommendedsurface: .dark · text: media queryWorkspaceSigned in as Mina.Recommendedall tokens: .darkWorkspaceSigned in as Mina. - Gradients can interpolate in different color spaces:
in oklabfor even brightness,in oklchfor livelier midtones,in srgbfor the softer, muddier blend.
Accessibility
The long read: Accessibility basics for interfaces.
- Style
:focus-visible, not:focus, and never strip the outline without putting something in its place.Press Tab to move focus through the buttonsNot recommendednoneRecommendeddefaultRecommendedcustom - Prefer the native element: a
<button>for actions, an<a>for navigation. Middle-click, right-click and keyboard support come for free.Not recommended<div>This side is the same link built from a div with a click handler. Without an href the browser cannot open it in a new tab, offer it in the right-click menu, preview its address on hover or list it as a link.
Recommended<a>Try a middle click, a right click and a Cmd-click on each one - Targets should be at least
24x24px, ideally40x40pxon desktop and44x44pxon touch, and they must never overlap.Not recommendedRecommended - Every field gets a visible
<label>. Matchtypeandinputmodeto the data so phones open the right keyboard.This field only has a placeholder, which disappears as soon as you type and its text type brings up the generic keyboard on touch devices.
Not recommendedRecommended - Leave the submit button enabled until the request starts, then validate. Flag fields with
aria-invalid="true", link the message witharia-describedbyand move focus to the first error. - Color cannot be the only signal that something changed. Pair it with an icon, a message or an underline.Not recommendedRecommended
Enter a valid email address
- Icon-only buttons need a real
aria-label. And never putaria-hidden="true"on anything that can take focus.Not recommendedRecommended - Alt text should say what the image is for and what it shows. Purely decorative images get
alt="".Not recommended
alt="image"Recommended
alt="A focus ring following the rounded corner of a card" - Stick to
tabindex="0"andtabindex="-1". Positive values reorder focus in ways nobody expects.Not recommendedTab through the buttons to record the order focus visits them.
RecommendedTab through the buttons to record the order focus visits them.
Click in and press Tab to cycle through items - Do not block paste. Passwords and one-time codes arrive by paste all the time.Copy the code and paste it into both fieldsNot recommendedRecommended
- A skip-to-content link should be the very first thing Tab lands on.Skip to content
Main content
- Honor reduced motion. Wrap animations in
@media (prefers-reduced-motion: no-preference)so they only run for people who want them.Not recommendedRecommended - Wrap hover styles in
@media (hover: hover). On touch screens a tapped item otherwise stays lit as if selected.Not recommendedRecommended - Glows, gradients and other decoration get
pointer-events: noneso they never eat a click meant for the control underneath.Clicks: 0
- Announce routine confirmations with
role="status". Keeprole="alert"for errors that need attention now.
Layout
- Space between groups should be at least double the space inside them:
8pxbetween items,16pxor more between groups.AlertsNot recommendedEmailPushSharingPublic profileRead receiptsRecommendedAlertsEmailPushSharingPublic profileRead receipts - Give headings
scroll-margin-topso a sticky header never covers them after an anchor jump.Click Roadmap in each headerNot recommendedRoadmap
RecommendedRoadmap
Copy
The long read: Writing clear UI copy.
- Buttons start with a verb: “Save changes”, “Remove workspace”. Never “OK!” or a lone “Yes”.Not recommended
Keep these edits?
This draft has edits you have not saved.
RecommendedKeep these edits?
This draft has edits you have not saved.
- Use one word to advance through a flow. Pick “Continue” or “Next” and keep it on every step.Not recommendedStep 1 of 3Step 2 of 3Step 3 of 3RecommendedStep 1 of 3Step 2 of 3Step 3 of 3
- A confirmation button names the outcome: “Remove workspace” beside “Cancel”, not “Yes” beside “No”.Not recommended
Remove this workspace?
This removes the workspace and everything in it.
RecommendedRemove this workspace?
This removes the workspace and everything in it.
- Keep capitalization consistent across buttons, headings and labels. Sentence case, “Save changes”, is the safest default.Not recommended
PREFERENCES
Pick Where Alerts Go.
Email AddressRecommendedPreferences
Pick where alerts go.
Email address - Name a toggle after what happens when it is on: “Show previews”, not “Turn off previews”.Not recommendedTurn off previewsRecommendedShow previews
- An empty view should say what belongs there and offer one way to start, instead of a bare “Nothing here”.
Nothing here.
Not recommendedRecommendedNo boards yet
Boards collect your screens, notes and links in one place.
- Link text should say where the link goes. “Read the changelog”, not “Click here”.
Click here for the details.
Not recommendedRead the changelog for the details.
Recommended - Talk to the reader as “you”, never as “the user”.
The user will get a confirmation.
Not recommendedYou'll get a confirmation.
Recommended
The live examples follow the ones on interfaces.dev, rebuilt by hand as a study in interaction detail.