coat

command module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2025 License: MIT Imports: 23 Imported by: 0

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 .coat files.
  • Deep-merges nodes across files into a single coat.go per package.
  • Skips locations ignored by .gitignore.
  • -s omits Doors-specific helpers so output is usable in plain templ projects.
  • -o writes 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
    }
}
  • ../Base copies Base classes at compile time.
  • You can chain with modifiers on the same line: hover: ../Base underline → every class from Base becomes hover:*, plus hover:underline.
  • Use / for absolute path, `./

Relative include

  • ../Name searches upward from parent node
  • ./Name searches upward from the current node

Absolute import

  • /a/b/c references 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

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

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