Kritische CSS splitsen in Tailwind v3/v4 op Laravel + Vite
Als een pagina traag aanvoelt, staren gebruikers naar een leeg scherm terwijl CSS het renderen blokkeert. Een pragmatische oplossing is om precies genoeg stijlen te leveren voor de above-the-fold weergave en de rest uit te stellen. Deze handleiding laat een schone setup met twee ingangen zien in Laravel + Vite, met productieklare voorbeelden voor Tailwind v3 en v4, waarom PageSpeed/Core Web Vitals belangrijk is en wat je moet vermijden.

Waarom PageSpeed belangrijk is (LCP/CLS)
Kritische CSS is de minimale set stijlen die nodig is om het eerste scherm (above-the-fold) te renderen. Door dit deel vroeg aan te leveren (inline of als een klein bestand) en de resterende CSS uit te stellen, verminder je renderblokkerend werk en verbeter je LCP. Het zorgvuldig instellen van lettertypen kan ook onverwachte verschuivingen in de lay-out verminderen en CLS helpen.
Basisstrategie (Laravel + Vite)
- Twee ingangspunten:
critical.css
voor het eerste scherm;app.css
voor al het andere (plugins, uitgebreide hulpprogramma's, typografie in lange vorm). - Laadvolgorde: lever het belangrijkste op in
<head>
(inline of een normale link). Stel de rest uit metrel="preload" as="style"
+onload
en een<noscript>
fallback. - Vermijd duplicaten: houd basis/componenten in kritisch en verplaats het grootste deel van de hulpprogramma's naar de uitgestelde bundel; zorg ervoor dat de bronbestanden die door elke build worden gescand elkaar niet overlappen.
Tailwind v3: twee configuraties + een preset
1) Deel je thema één keer
// tailwind.shared.js
module.exports = {
theme: {
extend: {
// colors, spacing, etc.
},
},
plugins: [],
};
2) Kritische build (scan alleen sjablonen boven de vouw)
// tailwind.critical.config.js
module.exports = {
presets: [require('./tailwind.shared')],
content: [
'./resources/views/layouts/app.blade.php',
'./resources/views/partials/header.blade.php',
'./resources/views/home/hero.blade.php',
],
};
/* resources/css/critical.css */
@config "tailwind.critical.config.js";
@tailwind base;
@tailwind components;
@tailwind utilities;
3) App bundel (sluit bestanden uit die door kritisch worden gebruikt)
// tailwind.app.config.js
module.exports = {
presets: [require('./tailwind.shared')],
content: [
'./resources/**/*.blade.php',
'./resources/**/*.vue',
'!./resources/views/partials/header.blade.php',
'!./resources/views/home/hero.blade.php',
],
};
/* resources/css/app.css */
/* Avoid duplicating base/components (already shipped in critical) */
@config "tailwind.app.config.js";
@tailwind utilities;
Als je dynamische klassenamen hebt, voeg ze dan toe aan safelist
in beide configuraties.
4) Bouwen en verbinden via Vite (Laravel)
// vite.config.ts
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/critical.css',
'resources/css/app.css',
'resources/js/app.js',
],
refresh: true,
}),
],
})
<!-- Blade: load critical normally (or inline it) -->
@vite('resources/css/critical.css')
<!-- Defer the rest: preload + onload + noscript -->
@php $appCss = Vite::asset('resources/css/app.css'); @endphp
<link rel="preload" as="style" href="{{ $appCss }}" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ $appCss }}"></noscript>
Tailwind v4: CSS-first sources met fijnmazige controle
In Tailwind v4 is de configuratie CSS-first. Om overlappingen tussen kritieke en app-bundels te voorkomen, schakel je automatisch scannen uit en declareer je sources expliciet.
1) Gedeelde tokens
/* resources/css/theme.css */
@theme {
/* brand tokens, breakpoints, etc. */
--color-brand-500: oklch(0.72 0.12 250);
--breakpoint-2xl: 96rem;
}
2) Kritische CSS: scan alleen wat nodig is
/* resources/css/critical.css */
@import "tailwindcss" source(none);
@import "./theme.css";
/* Enumerate just the above-the-fold templates */
@source "../views/layouts/app.blade.php";
@source "../views/partials/header.blade.php";
@source "../views/home/hero.blade.php";
/* Import required layers explicitly */
@layer theme, base, components, utilities;
@import "tailwindcss/theme" layer(theme);
@import "tailwindcss/base" layer(base);
@import "tailwindcss/components" layer(components);
@import "tailwindcss/utilities" layer(utilities);
3) App CSS: al het andere (sluit uit wat kritisch al omvat)
/* resources/css/app.css */
@import "tailwindcss" source(none);
@import "./theme.css";
/* Scan the project... */
@source "../views/**/*.blade.php";
/* ...except templates you already used for critical */
@source not "../views/partials/header.blade.php";
@source not "../views/home/hero.blade.php";
/* App bundle: keep it lean for post-fold content */
@layer theme, utilities;
@import "tailwindcss/theme" layer(theme);
@import "tailwindcss/utilities" layer(utilities);
/* Inline safelist if you generate classes dynamically */
@source inline("md:grid lg:gap-6 bg-brand-500");
4) Vite setup en Blade laden
// vite.config.ts (same as v3)
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/critical.css',
'resources/css/app.css',
'resources/js/app.js',
],
refresh: true,
}),
],
})
<!-- Blade: same pattern as v3 -->
@vite('resources/css/critical.css')
@php $appCss = Vite::asset('resources/css/app.css'); @endphp
<link rel="preload" as="style" href="{{ $appCss }}" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ $appCss }}"></noscript>
Wat hoort in kritisch (en wat niet)
- Opnemen: basisinstellingen, header/nav-structuur, heldensectie, essentiële knoppen/links, minimale typografie op het eerste scherm.
- Vermijd: zware onderdelen onder de vouw, langwerpige
.prose
typografie, zeldzame widgets.
Lettertypen & CLS-minirecepten
Lay-out verschuivingen treden vaak op wanneer het fallback lettertype wordt vervangen door het weblettertype. Gebruik size-adjust
en gerelateerde metric overrides om beter overeen te komen met het fallback lettertype en laad alleen de echt kritieke vlakken vooraf.
@font-face {
font-family: "InterVar";
src: url("/fonts/InterVar.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
/* Tweak metrics to reduce shifts */
size-adjust: 100%;
ascent-override: 92%;
descent-override: 24%;
line-gap-override: 0%;
}
Controleer het resultaat
- Voer PageSpeed/Lighthouse uit en vergelijk LCP; controleer "Verminder render-blocking bronnen" en "Verminder ongebruikte CSS".
- Sanity check: controleer of er geen duplicatie is tussen bundels; als je extra gewicht ziet, bekijk dan de gescande bronnen opnieuw.
- Onthoud: labscores zijn indicatief; let op de algemene trend en veldgegevens.
Veelvoorkomende valkuilen
- Overlappende bronnen → duplicaten: in v3, repareer je
content
globs; in v4, gebruiksource(none)
met expliciete@source
en@source not
. - Plugins/typografie in kritisch: dit maakt het bestand gemakkelijk voller - houd het in
app.css
. - Inlining kritisch vs CSP: als je CSP streng is, verzend kritisch dan als een klein extern bestand.
- migratie v3 → v4: verplaats tokens van
theme.extend
naar@theme
, schakel vancontent
naar@source
en geef de voorkeur aan CSS-first imports.
Alternatieven als twee-entry niet jouw ding is
- Automatisch gegenereerde kritieke (bijv. kritieke, beestjes): het snelst om te beginnen; moeilijker om te debuggen op complexe sjablonen.
- Enkele configuratie + meerdere bronnen: houd één config (v3) of CSS-first setup (v4) en beheer inclusie/exclusie via globs/
@source
. - Route-level CSS: bundels per pagina alleen geladen waar nodig; krachtig maar voegt infra complexiteit toe.
- Component-scoped kritisch: inline kleine stijlen voor belangrijke boven-de-vouw componenten; discipline vereist, werkt goed voor herhaalde blokken.
Samenvatting
Met een kleine kritische bundel in <head>
en een uitgestelde hoofdbundel geef je de browser minder om op te blokkeren en gebruikers een snellere eerste paint. Tailwind v3 geeft de voorkeur aan twee configs en zorgvuldige content
paden; Tailwind v4 maakt dit nog netter met CSS-first @source
controle. Op Laravel + Vite is het een eenvoudige, onderhoudbare opzet die de waargenomen snelheid meetbaar verbetert.
Neem contact op
Een project in gedachten?
eel je context en gewenst resultaat. Binnen 1 werkdag sturen we de eenvoudigste volgende stap (tijdlijn, ruwe begroting of snelle audit).