Styles & CSS Architecture
Dinou features a unified, zero-config styling pipeline supporting Tailwind CSS v4, CSS Modules, and Global CSS with instant HMR and single-bundle extraction at /styles.css.
Styling in Dinou
Dinou provides built-in, out-of-the-box support for the modern CSS ecosystem without requiring complex bundler setup. Under the hood, Dinou orchestrates PostCSS, @tailwindcss/postcss, Autoprefixer, and postcss-modules to:
- Extract all styles across Server and Client Components into a single, optimized production bundle served at
/styles.css. - Eliminate CSS specificity conflicts by compiling CSS Modules with cryptographic scoped hashing.
- Provide sub-millisecond Hot Module Replacement (HMR) in development, updating styles in place without full page reloads or React state loss.
- Resolve TypeScript path aliases (such as
@/styles/tokens.css) directly inside your CSS@importrules.
tailwind.config.js required; configured with native @theme blocks./styles.css, eliminating Flash of Unstyled Content (FOUC).Linking /styles.css
Because Dinou bundles all global styles, Tailwind classes, and CSS module rules into a unified stylesheet, you must manually link the stylesheet in your root layout or document <head>:
<link href="/styles.css" rel="stylesheet" precedence="default" />React 19 precedence Attribute
In React 19 and React Server Components (RSC), <link rel="stylesheet"> tags accept a special precedence attribute (e.g. precedence="default"). This tells the React runtime:
- Resource Hoisting: React hoists the link tag into the document
<head>automatically, even if rendered inside a nested sub-component or layout. - Suspense & Streaming Synchronization: React pauses rendering of stream-dependent HTML until the critical stylesheet has finished loading in the browser, preventing Flash of Unstyled Content (FOUC).
- Deduplication: If multiple components request
/styles.csswith the same precedence, React ensures only a single link tag is rendered in the final DOM.
Tailwind CSS (v4 Engine)
Dinou incorporates the official Tailwind CSS v4 engine via @tailwindcss/postcss. Tailwind v4 is fundamentally CSS-native: there is no JavaScript config file (tailwind.config.js) required by default.
To enable Tailwind CSS across your application, simply import it at the top of your global CSS file (typically src/globals.css):
/* src/globals.css */
@import "tailwindcss";Custom @theme & Directives
In Tailwind v4, custom colors, fonts, spacing, and breakpoints are defined directly in CSS using the @theme directive:
/* src/globals.css */
@import "tailwindcss";
@theme {
--color-brand-primary: #3b82f6;
--color-brand-dark: #1e293b;
--font-display: "Inter", system-ui, sans-serif;
--radius-badge: 0.375rem;
}
/* Custom utility definitions */
@utility text-balance {
text-wrap: balance;
}
/* Base style overrides */
@layer base {
body {
@apply bg-background text-foreground antialiased;
}
}Classes like bg-brand-primary, font-display, and text-balance are generated automatically by Tailwind v4 during compilation.
CSS Modules (*.module.css)
When you want component-scoped styling without utility classes or global selector collisions, use CSS Modules. Any file ending in .module.css is processed automatically by Dinou's PostCSS module compiler.
Scoped Class Names & Resolution
During compilation, Dinou transforms local class names into globally unique identifiers using its internal createScopedName generator:
/* src/components/card.module.css */
.card {
padding: 1.5rem;
border-radius: 0.75rem;
background-color: var(--card-bg, #ffffff);
border: 1px solid var(--border-color, #e2e8f0);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
}
.badge {
font-size: 0.75rem;
font-weight: 600;
color: #3b82f6;
}Import the module object in your component and apply the scoped keys:
// src/components/card.tsx
"use client";
import styles from "./card.module.css";
export function FeatureCard({ title, badge }: { title: string; badge: string }) {
return (
<div className={styles.card}>
<span className={styles.badge}>{badge}</span>
<h3 className="text-lg font-bold mt-2">{title}</h3>
</div>
);
}The compiled DOM will render classes like _card__card___a1b2c, guaranteeing zero styling overlap with any other card component in the application.
Global CSS & Path Aliases
You can import CSS files anywhere in your application tree:
- Root Layout: Import
./globals.cssin your rootsrc/layout.tsxto establish base reset rules, design tokens, and Tailwind directives. - Component Files: Import local stylesheets or third-party library CSS (e.g.
import "katex/dist/katex.min.css";) directly inside the component that uses them. - CSS @import with Path Aliases: Dinou integrates
postcss-importwith its internal path alias resolver. You can reference paths configured in yourtsconfig.jsoninside CSS files:
/* src/styles/main.css */
@import "@/styles/tokens.css";
@import "@/styles/typography.css";
@import "tailwindcss";Instant CSS Hot Module Replacement
During development, Dinou incrementally recompiles modified .css and .module.css files and updates /styles.css in place via WebSocket. Styles refresh instantly with zero full page reloads, keeping component state, form inputs, dialogs, and scroll positions intact.
TypeScript Declarations (dinou/env)
Built-in ambient type declarations for CSS Modules and static media assets are provided directly via dinou/env.
Create or verify a dinou-env.d.ts file at the root of your project:
/// <reference types="dinou/env" />Ensure your tsconfig.json includes the declaration file and specifies "moduleResolution": "bundler":
{
"compilerOptions": {
"paths": {
"@/*": ["src/*"]
},
"allowJs": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"module": "ESNext",
"moduleResolution": "bundler"
},
"include": [
"src",
"dinou-env.d.ts"
]
}Complete End-to-End Example
The following cohesive example shows how the root layout, Tailwind v4 global CSS, CSS modules, and page components function together harmoniously:
"use client";
import type { ReactNode } from "react";
import "./globals.css"; // Imports Tailwind v4 & global tokens
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My Dinou App</title>
{/* Critical single-bundle stylesheet with React 19 precedence */}
<link href="/styles.css" rel="stylesheet" precedence="default" />
</head>
<body className="min-h-screen bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100 antialiased">
{children}
</body>
</html>
);
}@import "tailwindcss";
@theme {
--color-brand: #10b981;
--color-brand-hover: #059669;
--radius-panel: 1rem;
}
:root {
--card-bg: #ffffff;
--border-color: #e2e8f0;
}
@media (prefers-color-scheme: dark) {
:root {
--card-bg: #0f172a;
--border-color: #1e293b;
}
}
.custom-gradient {
background: linear-gradient(135deg, var(--color-brand) 0%, #f59e0b 100%);
}.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: var(--radius-panel);
padding: 1.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.badge {
display: inline-block;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-brand);
}"use client";
import styles from "./card.module.css";
export default function HomePage() {
return (
<main className="container max-w-2xl mx-auto py-12 px-4 space-y-6">
<header className="space-y-2">
<h1 className="text-3xl font-extrabold tracking-tight">
Styling with Dinou
</h1>
<p className="text-slate-600 dark:text-slate-400">
Combines Tailwind utility classes with locally scoped CSS Modules.
</p>
</header>
{/* Using scoped CSS Module styles alongside Tailwind utilities */}
<div className={styles.card}>
<span className={styles.badge}>Modular Component</span>
<h2 className="text-xl font-bold mt-2">Zero Style Bleed</h2>
<p className="text-sm text-slate-500 dark:text-slate-400 mt-1">
Scoped classes are automatically hashed and isolated by Dinou PostCSS.
</p>
<button className="mt-4 px-4 py-2 custom-gradient text-white rounded-lg font-medium shadow hover:opacity-90 transition">
Interactive Button
</button>
</div>
</main>
);
}