Interface Guidelines

A working document for the websites, web apps, browser extensions, and visual experiments I design and build. These are current defaults not commandments and each should help make a real decision while building.

Layout

  • Keep reading surfaces at a comfortable line length. Give images, tables, and code room when they need more width.
  • Let content wrap and resize naturally before adding JavaScript measurements or fixed heights.
  • Design with an unusually long title, an empty list, and a crowded list. A layout should survive real content.
  • Check the composition at narrow, ordinary, and wide viewport sizes, including browser zoom.
  • Align optically, not only geometrically. Icons and asymmetric shapes often look off-centre when they are mathematically centred; nudge them with margin or padding until they look right. Check Optical adjustments.

Typography

  • Define a small set of type-scale with semantic names so its intuitive: display, heading, body, label
  • Long form reading line length should be in the range 65ch-70ch
  • Scale type with clamp() so sizes respond to the viewport without a pile of breakpoints.
  • Set text-wrap: pretty globally for nicer line breaks and fewer orphans. Use text-wrap: balance for headings, and fix the rare case where an orphan is actually wanted.
  • Apply -webkit-font-smoothing: antialiased (antialiased in Tailwind) so text renders slightly thinner and crisper.
  • Tighten letter-spacing on headings, loosen it on small uppercase labels and leave body text alone
  • Use tabular-nums anywhere numbers change or need to line up
  • Keep line-height tight on headings on larger sizes and relaxed on paragraphs
  • Dont use font-size less than 12px
  • Font weight should have minimum weight of 400
  • Avoid changing font weight between resting and interactive states when it moves nearby content.
  • Use tabular numerals (font-variant-numeric: tabular-nums) for values that are dynamic
  • Truncate text in cases of overflow. Provide option to view the full text where required

Design

  • Let the importance of the content determine its visual weight. A decorative element should have a reason to take up space.
  • Use familiar and traditional patterns for frequent tasks. Spend visual novelty where it makes the experience more expressive or easier to understand.
  • Remove an element when it cannot explain something, help someone act, or create a deliberate feeling.
  • Give a colour a consistent job within a project, such as action, selection, warning, or decoration.
  • Check text, borders, and controls against the surfaces where they actually appear, in every supported theme.
  • Keep gradients, textures, shadows, and blur from competing with important text or controls.
  • Test selected text, focus, hover, disabled, and error states as part of the palette.
  • Reference colours through variables from the CSS file. Never hard-code an arbitrary colour value in a component.
  • Smooth shadows and rings on UI components by layering several soft shadows instead of one hard one.

Border and corner radius

  • Make border radius responsive, so corners scale with the size of the element and the viewport.
  • Prefer squircles (corner-shape: squircle) for most surfaces, with a regular border-radius as the fallback where it isn’t supported.
  • Without squircles, keep nested corners concentric: outer radius = inner radius + padding.
  • With squircles, use the same corner shape on both the inner and outer element and start from the same formula, then check it by eye. Squircle curves aren’t exactly concentric, so the result may need an optical nudge.

Interactivity

  • Let controls respond at the point of interaction. A copied value can show success beside the copy button.
  • Use cursor: pointer on every element that responds to a click.
  • Distinguish available, busy, selected, and unavailable states without making people guess.
  • Preserve expected browser actions: Back, Forward, refresh, opening a link in a new tab, and restoring scroll where appropriate.
  • Let Escape close the topmost temporary layer, such as a dialog or command menu, when doing so is safe.
  • Put shortcuts where they help repeated work; reveal them in the interface and avoid conflicts with system, browser, or host-page shortcuts.
  • Design the state before data arrives, while it loads, when it succeeds, when it is empty, and when it fails.
  • Show feedback close to the thing that caused it, and keep it visible long enough to register.
  • Use optimistic updates when the result is likely and the action can be reconciled. Restore the previous state and explain a failure.
  • Make a blank screen useful: show what belongs there and how to create, import, or find the first item.
  • Do not rely on hover to reveal an essential action or explanation.
  • Avoid opening the mobile keyboard automatically unless typing is clearly the next intended step.

Forms

  • Give every input a persistent name. A placeholder may show an example, but it should not carry the entire label.
  • Use the right input type, keyboard mode, and autocomplete value for the information requested.
  • Keep native behaviours that help people: paste, autofill, selection, Enter to submit, and password managers.
  • Show a field error near the field and explain how to fix it. On submit, direct attention to the first error.
  • Preserve entered values after validation or network errors. Warn before leaving only when meaningful work would be lost.
  • During submission, show progress and prevent duplicate requests without erasing the action label.

Motion

  • Use motion to explain a change of place, state, or relationship, or to add a deliberate moment of character.
  • Keep frequent interactions responsive. A flourish should not delay the next action.
  • Keep interaction animations between 120ms and 180ms. Anything faster or slower needs a clear justification.
  • Use percentages for animated values wherever possible, such as translateY(100%). They adapt to the element’s size and are less error-prone than fixed pixels.
  • Match direction and origin to the trigger: a menu should feel connected to the control that opened it.
  • Make animations interruptible. Repeated input should update the interface rather than queue a performance.
  • Don’t add bounce to spring animations by default. Use it only when the movement carries momentum, such as releasing a drag, where a force caused the motion and an overshoot feels earned.
  • Keep the hit area still while the element animates. Listen for hover on a stable parent and animate a child, so a moving or scaling element can’t slip out from under the cursor and make the hover state flicker.
  • Honour prefers-reduced-motion. Provide a reduced-motion variant that preserves meaning and function.
  • Use will-change on elements that are about to animate, such as will-change: transform, so the browser can prepare ahead of time. Apply it only where it’s needed; every hint costs memory.
  • Never use transition: all (transition-all in Tailwind). List the exact properties that should animate, so unrelated changes don’t animate by accident.

Accessibility

  • Style focus with :focus-visible, so keyboard users get a clear ring and mouse clicks don’t leave one behind. Never remove the default outline without a replacement.
  • Check focus ring contrast against every surface it can land on, in both themes.
  • Name controls by what they do, not how they look: an icon button’s aria-label should say “Close”, not “X icon”.
  • Trap focus inside dialogs, make the page behind them inert, and return focus to the trigger when they close.
  • Use one h1 and headings in order, without skipping levels, so screen reader users can navigate by heading.
  • Meet contrast minimums: 4.5:1 for body text, 3:1 for large text, icons and control borders.
  • Test at browser zoom and with larger text. Content and controls should remain available without horizontal hunting.

Copy

  • Name actions for their result: “Save changes” is clearer than “Continue” when saving is what happens.
  • The starting letter of a second word in the CTA should be smallcase. "Save changes" and not "Save Changes"
  • Put short explanations near the decision they support. Use a tooltip for extra context, not essential instructions.
  • Give people a route forward from every empty, error, or permission state.
  • Check dates, numbers, currencies, and text expansion for the people the project is meant to serve.

Performance

  • Treat responsiveness as part of the design. Typing, scrolling, and pressing a control should remain smooth while work happens.
  • Reserve the main thread for interaction when an expensive task can run elsewhere or be broken into smaller steps.
  • Load only what the current view needs; defer heavy media and work below the fold.
  • Make images and videos fit their containers without shifting the page after load.
  • Test slow networks, failed requests, and less powerful devices before calling a flow finished.