All of the shadcn/ui magic, none of the React - A components library built with Tailwind CSS that works with any web stack.
π Official Basecoat Website
Want to see Basecoat in action?
π Local: Open basecoat-demo.html in your browser
Explore the complete component showcase with interactive examples, HTMX integration, and comprehensive design system patterns.
Basecoat is a set of components built with Tailwind CSS designed to be used with any traditional web stack. It brings the magic of shadcn/ui to any traditional web stack - no React required.
Tailwind won. But building UIs with utility classes alone kinda sucks. Most Tailwind libraries like Flowbite, Preline, or even Tailwind UI ask you to copy walls of unreadable classes into your HTML. It works, but it's messy and hard to maintain.
shadcn/ui avoids that by wrapping everything in React components. It also gives you a killer design system, theme support, a CLI, and a growing ecosystem.
But maybe you're not using React. Maybe your app is built with plain HTML. Or Flask. Or Rails. Or Laravel. Or Django. Or whatever.
That's where Basecoat comes in. It gives you modern, accessible components with the simplicity of plain HTML and Tailwind.
- π Lightweight: No runtime JS, just CSS and a tiny bit of vanilla JavaScript for interactive components
- π― Easy to use: Add classes like
btnorinputand you're done - π§ Framework-agnostic: Works with any backend or frontend stack
- βΏ Accessible: Components follow accessibility best practices
- π Dark mode ready: Respects your Tailwind config
- π¨ Extendable: Tweak styles with Tailwind or CSS variables
- π Themable: Fully compatible with shadcn/ui themes
- π Readable: No class soup, just clean markup
- π Free and open source: MIT licensed
A comprehensive guide to leveraging Basecoat design system with Tailwind CSS, avoiding common pitfalls and maximizing design consistency.
- π Quick Start
- π― Understanding Basecoat
- βοΈ Tailwind Configuration
- π¨ Color System Integration
- π Dark Mode Implementation
- π¦ Component Patterns
- π« Common Pitfalls & Solutions
- β Best Practices
- π§ Advanced Techniques
- π Reference
- Basic understanding of HTML, CSS, and JavaScript
- Familiarity with Tailwind CSS utility classes
- Access to Basecoat CSS framework
-
Include Tailwind CSS via CDN
<script src="https://cdn.tailwindcss.com"></script>
-
Configure Tailwind for Basecoat
<script> tailwind.config = { darkMode: 'class', theme: { extend: { colors: { // Basecoat color mappings } } } } </script>
-
Add Basecoat CSS Variables
<style> :root { /* Light mode colors */ } .dark { /* Dark mode colors */ } </style>
Basecoat is a modern CSS framework that provides:
- Design System: Consistent colors, spacing, and typography
- OKLCH Color Space: Better color consistency and accessibility
- Modern CSS Features: Uses
@layer,@custom-variant, and@themedirectives - Component Library: Pre-built UI components and patterns
- π¨ OKLCH Color System for better color consistency
- π Built-in Dark Mode support
- π± Mobile-first responsive design
- βΏ Accessibility focused components
- π― Design Tokens for consistent spacing and sizing
| Feature | Basecoat | Tailwind |
|---|---|---|
| Purpose | Design system | Utility-first CSS |
| Colors | OKLCH color space | HSL/RGB colors |
| Components | Pre-built components | Utility classes |
| Customization | Design tokens | Configuration file |
| Dark Mode | Built-in support | Manual implementation |
tailwind.config = {
darkMode: 'class', // Enable class-based dark mode
theme: {
extend: {
colors: {
// Map Basecoat colors to Tailwind
primary: {
DEFAULT: 'var(--color-primary, oklch(0.205 0 0))',
foreground: 'var(--color-primary-foreground, oklch(0.985 0 0))',
},
secondary: {
DEFAULT: 'var(--color-secondary, oklch(0.97 0 0))',
foreground: 'var(--color-secondary-foreground, oklch(0.205 0 0))',
},
// ... more colors
},
borderRadius: {
'sm': 'calc(0.625rem - 4px)',
'md': 'calc(0.625rem - 2px)',
'lg': '0.625rem',
'xl': 'calc(0.625rem + 4px)',
},
spacing: {
'18': '4.5rem',
'88': '22rem',
}
}
}
}/* Light Mode */
:root {
--color-primary: oklch(0.205 0 0);
--color-primary-foreground: oklch(0.985 0 0);
--color-secondary: oklch(0.97 0 0);
--color-background: oklch(1 0 0);
--color-foreground: oklch(0.145 0 0);
/* ... more colors */
}
/* Dark Mode */
.dark {
--color-primary: oklch(0.922 0 0);
--color-primary-foreground: oklch(0.205 0 0);
--color-secondary: oklch(0.269 0 0);
--color-background: oklch(0.145 0 0);
--color-foreground: oklch(0.985 0 0);
/* ... more colors */
}- Better Color Consistency: More perceptually uniform than HSL
- Accessibility: Better contrast ratios
- Future-proof: Modern color space standard
<!-- Using Basecoat colors with Tailwind -->
<div class="bg-primary text-primary-foreground">
Primary content
</div>
<div class="bg-secondary text-secondary-foreground">
Secondary content
</div>
<div class="bg-muted text-muted-foreground">
Muted content
</div>| Color | Light Mode | Dark Mode | Usage |
|---|---|---|---|
primary |
oklch(0.205 0 0) |
oklch(0.922 0 0) |
Main brand color |
secondary |
oklch(0.97 0 0) |
oklch(0.269 0 0) |
Secondary actions |
muted |
oklch(0.97 0 0) |
oklch(0.269 0 0) |
Subtle backgrounds |
accent |
oklch(0.97 0 0) |
oklch(0.371 0 0) |
Accent elements |
destructive |
oklch(0.577 0.245 27.325) |
oklch(0.704 0.191 22.216) |
Error states |
background |
oklch(1 0 0) |
oklch(0.145 0 0) |
Page background |
foreground |
oklch(0.145 0 0) |
oklch(0.985 0 0) |
Text color |
tailwind.config = {
darkMode: 'class', // This is crucial!
// ... rest of config
}<button onclick="toggleDarkMode()" class="btn btn-outline">
<i data-lucide="moon"></i>
</button>
<script>
function toggleDarkMode() {
document.body.classList.toggle('dark');
// Update icon
const icon = document.querySelector('[data-lucide]');
icon.setAttribute('data-lucide',
document.body.classList.contains('dark') ? 'sun' : 'moon'
);
}
</script><!-- Body with dark mode support -->
<body class="bg-background text-foreground dark:bg-background dark:text-foreground">
<!-- Components with dark mode variants -->
<div class="bg-card text-card-foreground dark:bg-card dark:text-card-foreground">
Content
</div>.dark {
--color-primary: oklch(0.922 0 0);
--color-primary-foreground: oklch(0.205 0 0);
--color-background: oklch(0.145 0 0);
--color-foreground: oklch(0.985 0 0);
/* ... all other colors */
}<!-- Primary Button -->
<button class="btn btn-primary">Primary Action</button>
<!-- Secondary Button -->
<button class="btn btn-secondary">Secondary Action</button>
<!-- Outline Button -->
<button class="btn btn-outline">Outline Action</button>
<!-- Destructive Button -->
<button class="btn btn-destructive">Delete</button><!-- Basic Card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Card Title</h3>
</div>
<div class="card-content">
<p>Card content goes here.</p>
</div>
<div class="card-footer">
<button class="btn btn-primary">Action</button>
</div>
</div><!-- Input Field -->
<div class="space-y-2">
<label class="block text-sm font-medium">Email</label>
<input type="email" class="input" placeholder="Enter your email">
</div>
<!-- Textarea -->
<div class="space-y-2">
<label class="block text-sm font-medium">Message</label>
<textarea class="input" rows="4" placeholder="Enter your message"></textarea>
</div>
<!-- Select -->
<div class="space-y-2">
<label class="block text-sm font-medium">Country</label>
<select class="input">
<option>Select a country</option>
<option>United States</option>
<option>Canada</option>
</select>
</div><!-- Status Badges -->
<span class="badge badge-success">Active</span>
<span class="badge badge-warning">Pending</span>
<span class="badge badge-destructive">Error</span>
<span class="badge badge-secondary">Info</span>Problem: Trying to use basecoat.css directly without processing
<!-- This won't work! -->
<link rel="stylesheet" href="basecoat.css">Solution: Use Basecoat as a design reference, implement with Tailwind
<!-- Correct approach -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
// Configure Tailwind with Basecoat colors
</script>Problem: Dark mode not working
// Missing darkMode configuration
tailwind.config = {
theme: { /* ... */ }
}Solution: Enable class-based dark mode
tailwind.config = {
darkMode: 'class', // This is essential!
theme: { /* ... */ }
}Problem: Using hardcoded colors instead of design tokens
<!-- Don't do this -->
<div class="bg-blue-500 text-white">Solution: Use Basecoat color variables
<!-- Do this -->
<div class="bg-primary text-primary-foreground">Problem: Colors not changing in dark mode
/* Missing CSS variables */
.dark {
/* No color definitions */
}Solution: Define all color variables
.dark {
--color-primary: oklch(0.922 0 0);
--color-background: oklch(0.145 0 0);
/* ... all colors */
}Problem: Colors not working if CSS variables fail
colors: {
primary: 'var(--color-primary)', // No fallback
}Solution: Always provide fallbacks
colors: {
primary: 'var(--color-primary, oklch(0.205 0 0))', // With fallback
}- Always reference Basecoat's design tokens
- Use consistent spacing and typography
- Follow the established color palette
<!-- Start with mobile, then enhance -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<!-- Content -->
</div><!-- Use proper semantic elements -->
<main class="container mx-auto px-4">
<section class="py-16">
<h2 class="text-3xl font-bold mb-8">Section Title</h2>
<!-- Content -->
</section>
</main><!-- Include proper ARIA labels and roles -->
<button class="btn btn-primary" aria-label="Submit form">
Submit
</button>
<!-- Use proper heading hierarchy -->
<h1>Main Title</h1>
<h2>Section Title</h2>
<h3>Subsection Title</h3><!-- Use Basecoat's spacing system -->
<div class="space-y-4"> <!-- 1rem spacing -->
<div class="p-4">Content</div> <!-- 1rem padding -->
<div class="p-6">Content</div> <!-- 1.5rem padding -->
</div><!-- Build complex components from simple utilities -->
<div class="card p-6 space-y-4">
<div class="flex items-center justify-between">
<h3 class="text-xl font-semibold">Title</h3>
<button class="btn btn-sm btn-outline">Action</button>
</div>
<p class="text-muted-foreground">Description</p>
</div>/* Create reusable component classes */
.btn {
@apply inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50;
}
.btn-primary {
@apply bg-primary text-primary-foreground hover:bg-primary/90;
}
.btn-secondary {
@apply bg-secondary text-secondary-foreground hover:bg-secondary/80;
}// Create theme variations
const themes = {
light: {
'--color-primary': 'oklch(0.205 0 0)',
'--color-background': 'oklch(1 0 0)',
},
dark: {
'--color-primary': 'oklch(0.922 0 0)',
'--color-background': 'oklch(0.145 0 0)',
}
};
function applyTheme(themeName) {
const theme = themes[themeName];
Object.entries(theme).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value);
});
}<!-- Responsive grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<!-- Content -->
</div>
<!-- Responsive typography -->
<h1 class="text-2xl md:text-3xl lg:text-4xl font-bold">
Responsive Title
</h1>
<!-- Responsive spacing -->
<div class="p-4 md:p-6 lg:p-8">
<!-- Content -->
</div><!-- Smooth transitions -->
<button class="btn btn-primary transition-all duration-200 hover:scale-105">
Hover me
</button>
<!-- Loading states -->
<div class="animate-spin w-4 h-4 border-2 border-primary border-t-transparent rounded-full"></div>/* Primary Colors */
--color-primary: oklch(0.205 0 0);
--color-primary-foreground: oklch(0.985 0 0);
/* Secondary Colors */
--color-secondary: oklch(0.97 0 0);
--color-secondary-foreground: oklch(0.205 0 0);
/* Neutral Colors */
--color-background: oklch(1 0 0);
--color-foreground: oklch(0.145 0 0);
--color-muted: oklch(0.97 0 0);
--color-muted-foreground: oklch(0.556 0 0);
/* Accent Colors */
--color-accent: oklch(0.97 0 0);
--color-accent-foreground: oklch(0.205 0 0);
/* Status Colors */
--color-destructive: oklch(0.577 0.245 27.325);
--color-destructive-foreground: oklch(0.985 0 0);
/* Border Colors */
--color-border: oklch(0.922 0 0);
--color-input: oklch(0.922 0 0);
--color-ring: oklch(0.708 0 0);/* Basecoat spacing system */
--spacing-1: 0.25rem; /* 4px */
--spacing-2: 0.5rem; /* 8px */
--spacing-3: 0.75rem; /* 12px */
--spacing-4: 1rem; /* 16px */
--spacing-5: 1.25rem; /* 20px */
--spacing-6: 1.5rem; /* 24px */
--spacing-8: 2rem; /* 32px */
--spacing-10: 2.5rem; /* 40px */
--spacing-12: 3rem; /* 48px */
--spacing-16: 4rem; /* 64px */
--spacing-20: 5rem; /* 80px */
--spacing-24: 6rem; /* 96px *//* Basecoat border radius system */
--radius-sm: calc(0.625rem - 4px); /* 6px */
--radius-md: calc(0.625rem - 2px); /* 8px */
--radius-lg: 0.625rem; /* 10px */
--radius-xl: calc(0.625rem + 4px); /* 14px *//* Basecoat typography system */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */"The only real mistake is the one from which we learn nothing." - Henry Ford
This section documents our real-world journey building the Basecoat demo, including every failure, obstacle, and breakthrough moment. Use this as a roadmap to avoid the same pitfalls.
What happened: User requested a comprehensive HTML/HTMX solution showcasing every Basecoat feature, using CDN for styling like tailwind-test.html.
The Challenge:
- No downloads allowed
- Must leverage Basecoat design system
- Show every feature and modal
- Use HTMX for interactivity
Goal: Build a complete demonstration of Basecoat's capabilities using Tailwind CSS utilities and HTMX.
Initial Approach:
- Read
basecoat.cssto understand the design system - Create HTML structure with Tailwind classes
- Implement HTMX for dynamic interactions
What we did:
<!-- Initial approach - trying to use basecoat.css directly -->
<link rel="stylesheet" href="basecoat.css">
<script src="https://cdn.tailwindcss.com"></script>What went wrong:
basecoat.cssuses Tailwind directives (@custom-variant,@theme,@layer)- These directives aren't standard CSS and require processing
- The page appeared completely unstyled
User Feedback:
"the styling for that page is plain I don't think it's working"
Lesson Learned: Basecoat CSS files contain unprocessed Tailwind directives that need compilation.
What we did:
- Abandoned
basecoat.cssentirely - Used generic Tailwind CSS classes
- Created a basic demo with standard colors
What went wrong:
- Completely ignored Basecoat's design system
- Used generic colors instead of Basecoat's OKLCH palette
- Lost all design consistency
User Feedback:
"congrats! you didn't fucking use basecoat! great job genius. you completely disregarded what I want. bravo."
Critical Realization: We needed to leverage Basecoat, not replace it.
What we did:
- Re-read
basecoat.cssas a design reference - Extracted Basecoat's color values, spacing, and design tokens
- Used Tailwind utilities to implement the Basecoat design system
Key Insight:
"No i want you to leverage basecoat not necessarily use that css file. it's just provided to help."
What went wrong:
- Added dark mode toggle but colors didn't change
- CSS variables weren't properly configured
- Tailwind wasn't recognizing dark mode classes
User Feedback:
"darkmode doesn't work"
Technical Issues:
- Missing
darkMode: 'class'in Tailwind config - CSS variables not defined for dark mode
- No fallback values for color variables
What we did:
-
Configured Tailwind properly:
tailwind.config = { darkMode: 'class', // This was the key! theme: { extend: { colors: { primary: 'var(--color-primary, oklch(0.205 0 0))', // ... with fallbacks } } } }
-
Added CSS variables for both modes:
:root { --color-primary: oklch(0.205 0 0); /* Light mode colors */ } .dark { --color-primary: oklch(0.922 0 0); /* Dark mode colors */ }
-
Applied dark mode classes:
<body class="bg-background text-foreground dark:bg-background dark:text-foreground">
What went wrong:
- Initial demo was too basic
- Missing many Basecoat features
- Not showcasing the full design system
User Request:
"Can you add 10X more sections with 10x more basecoat design feature attributes and benefits?"
What we did:
- Added 6 major new sections
- Implemented 50+ different components
- Showcased every aspect of Basecoat's design system
- Created comprehensive examples of:
- Advanced form components
- Data visualization
- Navigation patterns
- Layout systems
- Interactive components
- Advanced UI patterns
What we achieved:
- β Fully functional dark mode with proper color transitions
- β Complete Basecoat design system implementation using Tailwind
- β 50+ components showcasing every feature
- β Responsive design that works on all devices
- β Accessible components with proper ARIA labels
- β Consistent design tokens throughout
- β Real-world examples of complex UI patterns
- Don't assume what the user wants
- Ask clarifying questions when unclear
- Understand the difference between "use" and "leverage"
- Basecoat provides design tokens and patterns
- Tailwind provides utility classes for implementation
- They work together, not as replacements
- Requires proper Tailwind configuration
- Needs CSS variables for both light and dark modes
- Must include fallback values
- Requires class-based approach for CDN usage
- Having a design system file isn't enough
- You need to implement it with your chosen framework
- Consistency comes from following the design tokens
- Listen to what users are actually saying
- Don't get defensive about mistakes
- Use feedback to understand the real requirements
The moment everything clicked was when we realized:
// This configuration was the key to everything
tailwind.config = {
darkMode: 'class', // Enable class-based dark mode
theme: {
extend: {
colors: {
// Map Basecoat colors with fallbacks
primary: 'var(--color-primary, oklch(0.205 0 0))',
}
}
}
}Combined with:
/* CSS variables that actually work */
:root { --color-primary: oklch(0.205 0 0); }
.dark { --color-primary: oklch(0.922 0 0); }Our successful approach:
- Basecoat CSS β Design reference and token source
- Tailwind CSS β Utility framework for implementation
- CSS Variables β Bridge between design system and utilities
- HTMX β Dynamic interactions and state management
- JavaScript β Dark mode toggle and enhanced interactions
This journey shows that:
- Failure is part of the process - Each obstacle taught us something valuable
- User feedback is essential - Without it, we would have continued down the wrong path
- Understanding the problem is more important than jumping to solutions
- Iteration and learning lead to better outcomes than perfect first attempts
The final result wasn't just a working demoβit was a deep understanding of how to properly integrate design systems with utility frameworks, and the confidence to tackle similar challenges in the future.
By following this guide, you'll be able to:
- β Leverage Basecoat's design system with Tailwind CSS
- β Avoid common pitfalls that we encountered
- β Implement dark mode correctly
- β Create consistent, accessible components
- β Build responsive layouts
- β Maintain design consistency across your project
Remember: Basecoat provides the design system, Tailwind provides the implementation tools. Use them together to create beautiful, consistent, and maintainable user interfaces! π
Happy coding! If you have questions or run into issues, refer back to this guide or check the Basecoat documentation.
