Responsive UI Design
โš›๏ธ React 18+ ๐ŸŸข Chapter 33 of 39 ๐Ÿ“‚ Phase 14: Accessibility and UI Quality ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: Mobile-First Design ยท CSS Flexbox ยท CSS Grid ยท Media Queries ยท Responsive Navigation ยท Loading Skeletons ยท Empty/Error States ยท Dark Mode
A React app needs to work across phone, tablet, and desktop screens. This chapter covers the CSS techniques and component patterns that make an interface genuinely responsive, plus the polish states that make an app feel production-ready.
1Mobile-First Media Queries
/* Base styles apply to the SMALLEST screens by default */
.card {
  display: flex;
  flex-direction: column;
  padding: 16px;
}

/* Then progressively ADD styles for larger screens */
@media (min-width: 768px) {
  .card {
    flex-direction: row;
    padding: 24px;
  }
}

"Mobile-first" means writing your base CSS for the smallest screen, then layering on adjustments as the screen grows using min-width media queries โ€” generally more maintainable than starting from desktop and fighting to compress things down.

2Flexbox and Grid for Layout
๐Ÿ’ป Example 1: A Responsive Card Grid
CSS
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}

repeat(auto-fill, minmax(220px, 1fr)) is a genuinely powerful one-liner: it automatically fits as many 220px-minimum columns as will comfortably fit the container's current width, reflowing seamlessly from a single column on mobile to several on desktop โ€” with zero media queries needed for the grid itself.

3Responsive Navigation with Conditional Rendering
function NavBar() {
  const [isMenuOpen, setIsMenuOpen] = useState(false);

  return (
    <nav>
      <button
        className="hamburger"
        onClick={() => setIsMenuOpen(!isMenuOpen)}
        aria-label="Toggle menu"
      >
        โ˜ฐ
      </button>
      <div className={isMenuOpen ? "menu open" : "menu"}>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </div>
    </nav>
  );
}

This combines Chapter 12's useState with conditional class names (Chapter 6) โ€” the CSS itself handles hiding the hamburger button on desktop and the full menu on mobile via media queries, while React state tracks whether the mobile menu is currently open.

4Loading Skeletons, Empty States, and Dark Mode
function ProductCard({ product, loading }) {
  if (loading) {
    return <div className="skeleton-card"></div>;   // a gray pulsing placeholder shape
  }
  return <div className="card">{product.name}</div>;
}

// Dark mode via a data attribute + CSS variables
function ThemeToggle() {
  const [theme, setTheme] = useState("light");

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
  }, [theme]);

  return <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>Toggle</button>;
}

A skeleton (a gray placeholder shaped like the real content) feels noticeably smoother than a blank screen or spinner during loading (Chapter 21). Combined with genuinely designed empty and error states (Chapter 9), these small polish details are what separate a tutorial project from a production-quality one.

โš ๏ธ Designing Only for Desktop and 'Fixing' Mobile Afterward

Retrofitting a desktop-first layout to work on small screens usually results in cramped, awkward compromises. Starting with mobile-first CSS (as shown above) forces you to prioritize essential content from the beginning, and scaling up to larger screens is almost always easier than the reverse.

๐Ÿ’ป Hands-on Interactive Practice Challenge

Build a simple responsive card component using CSS Grid's auto-fill/minmax pattern that automatically reflows from one column to multiple as the viewport widens.

React Practice Challenge โ–ถ Run in Compiler
function ProductGrid({ products }) {
  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
      gap: "16px"
    }}>
      {products.map(p => (
        <div key={p.id} style={{ padding: "16px", border: "1px solid #ccc" }}>
          {p.name}
        </div>
      ))}
    </div>
  );
}
Run This Challenge in Online JS IDE โ†’
โ“ Frequently Asked Questions (FAQ)

Q Should I use a CSS framework like Tailwind, or write plain CSS?

Both are valid, common approaches in real React projects. Plain CSS (or CSS Modules) gives full control with no dependency; utility frameworks like Tailwind speed up development once you're comfortable with their class-naming conventions. Neither is 'more correct' โ€” it's a team/project preference.

Q What's the difference between a loading skeleton and a spinner?

Both indicate loading, but a skeleton mimics the actual shape of the content that's about to appear (cards, text lines), which research shows feels faster and less jarring to users than a generic spinner, since the layout doesn't suddenly shift once content loads.

OC
Written by Our Compiler Technical Editorial Team
Reviewed for accuracy & tested on React 18+ ยท Last updated August 2026