CSS vent arrière
bext s'intègre à Tailwind CSS v4 pour la génération CSS au moment de la construction. Le module @bext-stack/framework/tailwind analyse vos fichiers sources à la recherche de candidats à la classe et génère un fichier CSS prêt pour la production.
Comment ça marche#
- Votre script de build appelle
buildCSS()avec des modèles de fichier source - Le compilateur Tailwind charge votre entrée CSS (par défaut
@import "tailwindcss") - Les fichiers sources sont analysés à la recherche de jetons de type classe (candidats de classe)
- Seul le CSS des classes réellement utilisées dans votre code est généré
- Le fichier CSS de sortie est écrit sur le disque et référencé dans vos modèles
Aucun fichier de configuration PostCSS n'est nécessaire. Non tailwind.config.js. Tailwind v4 utilise la configuration CSS-first.
Installation#
bun add tailwindcss
Créer un script#
Créez un script de build qui génère du CSS avant le bundle SSR :
// scripts/build.ts
import { buildCSS } from "@bext-stack/framework/tailwind";
const result = await buildCSS({
sources: ["server"], // Directories to scan for class candidates
outFile: "dist/styles.css", // Where to write the generated CSS
});
console.log(`Tailwind CSS built: ${result.size} bytes`);
Exécutez-le :
bun run scripts/build.ts
Options complètes#
interface TailwindBuildOptions {
/** CSS entry content or path. Defaults to '@import "tailwindcss";' */
css?: string;
/** Glob patterns or directories to scan for class candidates. */
sources: string[];
/** Output file path for the generated CSS. */
outFile: string;
}
Entrée CSS personnalisée#
Vous pouvez transmettre une chaîne CSS personnalisée avec des directives Tailwind, des personnalisations de thème et du CSS supplémentaire :
await buildCSS({
css: `
@import "tailwindcss";
@theme {
--color-primary: #06b6d4;
--color-accent: #f43f5e;
--font-sans: "Inter", system-ui, sans-serif;
}
/* Custom utilities */
.prose-custom h1 {
@apply text-3xl font-bold tracking-tight;
}
`,
sources: ["server"],
outFile: "dist/styles.css",
});
Comment fonctionne l'analyse des candidats#
Le scanner extrait tous les jetons de type classe de vos fichiers sources à l'aide d'un large modèle d'expression régulière. Il fait correspondre des séquences de lettres, de chiffres, de traits d'union, de deux-points, de barres obliques, de points et de crochets, couvrant tous les modèles de classe Tailwind :
- Classes standards :
bg-white,text-sm,p-4 - Préfixes réactifs :
md:flex,lg:grid-cols-3 - Variantes d'état :
hover:bg-blue-600,focus:ring-2 - Valeurs arbitraires :
w-[200px],bg-[#1a1a1a] - Valeurs négatives :
-mt-4,-translate-x-1/2
Le scanner est volontairement trop inclusif. Les jetons qui ne correspondent à aucun utilitaire Tailwind sont simplement ignorés : ils ne produisent aucun CSS de sortie.
Types de fichiers analysés#
Le scanner traite les fichiers avec ces extensions :
.ts,.tsx— Source TypeScript et JSX.js,.jsx— Source JavaScript.html— Modèles HTML.css— Autres fichiers CSS (pour les directives@apply)
Les répertoires nommés node_modules, .git et dist sont automatiquement ignorés.
aide à la feuille de style()#
Utilisez l'assistant stylesheet() pour générer une balise <link> dans vos modèles :
import { stylesheet } from "@bext-stack/framework";
function renderPage(page: Page, ctx: RenderContext): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
${stylesheet("/styles.css")}
</head>
<body>
${page.html}
</body>
</html>`;
}
Cela génère :
<link rel="stylesheet" href="/styles.css">
Vous pouvez transmettre des attributs supplémentaires :
stylesheet("/styles.css", { media: "screen", crossorigin: "anonymous" })
// <link rel="stylesheet" href="/styles.css" media="screen" crossorigin="anonymous">
Tailwind dans les composants JSX#
Avec le runtime JSX de bext, utilisez className (automatiquement alias class ) :
function Card({ title, body }: { title: string; body: string }) {
return (
<div className="rounded-xl border border-gray-200 p-6 hover:shadow-lg transition-shadow">
<h3 className="text-lg font-semibold text-gray-900 mb-2">{title}</h3>
<p className="text-sm text-gray-600 leading-relaxed">{body}</p>
</div>
);
}
function HeroSection() {
return (
<section className="py-24 px-6 text-center bg-gradient-to-b from-cyan-50 to-white">
<h1 className="text-5xl font-bold tracking-tight text-gray-900 mb-4">
Build faster with bext
</h1>
<p className="text-xl text-gray-600 max-w-2xl mx-auto mb-8">
A single binary that replaces your entire deployment stack.
</p>
<div className="flex gap-4 justify-center">
<a href="/get-started" className="px-6 py-3 bg-cyan-500 text-white rounded-lg font-medium hover:bg-cyan-600">
Get Started
</a>
<a href="/docs" className="px-6 py-3 border border-gray-300 rounded-lg font-medium hover:bg-gray-50">
Documentation
</a>
</div>
</section>
);
}
Ou dans les littéraux du modèle :
function renderPage(page: Page): string {
return `
<div class="max-w-4xl mx-auto px-6 py-12">
<h1 class="text-3xl font-bold mb-4">${page.title}</h1>
<div class="prose prose-gray">${page.html}</div>
</div>
`;
}
Exemple de script de construction avec la configuration bext#
Un bext.config.toml typique avec une étape de construction :
[build]
script = "scripts/build-ssr.ts"
watch_dirs = ["server"]
Et le script de construction :
// scripts/build-ssr.ts
import { buildCSS } from "@bext-stack/framework/tailwind";
// 1. Build Tailwind CSS
const css = await buildCSS({
sources: ["server"],
outFile: "public/styles.css",
});
console.log(`CSS: ${css.size} bytes`);
// 2. Bundle the SSR entry (Bun builds this into a single file for the V8 pool)
const result = await Bun.build({
entrypoints: ["server/entry.ts"],
outdir: "dist",
target: "bun",
minify: true,
external: [],
});
if (!result.success) {
console.error("Build failed:", result.logs);
process.exit(1);
}
console.log("SSR bundle built");
Mode sombre#
Tailwind v4 prend en charge le mode sombre dès la sortie de la boîte. Utilisez la variante dark: :
function ThemeCard({ title }: { title: string }) {
return (
<div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-6">
<h3 className="text-gray-900 dark:text-gray-100 font-semibold">{title}</h3>
</div>
);
}
Les sites PRISM sur bext utilisent le moteur CSS natif de Rust (voir ci-dessous) — vous n'avez pas besoin du
Script buildCSS() TypeScript du tout. La fonctionnalité route-css est compilée par défaut et génère du CSS au démarrage sans générer Bun.
Moteur CSS natif de Rust#
bext comprend également un compilateur Tailwind v4 natif Rust qui remplace entièrement le pipeline TypeScript. Lorsque la fonctionnalité route-css est compilée dans (par défaut), bext :
- Construit
public/styles.cssau démarrage sans générer Bun - Génère un CSS minimal par route après SSR (
<style>en ligne avec uniquement les classes utilisées) - Reconstruit le CSS en ~ 1 ms lorsque seuls les fichiers
.csschangent (aucun Bun n'est nécessaire)
Voir Moteur CSS Rust pour plus de détails, les commandes CLI et la référence du thème.
Conseils#
- Scannez les bons répertoires. Analyse uniquement les répertoires contenant des noms de classe. La numérisation de
node_modulesest inutile et lente. - Utilisez
@themepour les couleurs personnalisées. Tailwind v4 utilise des propriétés personnalisées CSS pour la création de thèmes au lieu d'un fichier de configuration JavaScript. - Les valeurs arbitraires fonctionnent. Les classes telles que
w-[calc(100%-2rem)]etbg-[#1a1a1a]sont extraites correctement. - Construisez une fois, servez plusieurs. Le CSS généré est un fichier statique. bext le sert directement à partir du disque avec les en-têtes de mise en cache appropriés. Aucune génération CSS par requête.
En rapport#
- Moteur CSS Rust — référence complète pour le compilateur Rust-native Tailwind v4
- Cadre PRISM - le framework JSX dont le scanner lit l'utilisation
className - Exécution JSX — comment
classNameest aliasclassdans la sortie HTML - Cadre de modèle — l'assistant
stylesheet()et le câblage du script de construction - Rechargement en direct — Détection des modifications CSS et chemin de reconstruction rapide