tailwind

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 7, 2023 License: MIT Imports: 7 Imported by: 0

README

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

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Converter

type Converter struct {
	// contains filtered or unexported fields
}

Converter does processing of CSS input files and writes a single output CSS file with the appropriate @ directives processed. Inputs are processed in the order they are added (see e.g. AddReader()).

func New

func New(out io.Writer, dist Dist) *Converter

New returns an initialized instance of Converter. The out param indicates where output is written, it must not be nil.

func (*Converter) AddReader

func (c *Converter) AddReader(name string, r io.Reader, isInline bool)

AddReader adds an input source. The name is used only in error messages to indicate the source. And r is the CSS source to be processed, it must not be nil. If isInline it indicates this CSS is from an HTML style attribute, otherwise it's from the contents of a style tag or a standlone CSS file.

func (*Converter) Run

func (c *Converter) Run() (reterr error)

Run performs the conversion. The output is written to the writer specified in New().

func (*Converter) SetPostProcFunc

func (c *Converter) SetPostProcFunc(f func(out io.Writer, in io.Reader) error)

SetPostProcFunc sets the function that is called to post-process the output of the converter. The typical use of this is for minification.

Example
var buf bytes.Buffer
conv := tailwind.New(&buf, twembed.New())
conv.SetPostProcFunc(func(out io.Writer, in io.Reader) error {
	m := minify.New()
	m.AddFunc("text/css", css.Minify)
	return m.Minify("text/css", out, in)
})
conv.AddReader("input.css", strings.NewReader(`.test1 { @apply font-bold; }`), false)
err := conv.Run()
if err != nil {
	panic(err)
}
fmt.Printf("%s", buf.String())

// notice the missing trailing semicolon
Output:

.test1{font-weight:700}

func (*Converter) SetPurgeChecker

func (c *Converter) SetPurgeChecker(purgeChecker PurgeChecker)

type Dist

type Dist interface {
	// OpenDist should return a new ReadCloser for the specific tailwind section name.
	// Valid names are "base", "utilities" and "components" (only those exact strings,
	// without .css or anything like that) and will be updated along with
	// what the TailwindCSS project does.  The caller is responsible for ensuring
	// Close() is called on the response if the error is non-nil.
	OpenDist(name string) (io.ReadCloser, error)
}

Dist is where tailwind CSS data can be read.

type PurgeChecker

type PurgeChecker interface {
	ShouldPurgeKey(k string) bool
}

PurgeChecker is something which can tell us if a key should be purged from the final output (because it is not used). See package twpurge for default implementation.

Directories

Path Synopsis
cmd
Package twembed contains an embedded copy of the TailwindCSS distribution.
Package twembed contains an embedded copy of the TailwindCSS distribution.
Package twfiles implements tailwind.Dist against a filesystem.
Package twfiles implements tailwind.Dist against a filesystem.
Package twhandler provides an HTTP handler that performs processing on CSS files and serves them.
Package twhandler provides an HTTP handler that performs processing on CSS files and serves them.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL