Installation
Installation
Section titled “Installation”Basalt UI is a Tailwind CSS v4 design system package. Follow these steps to install and configure it.
Quick Start
Section titled “Quick Start”Step 1: Install Basalt UI and Dependencies
Section titled “Step 1: Install Basalt UI and Dependencies”# Using Bun (recommended)bun add basalt-uibun add -D @tailwindcss/typography shadcn tw-animate-css
# Using npmnpm install basalt-uinpm install -D @tailwindcss/typography shadcn tw-animate-css
# Using pnpmpnpm add basalt-uipnpm add -D @tailwindcss/typography shadcn tw-animate-cssWhy these dependencies?
@tailwindcss/typography- Powers the.proseclass for content stylingshadcn- Component CLI tool for adding UI componentstw-animate-css- Animation utilities
Note: These are peer dependencies, meaning you install them separately. This prevents version conflicts and lets you control which versions you use.
Step 2: Import the CSS
Section titled “Step 2: Import the CSS”Add this to your main CSS file:
/* src/styles/globals.css or app/globals.css */@import "basalt-ui/css";⚠️ Critical: This import REPLACES @import "tailwindcss" - do NOT use both!
/* ❌ WRONG - Don't import both */@import "tailwindcss";@import "basalt-ui/css";
/* ✅ CORRECT - Only basalt-ui */@import "basalt-ui/css";Why? BasaltUI includes the complete Tailwind v4 theme. Importing both causes conflicts and duplicate CSS.
Import path formats:
- ✅
basalt-ui/css(correct) - ❌
basalt-ui(wrong) - ❌
basalt-ui/src/index.css(wrong)
What this imports:
- Complete Tailwind v4 theme (
@theme inlineCSS) - Self-hosted variable fonts (Instrument Sans Variable, JetBrains Mono Variable) with
font-display: block - CSS variables for light/dark modes
- Typography plugin configuration
- Animation utilities
What you DON’T need:
- No
tailwind.config.jsfile required - No manual font setup
- No PostCSS configuration
Tailwind version requirement: Basalt UI requires Tailwind CSS v4.1.18 or newer. Earlier v4.0.x versions had critical bugs that are fixed in v4.1.18+.
Step 3: Add Dark Mode
Section titled “Step 3: Add Dark Mode”Add the dark class to your HTML element:
<html class="dark"> <!-- Your app --></html>Dynamic theme switching:
See the Dark Mode Toggle section below for complete React/Vue component examples.
Step 4: Custom CSS Variables (Optional)
Section titled “Step 4: Custom CSS Variables (Optional)”If you need project-specific design tokens or overrides, add them AFTER the basalt-ui import:
@import "basalt-ui/css"; /* Must come first */
/* Your custom overrides come after */:root { --my-custom-color: oklch(0.7 0.1 200); --my-spacing: 2rem;}
/* Override BasaltUI tokens if needed (not recommended) */.dark { --background: oklch(0.15 0.01 285); /* Darker than default */}Important ordering rules:
- ✅
@import "basalt-ui/css"must be first - ✅ Your custom CSS variables come after
- ❌ Don’t define custom variables before the import (they’ll be overridden)
Framework-Specific Setup
Section titled “Framework-Specific Setup”Vite (React, Vue, Svelte)
Section titled “Vite (React, Vue, Svelte)”1. Install Tailwind v4 Vite plugin:
bun add -D @tailwindcss/vite2. Configure Vite:
import { defineConfig } from 'vite';import react from '@vitejs/plugin-react';import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ plugins: [ tailwindcss(), // ⚠️ Must come before framework plugin react(), ],});3. Import CSS in entry file:
import './styles/globals.css';4. Create globals.css:
@import "basalt-ui/css";Next.js (App Router)
Section titled “Next.js (App Router)”1. Create global CSS file:
@import "basalt-ui/css";2. Import in root layout:
import './globals.css';
export default function RootLayout({ children }) { return ( <html className="dark"> <body>{children}</body> </html> );}Note: Tailwind v4 CSS imports work automatically in Next.js 15+ with App Router. No config file needed!
1. Install Tailwind CSS v4 Vite plugin:
bun add -D @tailwindcss/vite2. Add plugin to Astro config:
import { defineConfig } from 'astro/config';import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ vite: { plugins: [tailwindcss()], },});3. Create global CSS file:
@import "basalt-ui/css";4. Import in layout:
---import '../styles/global.css';---<html class="dark"> <body> <slot /> </body></html>Font Loading
Section titled “Font Loading”Basalt UI includes self-hosted variable fonts by default (Instrument Sans Variable and JetBrains Mono Variable). No additional setup needed.
Fonts load automatically when you import basalt-ui CSS:
@import "basalt-ui/css"; /* Includes fonts, no separate import needed */Why self-hosted?
- Privacy: No external CDN requests
- Performance: Fonts served from your domain (faster, no DNS lookup)
- Reliability: No dependency on Google Fonts availability
- Control: Font files bundled with your app
Opinionated by design:
Basalt UI uses Instrument Sans Variable and JetBrains Mono Variable as non-negotiable defaults. This ensures consistency across all integrations (ShadCN, Starlight, Tremor) and eliminates decision fatigue.
If you need different fonts: Fork the package and modify the font imports in src/index.css, or override the CSS variables (breaks consistency with Basalt UI philosophy).
Integration with UI Libraries
Section titled “Integration with UI Libraries”ShadCN UI
Section titled “ShadCN UI”Basalt UI works seamlessly with ShadCN:
# Install ShadCN CLInpx shadcn@latest init
# Choose "Zinc" as base color when prompted
# Add componentsnpx shadcn@latest add buttonnpx shadcn@latest add cardnpx shadcn@latest add dialogHow it works:
- ShadCN components use classes like
bg-primary,text-foreground - Basalt UI provides these via CSS variables
- Dark mode switches automatically with
.darkclass - No additional configuration needed
Tremor Charts
Section titled “Tremor Charts”Tremor Raw chart components use Basalt colors automatically:
import { AreaChart } from '@tremor/react';
<AreaChart data={chartData} index="date" categories={["sales"]} colors={["blue"]} // Uses Basalt OKLCH blue/>Available colors:
blue,red,emerald,amber,violet,cyan,indigo- Sequential:
chart-blue-1throughchart-blue-8
Starlight Documentation
Section titled “Starlight Documentation”For Astro Starlight, import the dedicated CSS file:
import starlight from '@astrojs/starlight';
export default defineConfig({ integrations: [ starlight({ title: 'My Docs', customCss: [ 'basalt-ui/starlight', ], }), ],});Self-Hosted Variable Fonts
Section titled “Self-Hosted Variable Fonts”Basalt UI includes two self-hosted variable fonts:
Instrument Sans Variable - Headings and body text
- Full variable font with weight axis (400-700)
- Optimized for UI and content
- Clean, modern sans-serif
JetBrains Mono Variable - Code blocks and monospace
- Full variable font with weight axis (400)
- Designed for programming
- Excellent readability
Font stack:
--font-heading: "Instrument Sans Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-body: "Instrument Sans Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono: "JetBrains Mono Variable", "Menlo", "Monaco", "Courier New", monospace;Requirements
Section titled “Requirements”Peer Dependencies
Section titled “Peer Dependencies”- Tailwind CSS v4+ - Core CSS framework
- @tailwindcss/typography - Typography plugin for
.proseclass - tw-animate-css - Animation utilities
- shadcn - Component utilities
All must be installed in your project (listed above).
Browser Support
Section titled “Browser Support”Modern browsers with OKLCH color support:
- Chrome 111+
- Edge 111+
- Safari 16.4+
- Firefox 113+
Troubleshooting
Section titled “Troubleshooting”Error: ”./css” is not exported under the condition “style”
Section titled “Error: ”./css” is not exported under the condition “style””Cause: Using Tailwind v4 Vite plugin with older basalt-ui version.
Fix: Update basalt-ui to latest version:
bun update basalt-ui# ornpm update basalt-uiError: Can’t resolve ‘@tailwindcss/typography’
Section titled “Error: Can’t resolve ‘@tailwindcss/typography’”Cause: Peer dependencies not installed.
Fix: Install all required peer dependencies:
bun add -D @tailwindcss/typography shadcn tw-animate-cssError: Cannot find module ‘basalt-ui’
Section titled “Error: Cannot find module ‘basalt-ui’”Cause: Using incorrect import path.
Fix: Use basalt-ui/css (not basalt-ui or basalt-ui/src/index.css):
/* ❌ Wrong */@import "basalt-ui";@import "basalt-ui/src/index.css";
/* ✅ Correct */@import "basalt-ui/css";Styles not applying / Components look unstyled
Section titled “Styles not applying / Components look unstyled”Possible causes:
- CSS import order incorrect
- Missing Tailwind plugin configuration
- Dark mode class not applied
Fixes:
- Ensure
basalt-ui/cssis imported in your entry CSS file - For Vite: Add
@tailwindcss/viteplugin BEFORE framework plugin - For Next.js: Ensure globals.css is imported in root layout
- Add
class="dark"to your<html>element - Check browser console for CSS loading errors
Vite plugin order
Section titled “Vite plugin order”Error: Styles not loading in Vite projects
Cause: Tailwind plugin must come before framework plugin
Fix:
export default defineConfig({ plugins: [ tailwindcss(), // ✅ First react(), // ✅ Second ],});
// ❌ Wrong order:// plugins: [react(), tailwindcss()]Fonts not loading
Section titled “Fonts not loading”Symptom: Using fallback system fonts instead of Instrument Sans
Check:
- Verify
basalt-ui/cssis imported (fonts are included) - Check browser DevTools Network tab for font requests
- Look for
InstrumentSans-Variable.woff2andJetBrainsMono-Variable.woff2 - Ensure no Content Security Policy blocking fonts
Note: Fonts are bundled automatically - no additional setup needed!
ShadCN components wrong colors
Section titled “ShadCN components wrong colors”Symptom: ShadCN components not using Basalt colors
Fix:
- Import
basalt-ui/cssBEFORE runningshadcn init - Choose “Zinc” as base color during initialization
- Basalt provides CSS variables (
--background,--primary, etc.) - Components automatically use these
Verify:
<Button variant="default">Primary Button</Button>// Should be blue (Basalt primary color)Dark mode not working
Section titled “Dark mode not working”Symptom: Dark mode not activating
Cause: Missing .dark class on <html> element
Fix:
<!-- ✅ Correct --><html class="dark">
<!-- ❌ Wrong --><body class="dark"> <!-- Must be on html! -->Dark Mode Toggle Component
Section titled “Dark Mode Toggle Component”React Example
Section titled “React Example”'use client'; // For Next.js App Router
import { useEffect, useState } from 'react';
export function ThemeToggle() { const [theme, setTheme] = useState<'light' | 'dark'>('dark');
// Load theme from localStorage on mount useEffect(() => { const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null; const initialTheme = savedTheme || 'dark'; setTheme(initialTheme); document.documentElement.classList.toggle('dark', initialTheme === 'dark'); }, []);
const toggleTheme = () => { const newTheme = theme === 'dark' ? 'light' : 'dark'; setTheme(newTheme); document.documentElement.classList.toggle('dark', newTheme === 'dark'); localStorage.setItem('theme', newTheme); };
return ( <button onClick={toggleTheme} className="rounded-md bg-primary px-4 py-2 text-primary-foreground" aria-label="Toggle theme" > {theme === 'dark' ? '🌙 Dark' : '☀️ Light'} </button> );}Vue 3 Example
Section titled “Vue 3 Example”<script setup lang="ts">import { ref, onMounted } from 'vue';
const theme = ref<'light' | 'dark'>('dark');
onMounted(() => { const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null; const initialTheme = savedTheme || 'dark'; theme.value = initialTheme; document.documentElement.classList.toggle('dark', initialTheme === 'dark');});
const toggleTheme = () => { const newTheme = theme.value === 'dark' ? 'light' : 'dark'; theme.value = newTheme; document.documentElement.classList.toggle('dark', newTheme === 'dark'); localStorage.setItem('theme', newTheme);};</script>
<template> <button @click="toggleTheme" class="rounded-md bg-primary px-4 py-2 text-primary-foreground" aria-label="Toggle theme" > {{ theme === 'dark' ? '🌙 Dark' : '☀️ Light' }} </button></template>Vanilla JavaScript
Section titled “Vanilla JavaScript”// Toggle dark modefunction toggleTheme() { const isDark = document.documentElement.classList.toggle('dark'); localStorage.setItem('theme', isDark ? 'dark' : 'light');}
// Load saved theme on page loadconst savedTheme = localStorage.getItem('theme') || 'dark';document.documentElement.classList.toggle('dark', savedTheme === 'dark');TypeScript errors
Section titled “TypeScript errors”If you see TypeScript errors related to basalt-ui, ensure you have the latest version:
bun update basalt-uiIf errors persist:
{ "compilerOptions": { "types": ["vite/client"] }}Monorepo resolution issues
Section titled “Monorepo resolution issues”Symptom: Can’t resolve basalt-ui/css in monorepo workspace
Fix (Option 1): Use relative path:
@import "../../../../packages/basalt-ui/src/index.css";Fix (Option 2): Use @source directive (Tailwind v4):
@import "tailwindcss";@import "basalt-ui/css";
@source "../../../packages/basalt-ui/src";Fix (Option 3): Ensure workspace protocol:
{ "dependencies": { "basalt-ui": "workspace:*" }}Next Steps
Section titled “Next Steps”After installation:
- Explore Typography for content styling
- Review Color System for theming
- Check Spacing for layout utilities