Tailwind v4
All django-cfg apps use Tailwind CSS v4 with CSS-first configuration.
For the full UI component library see UI Components.
Migration from v3
Import Syntax
v4 uses @import instead of @tailwind directives:
/* globals.css — v4 */
@import './theme.css'; /* Custom properties first */
@import 'tailwindcss'; /* NOT @tailwind directives */Theme variables must come before the Tailwind import.
CSS-First Config
Theme configuration uses @theme {} blocks with CSS custom properties instead of tailwind.config.js:
@theme {
--color-primary: hsl(222.2 47.4% 11.2%);
--color-background: hsl(0 0% 100%);
--color-destructive: hsl(0 84.2% 60.2%);
--radius-lg: 0.5rem;
--radius-md: calc(var(--radius-lg) - 2px);
--radius-sm: calc(var(--radius-lg) - 4px);
}Opacity Modifiers
bg-background/80 works with the ui-core token system. Tokens are wired via
@theme inline { --color-background: var(--background); }, so the opacity
modifier resolves through color-mix(in oklab, …).
// ✅ Opacity modifiers work for every semantic token
<div className="bg-background/80" />
<div className="border-border/30" />
<div className="bg-card/40 text-foreground/60" />
// ❌ Do NOT wrap tokens in hsl(var(--X)) — they are already full hsl(...)
// colors, so this double-wraps to hsl(hsl(...)) and falls back to default.
<div style={{ backgroundColor: 'hsl(var(--background) / 0.8)' }} />If you need opacity in an inline style or arbitrary value, use color-mix over
the variable (the variable already holds a complete color):
<div className="bg-[color-mix(in_oklab,var(--primary)_30%,transparent)]" />This replaces the old v4 advice to use inline
hsl(var(--X) / α)workarounds — that was needed before tokens moved to the@theme inlineshape and is now incorrect. See@djangocfg/ui-corestyles README → Token format.
Arbitrary Values
Arbitrary values like h-[80px] may not work reliably in v4. Define tokens in @theme or use inline styles:
/* In @theme block */
@theme {
--spacing-header: 80px;
--spacing-sidebar: 280px;
}// Use the token
<div className="h-[--spacing-header]" />
// Or use inline style
<div style={{ width: '80px', height: '80px' }} />Breakpoints
Mobile-first breakpoints are unchanged from v3:
| Breakpoint | Min Width |
|---|---|
sm: | 640px |
md: | 768px |
lg: | 1024px |
xl: | 1280px |
2xl: | 1536px |
DjangoCFG Preset
@djangocfg/ui-core provides a shared theme preset that all apps extend. The preset defines color tokens, border radii, font sizes, and animation timings that components depend on.
Rules
- Use
@import 'tailwindcss'— never@tailwind base/components/utilitiesdirectives - Import
theme.cssbeforetailwindcssinglobals.css— order matters - Use the opacity modifier (
bg-card/40) for translucency — it works via@theme inline+color-mix; never wrap tokens ashsl(var(--X)) - Define reusable spacing and size values as tokens in
@theme {}instead of using arbitrary values - Keep breakpoints mobile-first — all utility classes apply from the specified breakpoint and up