generates tailwind merges and classes from go templ sources
You can install this library using the following command:
go get github.com/conneroisu/twerge@latest
You can then use twerge
in your .templ
files like so:
package views
import "github.com/conneroisu/twerge"
templ ExampleTempl() {
<div class=twerge.It("bg-blue-500 text-blue-700")>
<h1 class=twerge.It("text-2xl font-bold")>Hello, world!</h1>
</div>
}
This will generate the following CSS in a code generation step:
/* twerge:begin */
.tw-1 {
@apply bg-blue-500 text-blue-700;
}
.tw-2 {
@apply text-2xl font-bold;
}
/* twerge:end */
To generate the CSS, you can use the CodeGen
function:
import "github.com/conneroisu/twerge"
func main() {
g := twerge.Default()
err := g.CodeGen(
g,
"views/view.go",
"views/views.css",
"views/views.html",
views.ExampleTempl(),
)
if err != nil {
panic(err)
}
}
With a working nix and direnv installed, run the following commands to get started:
direnv allow
Project structure:
./.
├── assets
│ └── twerge.png
├── benchmarks
│ └── regexs
│ └── main_test.go
├── CLAUDE.md
├── doc # documentation
│ ├── book.toml # book configuration
│ └── src # documentation source
│ └── ...
├── doc.go
├── examples # examples
├── flake.lock
├── flake.nix
├── go.mod # go module
├── go.sum # go module checksums
├── internal
│ └── files # utilities for working with files
│ ├── doc.go
│ ├── interpolate.go
│ ├── jen.go
│ └── paths.go
├── LICENSE # license
├── LLMS.txt # notes for llms
├── README.md # this file
├── config.go # twerge configuration
├── config_test.go # twerge configuration tests
├── twerge.go # twerge implementation
├── twerge_test.go # twerge tests
└── tw.go # twerge code generation implementations
import "github.com/conneroisu/twerge"
Package twerge provides a tailwind merger for go-templ with class generation and runtime static hashmap.
Twerge optimizes TailwindCSS usage in Go templ applications by intelligently merging Tailwind classes, resolving conflicts according to Tailwind's precedence rules, and generating short, unique class names for improved runtime performance and CSS output size.
It performs four key functions:
- Merges TailwindCSS classes intelligently (resolving conflicts between competing classes)
- Generates short unique CSS class names from the merged classes (e.g., "tw-1", "tw-2")
- Creates a mapping from original class strings to generated class names
- Provides a code generation step for efficient runtime lookups
In your .templ files:
import "github.com/conneroisu/twerge"
templ Button(primary bool) {
<button class={twerge.It("px-4 py-2 rounded " + twerge.If(primary, "bg-blue-500 text-white", "bg-gray-200 text-gray-800"))}>
{ children... }
</button>
}
When multiple Tailwind classes that target the same CSS property are provided, twerge will intelligently merge them according to Tailwind's precedence rules:
// Input: "text-red-500 bg-blue-500 text-blue-700"
// Output: "bg-blue-500 text-blue-700"
// (text-blue-700 takes precedence over text-red-500)
For optimal runtime performance, twerge includes a code generation step that creates:
-
A CSS file with all the generated classes
-
A Go file with a static map for class lookups
-
An HTML file for rendering
g := twerge.Default() err := g.CodeGen( g, "views/gen_twerge.go", // Generated Go file path "views/twerge.css", // Generated CSS file path "views/twerge.html", // Generated HTML file path views.Button(true), // Components to analyze views.Card(), // ... other components )
The generated CSS will look like:
/* twerge:begin */
/* from px-4 py-2 rounded bg-blue-500 text-white */
.tw-1 {
@apply px-4 py-2 rounded bg-blue-500 text-white;
}
/* from px-4 py-2 rounded bg-gray-200 text-gray-800 */
.tw-2 {
@apply px-4 py-2 rounded bg-gray-200 text-gray-800;
}
/* twerge:end */
## Basic Functions
// It returns a short unique CSS class name from the merged classes.
// This is the most commonly used function in templates.
func It(raw string) string
// If returns a class based on a condition.
// Useful for conditional styling.
func If(ok bool, trueClass string, falseClass string) string
// CodeGen generates all the code needed to use Twerge statically.
func CodeGen(g *Generator, goPath string, cssPath string, htmlPath string, comps ...templ.Component) error
## Generator
The Generator struct provides more advanced functionality:
// Default returns the default Generator instance.
func Default() *Generator
// New creates a new Generator with the given non-nil Handler.
func New(h Handler) *Generator
// Cache returns the cache of the Generator.
func (Generator) Cache() map[string]CacheValue
// It returns a short unique CSS class name from the merged classes.
func (g *Generator) It(classes string) string
## Configuration
Although most users will use the default configuration, customization is possible by implementing the Handler interface:
type Handler interface {
It(string) string
Cache() map[string]CacheValue
SetCache(map[string]CacheValue)
}
Twerge uses a sophisticated algorithm to:
- Parse and understand Tailwind class semantics
- Identify and resolve conflicting classes
- Handle complex class relationships (e.g., inset-x-1 and left-1)
- Support arbitrary values, modifiers, and variants
- Maintain proper precedence of important (!) modifiers
- Run CodeGen as part of your build process 2. Use twerge.It() for all Tailwind classes in your templates 3. Use twerge.If() for conditional class application 4. Avoid manual string concatenation for class names when possible
A typical development workflow with twerge:
- Write your templ components using twerge.It() for class handling 2. During build or development, run CodeGen to generate static assets 3. Include the generated CSS in your application 4. The templ components will use the generated optimized class names at runtime
- func CodeGen(g *Generator, goPath string, cssPath string, htmlPath string, comps ...templ.Component) error
- func If(ok bool, trueClass string, falseClass string) string
- func It(raw string) string
- func SetDefault(g *Generator)
- type CacheValue
- type Generator
- type Handler
func CodeGen
func CodeGen(g *Generator, goPath string, cssPath string, htmlPath string, comps ...templ.Component) error
CodeGen generates all the code needed to use Twerge statically.
func If
func If(ok bool, trueClass string, falseClass string) string
If returns a short unique CSS class name from the merged classes taking an additional boolean parameter.
func It
func It(raw string) string
It returns a short unique CSS class name from the merged classes.
func SetDefault
func SetDefault(g *Generator)
SetDefault sets the default Generator.
type CacheValue
CacheValue contains the value of a cache entry.
It is used to store the generated and merged classes.
As twerge is meant to be used statically, aka at build/compile time, it is trying to maxmimize performance at runtime.
type CacheValue struct {
// Generated is the generated class. It is a short unique CSS class name.
//
// Example: tw-123
Generated string
// Merged is the merged class. It is the final class name that is used in the CSS.
//
// Example: min-h-screen bg-gray-50 text-gray-900 flex flex-col
Merged string
}
type Generator
Generator generates all the code needed to use Twerge statically.
At runtime, it uses the statically defined code, if configured, to map the class names to the generated class names.
type Generator struct{ Handler Handler }
func Default
func Default() *Generator
Default returns the default Generator.
func New
func New(h Handler) *Generator
New creates a new Generator with the given non-nil Handler.
func (Generator) Cache
func (Generator) Cache() map[string]CacheValue
Cache returns the cache of the Generator.
func (*Generator) It
func (g *Generator) It(classes string) string
It returns a short unique CSS class name from the merged classes.
If the class name already exists, it will return the existing class name.
If the class name does not exist, it will generate a new class name and return it.
type Handler
Handler is the interface that needs to be implemented to customize the behavior of the Generator.
type Handler interface {
It(string) string
Cache() map[string]CacheValue
SetCache(map[string]CacheValue)
}
Generated by gomarkdoc