refactor: split build.py into brand package #12

Merged
brad merged 1 commit from refactor/brand-package into main 2026-02-27 14:53:33 -08:00
Owner

Summary

  • Split the 2,400-line scripts/build.py monolith into scripts/brand/ — a Python package with 17 focused modules
  • Each file handles one concern: color math, CSS generation, validation, individual export format writers, etc.
  • Entry points (build.py, validate.py) are now thin orchestrators that import from the package
  • Workflow path triggers updated from individual script files to scripts/** to catch module changes

What changed

scripts/
├── build.py              (rewritten — ~50 line orchestrator)
├── validate.py           (rewritten — ~30 line wrapper)
└── brand/
    ├── __init__.py
    ├── paths.py           — all path constants
    ├── colors.py          — color math, WCAG contrast
    ├── helpers.py         — text utilities (kebab, slugify, escape)
    ├── loader.py          — YAML loading
    ├── validation.py      — schema + structural validation
    ├── stage_json.py      — brand.json assembly
    ├── stage_css.py       — brand.css generation
    ├── stage_swatches.py  — MCP swatch PNG generation
    ├── stage_docs.py      — BRAND.md generation
    └── exports/
        ├── __init__.py    — stage_exports() orchestrator
        ├── ase.py         — Adobe Swatch Exchange
        ├── gpl.py         — GIMP Palette
        ├── scss.py        — SCSS variables
        ├── tokens.py      — W3C Design Tokens
        ├── procreate.py   — Procreate palette
        ├── tailwind.py    — Tailwind preset
        └── showcase.py    — HTML showcase

Why

  • Navigability: find what you need by filename instead of scrolling 2,400 lines
  • Extensibility: adding a new export format = one new file + one import line
  • Maintainability: changes to one format writer can't accidentally break another

Adding a new export format (after this PR)

  1. Create scripts/brand/exports/newformat.py with a write_newformat(path, brand) function
  2. Add one import + one call in scripts/brand/exports/__init__.py
  3. Done

Test plan

  • uv run scripts/build.py — full pipeline passes
  • uv run scripts/build.py validate — single-stage works
  • uv run scripts/validate.py — standalone validator works
  • All generated outputs are byte-for-byte identical to pre-refactor

🤖 Generated with Claude Code

## Summary - Split the 2,400-line `scripts/build.py` monolith into `scripts/brand/` — a Python package with 17 focused modules - Each file handles one concern: color math, CSS generation, validation, individual export format writers, etc. - Entry points (`build.py`, `validate.py`) are now thin orchestrators that import from the package - Workflow path triggers updated from individual script files to `scripts/**` to catch module changes ## What changed ``` scripts/ ├── build.py (rewritten — ~50 line orchestrator) ├── validate.py (rewritten — ~30 line wrapper) └── brand/ ├── __init__.py ├── paths.py — all path constants ├── colors.py — color math, WCAG contrast ├── helpers.py — text utilities (kebab, slugify, escape) ├── loader.py — YAML loading ├── validation.py — schema + structural validation ├── stage_json.py — brand.json assembly ├── stage_css.py — brand.css generation ├── stage_swatches.py — MCP swatch PNG generation ├── stage_docs.py — BRAND.md generation └── exports/ ├── __init__.py — stage_exports() orchestrator ├── ase.py — Adobe Swatch Exchange ├── gpl.py — GIMP Palette ├── scss.py — SCSS variables ├── tokens.py — W3C Design Tokens ├── procreate.py — Procreate palette ├── tailwind.py — Tailwind preset └── showcase.py — HTML showcase ``` ## Why - **Navigability**: find what you need by filename instead of scrolling 2,400 lines - **Extensibility**: adding a new export format = one new file + one import line - **Maintainability**: changes to one format writer can't accidentally break another ## Adding a new export format (after this PR) 1. Create `scripts/brand/exports/newformat.py` with a `write_newformat(path, brand)` function 2. Add one import + one call in `scripts/brand/exports/__init__.py` 3. Done ## Test plan - [x] `uv run scripts/build.py` — full pipeline passes - [x] `uv run scripts/build.py validate` — single-stage works - [x] `uv run scripts/validate.py` — standalone validator works - [x] All generated outputs are byte-for-byte identical to pre-refactor 🤖 Generated with [Claude Code](https://claude.com/claude-code)
refactor: split build.py monolith into brand package
All checks were successful
Validate brand.yml / validate (pull_request) Successful in 28s
f4cab2138f
Move 2,400 lines from scripts/build.py into scripts/brand/ — a proper
Python package with focused modules. Each file handles one concern:

  brand/colors.py       — color math (hex↔RGB/HSL/CMYK, WCAG contrast)
  brand/helpers.py      — text utilities (kebab-case, slugify, escape)
  brand/loader.py       — YAML loading
  brand/paths.py        — all path constants
  brand/validation.py   — schema + structural validation
  brand/stage_json.py   — brand.json assembly
  brand/stage_css.py    — brand.css generation
  brand/stage_swatches.py — MCP swatch PNG generation
  brand/stage_docs.py   — BRAND.md generation
  brand/exports/        — one module per format (ASE, GPL, SCSS, tokens,
                          Procreate, Tailwind, showcase)

Entry points (build.py, validate.py) are now thin orchestrators.
Workflow path triggers updated to scripts/** to catch module changes.

Adding a new export format: drop one file in brand/exports/ and add one
import + call in brand/exports/__init__.py.

All outputs are byte-for-byte identical after refactoring.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
brad merged commit 75f70e094f into main 2026-02-27 14:53:33 -08:00
brad deleted branch refactor/brand-package 2026-02-27 14:53:33 -08:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
brad/brand-guidelines!12
No description provided.