Theming Guide
June 22, 2026 · View on GitHub
This document provides a comprehensive guide for understanding and customizing the theming system in LibreDB Studio.
Overview
LibreDB Studio uses a modern theming architecture built on:
- Tailwind CSS v4 - CSS-first configuration with
@themedirective - shadcn/ui - Accessible component library with CSS variable theming
- CSS Custom Properties - Light and dark variable sets defined in
globals.css
Note: Studio currently ships dark-mode only. The
.darkclass is applied statically on the<body>element insrc/app/layout.tsx, so the light-mode variables defined in:rootare present but not reachable at runtime. There is no theme toggle yet (see Switching Themes). The light-mode values are documented below for when runtime switching is added.
Architecture
Theme Configuration Flow
globals.css
│
├── :root (Light mode variables)
├── .dark (Dark mode variables)
│
└── @theme inline
│
└── Maps CSS variables to Tailwind utilities
│
└── bg-background, text-foreground, etc.
File Structure
src/
└── app/
└── globals.css # Theme configuration (single source of truth)
CSS Variables
Core Variables
| Variable | Description | Usage |
|---|---|---|
--background | Page background color | bg-background |
--foreground | Default text color | text-foreground |
--card | Card/panel background | bg-card |
--card-foreground | Card text color | text-card-foreground |
--popover | Popover/dropdown background | bg-popover |
--popover-foreground | Popover text color | text-popover-foreground |
--primary | Primary action color | bg-primary, text-primary |
--primary-foreground | Text on primary | text-primary-foreground |
--secondary | Secondary action color | bg-secondary |
--secondary-foreground | Text on secondary | text-secondary-foreground |
--muted | Muted/subtle background | bg-muted |
--muted-foreground | Muted text color | text-muted-foreground |
--accent | Accent/hover background | bg-accent |
--accent-foreground | Text on accent | text-accent-foreground |
--destructive | Destructive action color | bg-destructive |
--destructive-foreground | Text on destructive | text-destructive-foreground |
--border | Border color | border-border |
--input | Input border color | border-input |
--ring | Focus ring color | ring-ring |
--radius | Border radius base | rounded-lg, rounded-md |
Chart Colors
| Variable | Light (:root) | Dark (.dark) | Usage |
|---|---|---|---|
--chart-1 | #e76e50 | #3b82f6 | Primary chart color |
--chart-2 | #2a9d90 | #22c55e | Secondary chart color |
--chart-3 | #274754 | #f59e0b | Tertiary chart color |
--chart-4 | #e8c468 | #a855f7 | Quaternary chart color |
--chart-5 | #f4a462 | #ec4899 | Quinary chart color |
Dark Mode
Current Configuration
LibreDB Studio uses a dark-first design with the following color palette (based on Tailwind Zinc):
.dark {
--background: #09090b; /* zinc-950 */
--foreground: #fafafa; /* zinc-50 */
--card: #0a0a0a; /* near zinc-950 */
--popover: #0a0a0a;
--secondary: #27272a; /* zinc-800 */
--muted: #27272a; /* zinc-800 */
--accent: #27272a; /* zinc-800 */
--border: #27272a; /* zinc-800 */
--muted-foreground: #a1a1aa; /* zinc-400 */
}
Switching Themes
Current state: there is no runtime theme switching. Dark mode is forced by hardcoding the
darkclass on<body>insrc/app/layout.tsx:<body className={`${geistSans.variable} ${geistMono.variable} antialiased dark font-sans`}>The
next-themespackage is present inpackage.jsonbut is not wired up — there is no<ThemeProvider>in the layout and no toggle component.
To add a runtime light/dark toggle, you would wrap the app in next-themes' ThemeProvider
(attribute="class") instead of hardcoding the class, then add a toggle that flips the theme:
// Not yet implemented — illustrative only
<ThemeProvider attribute="class" defaultTheme="dark">
{children}
</ThemeProvider>
Tailwind v4 Integration
The @theme inline Directive
Tailwind CSS v4 introduces CSS-first configuration. The @theme inline directive maps CSS variables to Tailwind utility classes:
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
/* ... */
}
This enables using semantic class names:
<div className="bg-background text-foreground">
<div className="bg-card border-border">
Content
</div>
</div>
IDE Warnings
Your IDE may show warnings like Unknown at rule @theme. This is expected because:
- Tailwind v4's
@themedirective is new - CSS validators don't recognize it yet
- It works correctly - the build succeeds
To suppress these warnings in VS Code, add to .vscode/settings.json:
{
"css.lint.unknownAtRules": "ignore"
}
Best Practices
DO Use Theme Variables
// Good - uses theme variables
<div className="bg-background text-foreground border-border">
<span className="text-muted-foreground">
<button className="bg-primary text-primary-foreground hover:bg-accent">
DON'T Use Hardcoded Colors
// Bad - hardcoded colors
<div className="bg-[#050505] text-white border-[#262626]">
<span className="text-zinc-500">
<button className="bg-zinc-900 hover:bg-zinc-800">
Opacity Modifiers
Use opacity modifiers with theme variables:
<div className="bg-accent/50"> {/* 50% opacity */}
<span className="text-muted-foreground/70"> {/* 70% opacity */}
<div className="border-border/30"> {/* 30% opacity */}
Customizing the Theme
Step 1: Modify CSS Variables
Edit src/app/globals.css:
.dark {
/* Change the primary color */
--primary: #3b82f6; /* blue-500 */
--primary-foreground: #ffffff;
/* Change the accent color */
--accent: #1e3a5f;
}
Step 2: Verify Mappings
Ensure @theme inline maps your variables:
@theme inline {
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-accent: var(--accent);
}
Step 3: Test in Dark Mode
Since Studio runs dark-mode only today, edit and verify the .dark variable set. If you also
maintain the :root (light) values for a future toggle, keep them in sync — but only the
.dark set is rendered at runtime.
Component-Specific Theming
Buttons
shadcn/ui buttons use theme variables automatically:
<Button variant="default"> {/* bg-primary */}
<Button variant="secondary"> {/* bg-secondary */}
<Button variant="outline"> {/* border-input */}
<Button variant="ghost"> {/* hover:bg-accent */}
<Button variant="destructive"> {/* bg-destructive */}
Cards
<Card> {/* bg-card border-border */}
<CardHeader>
<CardTitle> {/* text-card-foreground */}
Dropdowns & Popovers
<DropdownMenuContent> {/* bg-popover text-popover-foreground */}
Inputs
<Input> {/* bg-background border-input */}
Adding New Colors
Step 1: Define Variables
:root {
--warning: #f59e0b;
--warning-foreground: #ffffff;
}
.dark {
--warning: #d97706;
--warning-foreground: #ffffff;
}
Step 2: Add Theme Mapping
@theme inline {
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}
Step 3: Use in Components
<div className="bg-warning text-warning-foreground">
Warning message
</div>
Troubleshooting
Colors Not Applying
- Check that the variable is defined in both
:rootand.dark - Verify the
@theme inlinemapping exists - Ensure you're using the correct class name (
bg-cardnotbg-[--card])
Dark Mode Not Working
- Check that the
darkclass is present on<body>insrc/app/layout.tsx(it is hardcoded there) - Ensure variables are defined in the
.dark {}selector inglobals.css - Confirm
@theme inlinemaps the variable to a--color-*utility
Build Errors
- Run
bun run buildto check for CSS syntax errors - Verify all variables are properly closed
- Check for typos in variable names
Resources
Official Documentation
Theme Generators
- tweakcn - Interactive shadcn/ui theme editor
- shadcn Theme Generator - Official theme generator
Color References
- Tailwind Zinc Palette
- OKLCH Color Space - Modern color space for themes
Color Palette Reference
Light Mode (Default)
| Variable | Hex | Description |
|---|---|---|
| background | #ffffff | White |
| foreground | #0a0a0a | Near black |
| card | #ffffff | White |
| primary | #171717 | Near black |
| secondary | #f5f5f5 | Light gray |
| muted | #f5f5f5 | Light gray |
| muted-foreground | #737373 | Medium gray |
| accent | #f5f5f5 | Light gray |
| border | #e5e5e5 | Gray |
Dark Mode
| Variable | Hex | Tailwind | Description |
|---|---|---|---|
| background | #09090b | zinc-950 | Near black |
| foreground | #fafafa | zinc-50 | Near white |
| card | #0a0a0a | - | Dark |
| primary | #fafafa | zinc-50 | Near white |
| secondary | #27272a | zinc-800 | Dark gray |
| muted | #27272a | zinc-800 | Dark gray |
| accent | #27272a | zinc-800 | Dark gray |
| border | #27272a | zinc-800 | Dark gray |
| muted-foreground | #a1a1aa | zinc-400 | Medium gray |
Last updated: June 2026