/tailwind

A Tailwind CSS implementation in Go

Primary LanguageGoMIT LicenseMIT

A Tailwind CSS implementation in Go

This project provides an implementation of Tailwind CSS functionality in pure Go. It includes the ability to embed an HTTP handler which processes Tailwind directives on the fly, facilities to purge unneeded styles, and a command line tool.

Documentation

Godoc can be found in the usual place: https://pkg.go.dev/github.com/gotailwindcss/tailwind?tab=doc

Typical Usage

For development, the typical use is to integrate the handler found in twhandler so Tailwind CSS processing is done as your CSS file is served. Example:

main.go

// ...
import "github.com/gotailwindcss/tailwind/twembed"
import "github.com/gotailwindcss/tailwind/twhandler"

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", http.FileServer(http.Dir("static")))
	mux.Handle("/css/", twhandler.New(http.Dir("css"), "/css", twembed.New()))

	s := &http.Server{Addr: ":8182", Handler: mux}
	log.Fatal(s.ListenAndServe())
}

static/index.html

<html>
    <head><link rel="stylesheet" href="/css/main.css"/></head>
    <body><a href="#" class="button">Test Button</a></body>
</html>

css/main.css

@tailwind base;
@tailwind components;
.button { @apply inline-block m-2 p-2 rounded-md bg-green-400; }
@tailwind utilities;

In Production

In production we recommend you use a simple static file server whever possible, e.g. http.FileServer(distDir).

See Procesing CSS Files below for more info on how to create output from the command line, or Library Usage for how to perform Tailwind CSS conversion from Go.

Supported Tailwind CSS Directives

The following Tailwind directives are supported:

  • @tailwind
  • @apply

These are intended to work with the same behavior as the Tailwind project. If differences are encountered/necessary this section will be updated as applicable.

Command Line

To install the gotailwindcss command, do:

go get github.com/gotailwindcss/tailwind/cmd/gotailwindcss

Once installed, for help:

gotailwindcss --help

Processing CSS Files

Use the build subcommand to perform processing on one or more CSS files.

gotailwindcss build -o out.css in1.css in2.css

Library Usage

This project is organized into the following packages:

  • tailwind - Handles CSS conversion and Tailwind processing logic
  • twhandler - HTTP Handler for processing CSS files
  • twpurge - Handles purging unused style rules
  • twembed - Contains an embedded copy of Tailwind CSS styles
  • twfiles - Facilitates using a directory as source for Tailwind CSS styles

Embedded TailwindCSS

To process "convert" files, a "Dist" (distribution) of Tailwind CSS is required. The twembed package provides this. Importing it embeds this data into your application, which is usually file for server applications.

Calling twembed.New() will return a new Dist corresponding to this embedded CSS. It is intentionally inexpensive to call and there is no need to retain an instance as opposed ot calling twembed.New() again.

Performing Conversion

A tailwind.Convert is used to perform processing of directives like @tailwind and @apply. Example:

var w bytes.Buffer
conv := tailwind.New(&w, twembed.New())
conv.AddReader("base.css", strings.NewReader(`@tailwind base;`), false)
err := conv.Run()
// w now has the processed CSS output

HTTP Handler

The twhandler package has an HTTP handler intended to be useful during development by performing CSS processing on the fly as the file is requested. Creating a handler is simple:

h := twhandler.New(
	http.Dir("/path/to/css"), // directory from which to read input CSS files
	"/css",                   // HTTP path prefix to expect
	twembed.New(),            // Tailwind distribution
)

From there it is used like any other http.Handler.

Compression

The SetWriteCloserFunc can be used in conjunction with brotli.HTTPCompressor in order to enable brotli and gzip compression. Example:

h := twhandler.New(http.Dir("/path/to/css"), "/css", twembed.New())
h.SetWriteCloserFunc(brotli.HTTPCompressor)
// ...

Caching

By default, caching is enabled on handlers created. Meaning the same output will be served without re-processing as long as the underlying input CSS file's timestamp is not modified.

And by default, responses do not have a browser caching max-age, so each load results in a new request back to the server to check for a modified file. This can be adjusted with SetMaxAge if needed.

Purging

TODO: write doc and example

See Also

This project was created as part of research while developing Vugu (doc).

Roadmap

  • Command line build tool
  • Pure Go library, no npm/node dependency
  • HTTP Handler
  • Purge functionality to minimize output file size
  • Test server for prototyping