Front end best practices (framework-agnostic)
Apply these when implementing or reviewing user interfaces. Map concepts to the project's stack (utility CSS, design tokens, component primitives).
Layout and spacing
- Prefer design-system spacing (scale/tokens) or utility classes over arbitrary pixel values when equivalents exist.
- Flexbox for alignment and distribution: wrap, center on both axes, consistent gaps (
gapin CSS; many stacks exposegap-*utilities). - Consistent rhythm: outer containers slightly looser padding than nested sections; dense regions (toolbars, tags) tighter.
- Grouped actions: horizontal button groups use uniform spacing (e.g. one gap token) instead of mixed margins.
Actions and buttons
- Primary action on the right in left-to-right locales (Save, Submit, Continue); secondary actions (Cancel, Back) to the left.
- Destructive actions (Delete, Remove): use a secondary / outline / ghost treatment, not the same prominence as primary. Place them left of the primary when both appear in one row.
- Loading / disabled: disable or show in-button progress while the primary action runs; avoid double submission.
Modal vs persistent panel
- Modal (dialog): short, blocking flow — confirm/cancel, errors, small single-purpose forms. User must resolve or dismiss; focus is trapped while open.
- Panel / sheet / drawer: longer or multi-step work on a selected item while list or parent context stays visible. Often slides from an edge; dismissible with close control and Escape where appropriate.
Choose modal for "must decide now"; choose panel when context preservation matters.
Accessibility
- Contrast: target WCAG AA — roughly 4.5:1 for normal text, 3:1 for large text (about 18pt+ or 14pt+ bold). Verify with a contrast checker.
- Keyboard: every control reachable with Tab / Shift+Tab; visible focus styles; logical tab order (avoid positive
tabindexexcept rare cases). - Semantics: use native interactive elements (
button,awithhref,input,select) or components that expose the same roles and keyboard behavior. Avoid clickablediv/spanunless they have explicit role,tabindex, and key handlers. - Icon-only controls: provide an accessible name (
aria-labelor visually hidden text); pair with tooltips only as enhancement, not as the sole label. - Live regions: use
aria-live(polite/assertive) for async status ("Saving…", "Saved", errors) when content updates without moving focus.
Loading, empty, and error states
- Reserve space — skeletons, min-height containers, or stable layout so content swap does not shift the page jarringly.
- Minimum loader visibility — very fast responses can flash spinners; optional short minimum display (e.g. ~300–400ms) if the product cares about perceived stability (use sparingly; do not delay real feedback).
- Motion — prefer short opacity/transform transitions for enter/leave; respect
prefers-reduced-motion: reduce or disable non-essential animation.
Forms and validation
- Associate labels with controls (
for/idor wrapping label); surface errors inline near fields and summarize at the top for long forms. - Do not rely on color alone for errors; use text and icons.
- Autofocus sparingly (e.g. single-field search or modal primary field); avoid trapping users unexpectedly.
Performance and resilience
- Images: appropriate formats, dimensions, and
loading="lazy"for below-the-fold content where supported. - Lists: virtualize or paginate large collections; avoid rendering thousands of DOM nodes at once.
- Network: optimistic UI only when rollback is clear; show failure states and retries for critical actions.
Quick checklist
- Primary action right; cancel/secondary left; destructive de-emphasized.
- Spacing and layout use tokens/utilities; gaps consistent in toolbars and button rows.
- Modal vs panel choice matches task length and need for surrounding context.
- Focus order, labels, contrast, and icon-only names covered.
- Loading and empty states avoid layout jump; motion respects reduced-motion.
- Forms: labels, errors, no color-only status.