README
¶
🧥.go
Tailwind gives you chills? Here’s a nice and warm coat.
Coat is a compiler that turns a minimal CSS utility class DSL (.coat files) into Go structs containing Tailwind-style class strings.
It provides a declarative way to define and reuse consistent styling, with inheritance, modifiers, and deep merging built in.
The syntax is compact and readable, designed for projects using templ or doors framework, but works anywhere Go runs.
It replaces long class="" strings with composable, type-safe style nodes:
@UI.Button.Primary // doors sugar syntax
<button>Click</button>
or in plain templ:
<button class={ UI.Button.Primary.Class() }>Click</button>
Each .coat file defines a tree of named style nodes.
All .coat files in the same Go package merge recursively into one coat.go file.
Modifiers like hover: or dark: apply automatically.
You can import, inherit, or extend nodes declaratively.
In short: Coat lets you write semantic, maintainable utility classes directly in Go, with Tailwind syntax and full composition control.
Coat CLI — Specification
Install
In the go project dir:
go get -tool github.com/doors-dev/coat
Usage
go tool coat [path] [flags]
Flags
-s, --standalone Generate files standalone (without Doors framework helpers)
-o, --output string Write collected utility classes list to file
-f, --force Force rebuild
-h, --help Show help
-v, --version Show version
Description
- Scans
path(or.) for.coatfiles. - Deep-merges nodes across files into a single
coat.goper package. - Skips locations ignored by
.gitignore. -somits Doors-specific helpers so output is usable in plain templ projects.-owrites a flat list of resolved utility classes (useful for Tailwind safelists).
Examples
Compile current directory:
go tool coat
Compile a subdir:
go tool coat ./ui
Compile and write class list:
go tool coat -o ./classes.txt
Generate without Doors helpers:
go tool coat -s
Coat DSL — Specification
1. Files, roots, and deep merge
Files
For any number of .coat files per Go package compiler generates one coat.go per package.
Roots
Each top-level node is a root (e.g., UI, Theme, Forms). Any non-keyword name is valid.
Deep merge
- Nodes with the same path across files are merged recursively.
- Class lines from the same node are appended;
Example across two files:
File A
UI {
Button {
Base {
px-3 py-2 rounded
}
}
}
File B
UI {
Button {
Base {
text-sm font-medium
}
Primary {
../Base
bg-sky-600 text-white
}
}
}
Resulting tree
UI.Button.Base -> px-3 py-2 rounded text-sm font-medium
UI.Button.Primary -> ../Base + bg-sky-600 text-white
2. Blocks, lines, and tokens
Blocks
{ ... }define nodes. Nesting = path.
Lines
- Inside a node, each line contains tokens.
- Tokens are utilities or modifiers.
- New line resets modifier state.
Utilities
- Tailwind classes:
px-3,text-gray-700,rounded-lg, etc. - Must be written on separate lines inside braces (no single-line inline lists).
✅ Valid:
Ul {
inline-flex -space-x-px
rtl:space-x-reverse text-sm h-8
}
(Utilities are space-separated, but the block uses its own line.)
❌ Invalid:
# disallowed
Ul { inline-flex -space-x-px rtl:space-x-reverse text-sm h-8 }
3. Modifiers
Definition
- A modifier is any token ending with
:(e.g.,hover:,focus:,active:,dark:,sm:,md:,lg:,xl:). - Modifiers can appear anywhere on a line.
- Modifiers on a line are accumulated left-to-right and prepended to each following non‑modifier on that same line.
- New line clears the modifier set.
Examples
Link {
Base {
hover: underline text-sky-500
}
}
# → hover:underline hover:text-sky-500
Button {
Primary {
hover: underline text-sky-800 dark: text-sky-200
}
}
# → hover:underline hover:text-sky-800 hover:dark:text-sky-200
Item {
Row {
sm: hover: bg-gray-100 text-gray-900
}
}
# → sm:hover:bg-gray-100 sm:hover:text-gray-900
Badge {
Base {
hover: bg-sky-600 text-white
# no hover: here (newline reset)
px-2 py-0.5
}
}
# → hover:bg-sky-600 hover:text-white px-2 py-0.5
Imports under modifiers
- You may inherit another node on a line with modifiers; the modifiers apply to the imported classes.
Link {
Accent {
hover: /style/text/accent underline
}
}
# If /style/text/accent → "text-sky-600 dark:text-sky-400"
# Resolved → "hover:text-sky-600 hover:dark:text-sky-400 hover:underline"
4. Inheritance (../)
Use ../Name to include classes defined in a sibling/parent node named Name.
Button {
Base {
px-3 py-2 rounded text-sm font-medium
}
Primary {
../Base
bg-sky-600 text-white
hover: bg-sky-700
}
}
../BasecopiesBaseclasses at compile time.- You can chain with modifiers on the same line:
hover: ../Base underline→ every class fromBasebecomeshover:*, plushover:underline. - Use
/for absolute path, `./
Relative include
../Namesearches upward from parent node./Namesearches upward from the current node
Absolute import
/a/b/creferences from the package style root. Example:/UI/Button/Base.
Merge behavior on import
- Imported classes are added to the current node’s class list
5. Visibility / export
- Node names map exactly to generated identifiers.
- Capitalized → exported (usable from other packages).
- lowercase → internal (package-only).
Examples
# exported
UI {
...
}
# internal
theme {
...
}
6. Comments
Starts with #, must be on the separate line.
✅ Valid:
Ul {
# good comment!
inline-flex -space-x-px
}
❌ Invalid:
Ul {
inline-flex -space-x-px # bad comment!
}
7. Using in templ
Doors framework (style injection helper)
- A node can be applied directly with
@Path.To.Node. - Multiple nodes can be accumulatedt.
@UI.Button.Primary
<button>Click</button>
Accumulating multiple styles
@UI.Button.Primary
@common.Style.Button
<button>Click</button>
All injected classes are concatenated in order of appearance.
Plain templ (portable)
<button class={ UI.Button.Primary.Class() }>Click</button>
.Class() returns the composed class string for that node.
8. Example
Shared base and modifiers
Li {
base {
select-none flex items-center justify-center px-2 h-8 leading-tight border
}
Enabled {
../base
bg-gray-700 border-gray-600 text-gray-200 cursor-pointer
hover: bg-gray-600 text-white
}
Disabled {
../base
bg-gray-700 border-gray-600 text-gray-200
}
}
Documentation
¶
There is no documentation for this package.