diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1f70e6..7abf770 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,6 @@ jobs: - name: Build run: pnpm run build + + - name: Verify Tailwind CSS compiled + run: pnpm run verify:css diff --git a/package.json b/package.json index 587c527..d29a21c 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "start": "next start", "lint": "pnpm exec eslint .", "typecheck": "tsc --noEmit", + "verify:css": "node scripts/assert-tailwind-css.mjs", "format": "prettier --write ." }, "keywords": [ diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..a869506 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + '@tailwindcss/postcss': {}, + }, +} + +export default config diff --git a/scripts/assert-tailwind-css.mjs b/scripts/assert-tailwind-css.mjs new file mode 100644 index 0000000..383c9cd --- /dev/null +++ b/scripts/assert-tailwind-css.mjs @@ -0,0 +1,64 @@ +// Asserts that the production build emitted compiled Tailwind v4 utility CSS. +// +// This is a regression guard for the bug fixed in PR #92 / issue #84: without a +// PostCSS config registering `@tailwindcss/postcss`, the `@import "tailwindcss"` +// directive in app/globals.css is never processed, so the build emits HTML with +// no Tailwind utility classes and the UI renders completely unstyled. +// +// Run AFTER `next build`. Scans the .next output for known utility classes that +// must be present whenever Tailwind v4 compilation is working. + +import { readdirSync, readFileSync, statSync } from 'node:fs' +import { join } from 'node:path' + +const BUILD_DIR = '.next' + +// Utilities used by the app's own components — if PostCSS processed Tailwind, +// these compile into real rules. If the config is missing, they are absent. +const REQUIRED_UTILITIES = ['.flex', '.grid', '.items-center', '.p-4', '.text-sm'] + +function collectCssFiles(dir) { + const out = [] + for (const entry of readdirSync(dir)) { + const full = join(dir, entry) + if (statSync(full).isDirectory()) { + out.push(...collectCssFiles(full)) + } else if (entry.endsWith('.css')) { + out.push(full) + } + } + return out +} + +function fail(message) { + console.error(`\n[assert-tailwind-css] FAIL: ${message}\n`) + process.exit(1) +} + +let cssFiles +try { + cssFiles = collectCssFiles(BUILD_DIR) +} catch { + fail(`build output "${BUILD_DIR}" not found — run \`pnpm build\` first.`) +} + +if (cssFiles.length === 0) { + fail('no CSS files emitted by the build — Tailwind/PostCSS is not running.') +} + +const allCss = cssFiles.map((f) => readFileSync(f, 'utf8')).join('\n') + +const missing = REQUIRED_UTILITIES.filter((cls) => !allCss.includes(`${cls}{`) && !allCss.includes(`${cls} `) && !allCss.includes(`${cls},`) && !allCss.includes(`${cls}:`)) + +if (missing.length > 0) { + fail( + `compiled CSS is missing expected Tailwind utilities: ${missing.join(', ')}.\n` + + 'This usually means postcss.config.mjs is missing or not registering ' + + '@tailwindcss/postcss, so `@import "tailwindcss"` was never processed.', + ) +} + +console.log( + `[assert-tailwind-css] OK: found Tailwind utilities (${REQUIRED_UTILITIES.join(', ')}) ` + + `in ${cssFiles.length} emitted CSS file(s).`, +)