This article explains how configure and use Tailwind CSS to build a consistent design system in a webapp.
This approach is used in the project https://github.com/kolok/brico/, note : Parcel is used to compile JS and CSS files which are stored in static/to_compile folder.
Overview
The design system is built using Tailwind CSS with a custom configuration that extends Tailwind’s default theme with project-specific design tokens. The approach combines:
- Custom theme configuration in
tailwind.config.jsfor design tokens (colors, spacing, etc.) - Component-based CSS modules in
static/to_compile/css/using Tailwind’s@applydirective - Direct utility classes in templates when component classes aren’t sufficient
Configuration
Theme Customization
The tailwind.config.js file defines a custom color palette that serves as the foundation of the design system:
theme: {
colors: {
// Layout colors
bg_layout: "#96B9D4", // Header, sidebar, footer background
bg_content: "#EDF5FB", // Main content background
// Text and borders
border: "#9E9E9E", // Input borders, dividers
content: "#424242", // Main content text color
// Link colors
link: "#0066cc",
hover_link: "#0052a3",
active_link: "#0052a3",
// Navigation link colors
nav_link: "#424242",
nav_hover_link: "#212121",
nav_active_link: "#212121",
// Primary button colors
primary: "#2563eb",
primary_hover: "#1d4ed8",
primary_text: "white",
primary_hover_text: "#F5F5F5",
// Secondary button colors
secondary: "#F7FAFD",
secondary_hover: "#EDF5FB",
secondary_text: "#424242",
secondary_hover_text: "#212121",
// Danger/error colors
danger: "#DC2626",
danger_hover: "#B91C1C",
danger_text: "white",
danger_hover_text: "#F5F5F5",
// Table/list alternating colors
odd: "#E8F0F9",
even: "#E4EBF9",
// Additional semantic colors
info: "#000091",
dark: "#3c3d40",
// ... standard colors
}
}
Key Configuration Options
preflight: false: Tailwind’s base styles are disabled to avoid conflicts with existing stylescontent: Configured to scan templates and TypeScript/JavaScript files for class usageplugins: Includestailwindcss-animatefor animation utilities
module.exports = {
content: ["templates/**/*.html", "static/to_compile/**/*.{js,ts,svg}"],
corePlugins: {
preflight: false,
},
theme: {
colors: {
…
},
},
plugins: [require("tailwindcss-animate")],
}
Architecture
CSS Module Structure
The CSS is organized into modular files, each responsible for a specific component or area:
static/to_compile/css/
├── base.css # Base styles and imports
├── buttons.css # Button components
├── forms.css # Form inputs and labels
├── cards.css # Card components
├── dropdown.css # Dropdown menus
├── headers.css # Header navigation
├── footers.css # Footer styles
├── links.css # Link styles
├── tiles.css # Tile/list components
├── blocks.css # Content blocks
├── page-headers.css # Page heading styles
├── danger-zone.css # Danger/error zones
├── markdown-zone.css # Markdown content areas
├── signup-login.css # Authentication pages
└── main.css # Main layout styles
Base Styles (base.css)
The base.css file serves as the entry point:
- Imports all component modules in the correct order
- Defines global base styles using
@apply:
body: Sets font, background, and text colorp: Sets paragraph spacing
Component Pattern
Components are built using Tailwind’s @apply directive, which allows you to compose utility classes into reusable CSS classes:
/* Example from buttons.css */
.btn-primary {
@apply bg-primary text-primary_text border-border
hover:bg-primary_hover hover:text-primary_hover_text
transition;
}
This pattern provides:
- Consistency: All buttons share the same base styles
- Maintainability: Changes to button styles happen in one place
- Flexibility: Can still use utility classes directly when needed
Usage Guidelines
When to Use Component Classes
Use predefined component classes (e.g., .btn-primary, .card, .tile) when:
- The component matches an existing pattern
- You want consistency across the application
- The component needs complex styling that benefits from abstraction
Example:
<button class="btn btn-primary">Save</button>
<div class="card">Card content</div>
When to Use Utility Classes Directly
Use Tailwind utility classes directly when:
- You need one-off styling that doesn’t warrant a component
- You’re prototyping and haven’t decided on a pattern yet
- The component classes don’t quite fit your needs
Example:
<div class="flex items-center gap-4 p-6 bg-white rounded-lg">
Custom layout
</div>
Extending the Design System
Adding New Colors
- Add the color to
tailwind.config.jsin thetheme.colorsobject - Use it in component CSS files with
@apply bg-your-color - Or use it directly in templates:
bg-your-color
Creating New Components
- Create a new CSS file in
static/to_compile/css/(e.g.,modals.css) - Define component classes using
@apply:
.modal {
@apply fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center;
}
.modal-content {
@apply bg-white rounded-lg p-6 max-w-md w-full;
}
- Import the new file in
base.css - Document the component in this file or in component-specific documentation
Modifying Existing Components
- Locate the relevant CSS file in
static/to_compile/css/ - Update the
@applydirectives as needed - Test across all usages of the component
- Consider backward compatibility if the component is widely used
Best Practices
- Use semantic color names: Prefer
bg-primaryoverbg-blue-600for better maintainability - Compose with
@apply: Create component classes for repeated patterns - Keep components focused: Each CSS file should handle one concern
- Document new patterns: When creating new components, document it
- Test across breakpoints: Ensure components work on mobile (
lg:prefixes indicate desktop-only styles) - Maintain color consistency: Use theme colors rather than hardcoded hex values
Color System Philosophy
The color system is organized around semantic meaning rather than visual appearance:
- Layout colors (
bg_layout,bg_content) define the page structure - Semantic colors (
primary,secondary,danger) define component states - State colors (
hover_*,active_*) define interactive states - Text colors (
*_text) ensure proper contrast
This approach makes it easier to:
- Update the entire color scheme by changing values in
tailwind.config.js - Maintain accessibility by ensuring text colors have proper contrast
- Understand component behavior through color names
Accessibility Considerations
- Contrast: All text colors are paired with appropriate background colors for WCAG compliance
- Focus states: Form inputs include focus styles (
focus:border-primary) - Disabled states: Buttons have disabled styles (
cursor-not-allowed) - Semantic HTML: Component classes work with semantic HTML elements
Conclusion
Using Tailwind CSS to build a design system provides consistency and a flexible toolbox for rapid development. However, several considerations are essential for success.
Consistency and flexibility: Tailwind’s utility-first approach combined with custom theme tokens creates a consistent visual language across your application. The @apply directive allows you to compose reusable component classes while maintaining the flexibility to use utilities directly when needed.
Accessibility requires attention: Tailwind provides the tools, but accessibility doesn’t come automatically. You must carefully configure every component—ensuring proper contrast ratios, focus states, and semantic HTML. Consider pairing colors thoughtfully (e.g., primary_text with primary backgrounds) and always test with screen readers and keyboard navigation.
Maintainability through simplicity: The KISS principle (Keep It Simple, Stupid) is crucial, especially regarding component DOM structure. Overly complex HTML hierarchies make components harder to maintain, debug, and modify. Prefer flat, semantic structures that are easy to understand at a glance.
Additional considerations:
- Documentation is essential: As your design system grows, document component usage, color meanings, and patterns. This helps onboard new developers and prevents design drift.
- Balance abstraction with pragmatism: Create component classes for repeated patterns, but don’t over-abstract. Sometimes a few utility classes directly in templates are more appropriate than creating a new component.
- Performance benefits: Tailwind’s purge mechanism ensures only used CSS is included in production, keeping bundle sizes small even as your design system expands.
- Team alignment: A well-defined design system reduces decision fatigue and ensures all team members can contribute consistently without deep CSS expertise.
By combining Tailwind’s powerful utilities with thoughtful component abstraction and proper documentation, you can build a design system that scales with your project while remaining maintainable and accessible.