// Package tw is kjol's native Go Tailwind v4 compiler. // // It lives on its own, not inside the bundler, because Tailwind is not a // JavaScript concern. The bundler builds web/ (TSX, Solid, esbuild); the Tailwind // engine only ever reads text and writes CSS, and the text it reads is just as // likely to be Go — the gowasm kit authors its markup in Go and has no JS build at // all. Burying the compiler in the bundler made every Go-only consumer drag a // JavaScript bundler along for a CSS file. // // The scanner is language-agnostic: it pulls candidate class tokens out of any text // file, so `class="flex gap-2"` in a .tsx and `vdom.Attr("class", "flex gap-2")` in // a .go are found the same way. // // The engine itself is in tailwind.go — a from-scratch port of Tailwind v4's // compiler. This file is the surface the rest of kjol calls. package tw import ( "github.com/tdewolff/minify/v2" mincss "github.com/tdewolff/minify/v2/css" ) var minifier *minify.M func init() { minifier = minify.New() minifier.AddFunc("text/css", mincss.Minify) } // Scan extracts candidate utility class names from the files matched by patterns // (globs, relative to baseDir). It reads text, not syntax: a candidate is any token // that could plausibly be a class, and the compiler decides which ones actually are. func Scan(baseDir string, patterns []string) []string { return scanSources(baseDir, patterns) } // Compile compiles a Tailwind entry stylesheet against a set of candidates, and // returns the CSS along with how many utilities it emitted (useful for a build log — // a sudden drop usually means the scanner stopped seeing a source tree). // // entryCSS is the stylesheet source: typically `@import "tailwindcss";` plus an // `@theme { … }` block. Anything else in it — @font-face, plain rules — passes // through untouched. func Compile(entryCSS, baseDir string, candidates []string) (css string, utilities int, err error) { return twCompile(entryCSS, baseDir, candidates) } // CompileFiles is Scan + Compile + minify: the whole job, for a caller that just // wants CSS out of a stylesheet and some source globs. func CompileFiles(entryCSS, baseDir string, sourceGlobs []string) (string, error) { compiled, _, err := twCompile(entryCSS, baseDir, scanSources(baseDir, sourceGlobs)) if err != nil { return "", err } return Minify(compiled) } // CompileApp is Compile with kjol's shared extension layer prepended to entryCSS — // the layering every kjol front-end (Solid and gowasm) shares: // // base Tailwind → kjol extensions (kjol_theme.css) → the app's brand entryCSS // // The kjol layer does the `@import "tailwindcss"` and defines the semantic tokens, // the class-based dark variant and the chart palette, so entryCSS carries only // brand. Compile itself stays a plain Tailwind engine (no kjol tokens) for callers // that want exactly that. func CompileApp(entryCSS, baseDir string, candidates []string) (css string, utilities int, err error) { return twCompile(kjolThemeCSS+"\n"+entryCSS, baseDir, candidates) } // CompileAppFiles is Scan + CompileApp + minify: CompileFiles with the kjol // extension layer prepended. func CompileAppFiles(entryCSS, baseDir string, sourceGlobs []string) (string, error) { compiled, _, err := CompileApp(entryCSS, baseDir, scanSources(baseDir, sourceGlobs)) if err != nil { return "", err } return Minify(compiled) } // Minify shrinks compiled CSS. func Minify(css string) (string, error) { return minifier.String("text/css", css) }