Skip to main content

Command Palette

Search for a command to run...

Practical CSS Tips for Better UX and Accessibility

Updated
4 min read
Practical CSS Tips for Better UX and Accessibility

Focus & Interaction

1. Use :focus-visible for keyboard-only focus outlines

/* Outline is shown only for keyboard-only focus */
button:focus-visible {
  outline: 2px solid blue;
  outline-offset: 2px;
}

2. Never remove outlines without replacement

/* Bad */
*:focus { outline: none; }

/* Good */
*:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

3. Use prefers-reduced-motion for animations

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Color & Contrast

4. Ensure sufficient color contrast

  • Text: minimum 4.5:1 ratio (WCAG AA)

  • Large text (18pt+): minimum 3:1 ratio

  • Use tools like WebAIM contrast checker

5. Don't rely on color alone

Make sure there is text or other clear indications to convey the message.

6. Use currentColor for flexible theming

.icon {
  border: 2px solid currentColor; /* Inherits text color */
}

Typography

7. Use relative units for font sizes

/* Respects user's browser font size preferences */
body { font-size: 1rem; }
h1 { font-size: 2rem; }

8. Set comfortable line heights

body {
  line-height: 1.5; /* WCAG recommends 1.5 minimum for body text */
}

9. Limit line length for readability

p {
  max-width: 65ch; /* Optimal: 45-75 characters per line */
}

Responsive Design

10. Use clamp() for fluid typography

h1 {
  /* Minimum: 1.5rem (24px) - won't go smaller, even on tiny screens
     Preferred: 5vw (5% of viewport width) - scales with screen size
     Maximum: 3rem (48px) - won't go larger, even on huge screens
  */
  font-size: clamp(1.5rem, 5vw, 3rem);
}

11. Make tap targets large enough

button, a {
  min-height: 44px; /* Apple & Google recommend 44-48px */
  min-width: 44px;
}

12. Use gap instead of margins in flex/grid

.container {
  display: flex;
  gap: 1rem; /* Cleaner than margin hacks */
}

Visibility & Display

13. Add a custom class to hide content for screen readers only

/* Screen reader accessible but visually hidden */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

14. Avoid display: none for focusable elements

/* Bad - removes from tab order */
.hidden { display: none; }

/* Good - visually hidden but still accessible */
.visually-hidden { /* use sr-only class above */ }

Forms

15. Style form validation states clearly

input:invalid {
  border-color: red;
}
input:valid {
  border-color: green;
}
/* But only after interaction */
input:not(:placeholder-shown):invalid {
  border-color: red;
}

16. Increase input padding for better touch targets

input, textarea, select {
  padding: 0.75rem 1rem;
  font-size: 1rem; /* Prevents zoom on iOS */
}

17. Style disabled states obviously

button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

Layout & Spacing

18. Use logical properties for internationalization

/* Instead of margin-left */
margin-inline-start: 1rem; /* Works for RTL languages */

19. Prefer padding over height for vertical spacing

/* More flexible and accessible */
button {
  padding: 0.75rem 1.5rem;
  /* Not: height: 40px; */
}

20. Use aspect-ratio for responsive media

.video-container {
  aspect-ratio: 16 / 9; /* Modern browsers */
  width: 100%;

  /* Fallback for older browsers */
  @supports not (aspect-ratio: 16 / 9) {
    padding-bottom: 56.25%;
  }
}

Dark Mode

21. Support dark mode preferences

@media (prefers-color-scheme: dark) {
  body {
    background: #1a1a1a;
    color: #f0f0f0;
  }
}

Performance

22. Use content-visibility for long lists

.list-item {
  content-visibility: auto;
  contain-intrinsic-size: 0 200px;
}

23. Optimize animations with will-change

.animated {
  will-change: transform; /* Use sparingly */
}

Misc

24. Use pointer-events carefully

/* Be cautious - affects accessibility */
.overlay {
  pointer-events: none;
}

If you need to disable all user interactions on an element, consider using inert attribute:

  • Removes elements from tab order

  • Prevents clicks

  • Hides from screen readers

  • Can be toggled with JavaScript

// Toggle overlay
overlay.inert = false; // Make interactive
overlay.inert = true;  // Make non-interactive

25. Add :has() for parent-based styling

/* Style form if it has invalid input */
form:has(input:invalid) {
  border: 2px solid red;
}

26. Use scroll-margin-top for anchor links

:target {
  scroll-margin-top: 80px; /* Accounts for fixed header */
}

27. Smooth scrolling (with motion preference)

html {
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

These tips will help you build more accessible, user-friendly interfaces that work well for everyone, regardless of their device, abilities, or preferences.

More from this blog

UI Dev

18 posts