UI Styling
UI styling shapes the storefront’s look and feel, including layout, spacing, colors, typography, and interactive states. It provides a consistent, accessible, and responsive shopping experience for your storefront across devices.
Storefront Next uses Tailwind CSS (v4) for utility-first styling and shadcn/ui patterns, and Radix UI for headless accessible components.
Here are the Tailwind-related dependencies in this package.
| Dependency | Version | Purpose |
|---|---|---|
| tailwindcss | 4.x.x+ | Utility-first CSS framework |
| @radix-ui/* | Various | Headless UI primitives |
| class-variance-authority | 0.7.1 | Component variant management |
| clsx | 2.1.1 | Conditional class composition |
| tailwind-merge | 3.4.0 | Tailwind class conflict resolution |
The global and theme styles are in src/theme/ only. The entry point is src/theme/index.css, with tokens split across src/theme/tokens/, base resets in src/theme/base.css, and component overrides in src/theme/overrides/.
Components are stored in src/components/ui/ and follow the shadcn/ui pattern.
- Built on Radix UI primitives for accessibility.
- Styled with Tailwind utility classes.
- Variants managed with
class-variance-authority(cva). - Classes composed using
cn()utility (clsx + tailwind-merge).
A helper function that combines clsx for conditional classes with tailwind-merge to resolve conflicting utilities.
Style patterns for components include variant-based components with CVA, compound components, and direct utility class usage.
With CVA, you can create variants of an existing component by extending its Tailwind classes without overriding them. Here’s a minimal example.
Multi-part components are organized as separate functions:
Feature components apply Tailwind classes directly.
Tailwind uses prefix-based responsive modifiers.
Hardcoded Tailwind color utilities like bg-red and text-green are blocked via an ESLint rule. Use semantic tokens (for example, bg-primary, text-foreground) or CSS variable classes instead.
Tailwind’s utility-first approach means most styling lives inline in JSX. Before extracting a reusable abstraction, read the official guide on managing reuse — it covers multi-cursor editing, loops, and component extraction as the preferred strategies before reaching for CSS abstractions.
Use a React component (the default choice) when:
- The pattern involves markup structure—multiple elements, slots, children
- There is logic, state, or event handling
- It accepts props that change behavior or content
- It composes other components (shadcn, Radix, etc.)
Use a CSS component class (@layer components in src/theme/base.css) only when:
- The pattern is pure layout/styling—padding, max-width, centering, typography presets
- There is no logic, state, or props—just a bag of CSS properties
- It needs to be applied to many different HTML elements across the codebase (divs, sections, wrappers)
- Utilities need to override it in specific contexts (the components layer is lower specificity than utilities)
Example: section-container—consolidates px-4 sm:px-8 lg:px-16 max-w-screen-2xl mx-auto into one class, used by 30+ files. A page can add max-w-4xl alongside it and the utility wins.
Rule of thumb: if you can express it as a single className string with no JSX children, it’s a CSS class. If it renders elements or accepts props, it’s a React component.
Don’t use @utility for multi-property compositions that need to be overridable. The utility layer has the highest specificity, so any override attempt (e.g., adding max-w-4xl alongside a @utility class) would lose. Use @layer components instead.