This is the next installment in my series on building an automation foundation for shipping personal projects at scale, following Getting Claude Code and Codex to collaborate on a single machine. This time I want to write about a setup I built that has AI generate a brand guide before you start any frontend implementation. Why "front-load" the guide? There's a pattern almost everyone falls into when building UI for a personal project: "Let me just implement something first and clean up the design later." And here's what happens. The code Claude generates always carries that default Tailwind vibe — the screen ends up buried in shadows, rounded corners, and uniform card grids, looking like something you've seen a hundred times before. When you tell it "clean and minimal," the design the AI reaches for is always the lowest common denominator. The problem is an imbalance of information. Without instructions, Claude implements with the library's default settings. When a component has no intent of its own, the framework's intent fills the void. Turn that around: hand the AI an intentional brand guide before implementation, and its output transforms completely. Decide up front the "this is the only direction" for each project, spell out the intent behind color, typography, motion, gradients, and grain, and then start implementing — the quality of the generated code changes by an order of magnitude. The tool that automates that "front-loading" is ~/dev/brand-kit/generate.sh. The big picture of brand-kit The structure of ~/dev/brand-kit/ is simple. brand-kit/ ├── generate.sh ← main script ├── lib/ │ └── strip_fence.py ← code-fence stripping utility ├── output/ │ └── / ← where generated files land └── README.md Enter fullscreen mode Exit fullscreen mode When you pass generate.sh a slug (a project identifier) and a brief (a one-to-three-line summary), up to four files get generated under output//. File Contents brand-guideline.md Worldview, style direction, palette intent, typography strategy, motion principles, Do & Don't tokens.css oklch-based CSS custom properties preview.html A single-HTML landing-page fragment that actually applies the tokens references.md (only with --refs) Real reference URLs gathered via WebSearch The basic usage is to hand this whole guide set to the implementer (Claude Code) and say "implement following this guide." With the guide in place first, Claude never has to agonize over choices. How you actually use it bash generate.sh "" [--refs] [--force] Enter fullscreen mode Exit fullscreen mode Let me run it on a fictional local-contractor project. cd ~/dev/brand-kit bash generate.sh marui-koumuten \ "板橋区の地域工務店のリニューアルHP。50〜60代施主向け。職人の手仕事と地域実績で問い合わせを取る" Enter fullscreen mode Exit fullscreen mode When it runs, three files are generated in sequence. [brand-kit] guideline生成 (試行1/3)… [brand-kit] ✓ brand-guideline.md [brand-kit] tokens生成 (試行1/3)… [brand-kit] ✓ tokens.css [brand-kit] preview生成 (試行1/3)… [brand-kit] ✓ preview.html [brand-kit] 完了: ~/dev/brand-kit/output/marui-koumuten - brand-guideline.md - tokens.css - preview.html → preview.html をブラウザで確認。方向が違えば概要を変えて --force で再生成。 Enter fullscreen mode Exit fullscreen mode If you don't like the direction, rewrite the summary and add --force to rebuild all the files. Add --refs and it also generates a references.md of real reference URLs gathered via WebSearch (which takes a bit more time). The heart of the design: why split it into 3 calls? Read generate.sh and you'll see it generates the guideline, tokens, and preview in three separate, independent calls. The internal call() helper looks like this. # 1コール実行ヘルパ: $1=ラベル $2=プロンプト $3=allowedTools call() { local label="$1" prompt="$2" tools="${3:-}" r="" local toolflag=(); [ -n "$tools" ] && toolflag=(--allowedTools "$tools") for attempt in 1 2 3; do echo "[brand-kit] $label (試行$attempt/3)…" >&2 r=$(timeout "$T" "$CLAUDE" -p "$prompt" "${toolflag[@]}" --model "$MODEL" /dev/null) [ -n "$r" ] && ! printf '%s' "$r" | grep -qiE \ 'usage limit|rate limit|session limit|hit your session|request timed out' \ && { printf '%s' "$r"; return 0; } sleep 3 done return 1 } Enter fullscreen mode Exit fullscreen mode Why not emit everything in a single call? The reason is clear. guideline.md alone is a substantial amount of content, and adding tokens.css and preview.html on top of it pushes the output past the max token count and cuts it off mid-stream. You end up with CSS that ends without a closing brace, or a broken HTML file with no . There's another benefit to splitting: later calls can take the output of earlier calls as input. GUIDE=$(head -c 6000 "$GFILE" 2>/dev/null) # ↑ guideline の先頭6000文字を tokens 生成プロンプトに埋め込む TOKENS=$(cat "$TFILE" 2>/dev/null) # ↑ tokens.css 全文を preview 生成プロンプトに埋め込む Enter fullscreen mode Exit fullscreen mode The guideline's intent carries into the tokens, and the tokens' values carry into the preview, consistently. The outputs don't scatter — the three files share the same worldview. Also, each file is skipped if it already exists. if [ "$FORCE" = 1 ] || [ ! -s "$TFILE" ]; then # tokens を生成 else echo "[brand-kit] = tokens.css (既存・skip)" fi Enter fullscreen mode Exit fullscreen mode This is the crux of idempotency. Even if it times out or fails partway through, re-running it fills in only the missing files. If brand-guideline.md succeeds but tokens.css fails, the next run skips brand-guideline.md and resumes from tokens.css. Pitfalls I hit Pitfall 1: forget /dev/null) Enter fullscreen mode Exit fullscreen mode Note When you call claude -p from a shell script or launchd, "$TFILE" Enter fullscreen mode Exit fullscreen mode Pitfall 4: cramming everything into one call cuts the output off In the first version I wrote a single prompt saying "emit the guideline, tokens, and preview all at once." The guideline came out fine. But tokens.css would end in the middle of :root {, or preview.html would stop without closing a tag. That's the output-token ceiling kicking in. The problem went away once I split it into three calls, each returning only one file. Splitting made each call shorter, so it finishes comfortably even within the default 240 seconds. The per-call timeout is adjustable via BRAND_KIT_TIMEOUT (default 240 seconds). The actual output: looking at brand-guideline.md Here's the style-direction section from a brand-guideline.md that was actually generated. ## 選んだ方向と理由 ### 墨付けエディトリアル(Sumitsuke Editorial) 「墨付け(すみつけ)」とは、大工が刃を入れる前に木材に墨壺で正確な基準線を引く所作—— 職人の仕事はこの一線から始まる。このサイトのデザイン言語は、その精度と誠実さを視覚に移植する。 和紙のような温かい紙質のベースに、重量のある明朝体見出しを置く。 グリッドを意図的に破り、写真は枠に入れず断ち切る。 グレインは「刷られた紙」の手触りとして機能する。 参照軸は、活版印刷の日本建築専門誌(新建築・住宅建築)の誌面。 なぜこの方向か(3つの根拠): 1. 「ミニマル」は空虚に見える:50-60代には「余白だらけ=やる気がない会社」と読まれるリスクがある 2. 明るい紙白が読みやすさを担保する:温和紙のような地色が50-60代の読み疲れを防ぐ 3. エディトリアルの非対称が「記憶に残る」:均一カードグリッドはどの工務店も使う。 7:5分割・テキストと写真の重なり・引き出し線が「ここは違う」と感じさせる Enter fullscreen mode Exit fullscreen mode Rather than "clean and minimal," it's a style direction with a proper name of its own: "Sumitsuke Editorial." It even includes the directions it didn't choose (Japanese modern, dark luxury, Swiss grid) and the reasons why, so the decision about direction remains on record. When the implementer reads this guide, judgments like "don't use card grids with corner radii of 8px or more," "the line-height for Mincho headings is 1.15," and "CTAs use only the burnt-cedar color, and the verdigris accent is reserved for pull-quote lines" become uniquely determined. With less room for interpretation in the instructions, there's less variance in the output. The actual output: looking at tokens.css Here's part of tokens.css. :root { /* ─── カラー ─────────────────────────────────────── 素材参照: 漆喰・墨・赤土・緑青の錆 / すべて oklch() */ --color-ground: oklch(96.5% 0.008 82); /* 温和紙・漆喰白 — 全体ベース */ --color-ground-alt: oklch(93.0% 0.012 79); /* 素焼きタイル面 — セクション交互 */ --color-ink: oklch(14.0% 0.006 255); /* 墨 — わずかに冷色 */ --color-accent: oklch(40.0% 0.068 165); /* 緑青 — プルクォートライン・リンク */ --color-cta: oklch(48.0% 0.052 60); /* 焼き杉色 — CTAの唯一の強調色 */ Enter fullscreen mode Exit fullscreen mode The colors are all defined with oklch(). oklch is a perceptually linear color space that expresses a color with three components: lightness L (0–100%), chroma C, and hue H. Unlike hsl, changing only the lightness while keeping the chroma the same doesn't muddy the color, which makes it easy to create derived colors for things like hover states. Fluid typography is implemented with clamp(). /* ─── 流体テキストスケール ────────────────────────── 50-60代ターゲット → ベースやや大きめ */ --text-base: clamp(1rem, 0.95rem + 0.45vw, 1.125rem); /* 16-18px 本文 */ --text-hero: clamp(3.5rem, 1.50rem + 8.93vw, 7.5rem); /* 56-120px ヒーロー */ Enter fullscreen mode Exit fullscreen mode In the clamp(minimum, preferred, maximum) form, the size changes smoothly according to the viewport width. The vw coefficient in the preferred value is computed so it reaches the minimum at 320px and the maximum at 1440px. The grain (paper texture) embeds an SVG feTurbulence filter directly into a CSS variable as a data URI. /* ─── グレイン ────────────────────────────────────── 刷られた紙の手触り。SVG feTurbulence / 200×200px タイル */ --grain-bg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)' opacity='0.06'/%3E%3C/svg%3E"); Enter fullscreen mode Exit fullscreen mode You can reproduce "the feel of printed paper" with no external file dependency. The noise generated by feTurbulence is grayscaled with feColorMatrix and layered thinly at opacity='0.06'. The guideline even spells out the places not to apply it (CTA buttons, input forms, on top of photos), and a comment that prevents misuse goes into tokens.css as well. Gradients, too, are turned into CSS variables, limited to three uses. /* ① ヒーロー背景: 木と陰(杉色 → 墨 / 対角) */ --gradient-hero: linear-gradient( 135deg, oklch(28% 0.055 68) 0%, oklch(12% 0.005 255) 100% ); /* ② ビネット: 写真下端の墨色フェード(テキスト乗せ用) */ --gradient-vignette: linear-gradient( to bottom, transparent 45%, oklch(14% 0.006 255 / 0.88) 100% ); /* ③ セクション上端: 赤土の気配 */ --gradient-section-top: linear-gradient( 180deg, oklch(67% 0.082 68 / 0.10) 0%, transparent 100% ); Enter fullscreen mode Exit fullscreen mode Because the gradient usage is explicitly stated as "limited to three places across the whole site," the implementer won't multiply them unnecessarily. How to hand it off to implementation There are three patterns for using the generated guide. Pattern 1: hand it directly to Claude Code output/marui-koumuten/ フォルダを「このガイドに従って実装して」と伝える。 Enter fullscreen mode Exit fullscreen mode Give Claude Code the guide's path and then ask it to implement, and the implementation of color, typography, and grain is generated in line with the guide. A Claude Code that has read the guide can autonomously decide "which color variable to use," "which variable on hover," and "where to apply the grain." Pattern 2: polish it on claude.ai Paste preview.html and tokens.css into claude.ai and adjust in natural language. Open preview.html in a browser, point out what bothers you through conversation, and refine the design. Pattern 3: turn it into a card with /design-sync The prompt in generate.sh includes an instruction to emit as the first line of preview.html. Sync this to a design-system Project on claude.ai with the /design-sync skill, and subsequent Claude Design generations inherit this project's tokens and stay consistent (using /design-sync requires being logged in to claude.ai). Tuning the model and timeout There are environment-variable settings at the top of generate.sh. CLAUDE="${CLAUDE:-~/.local/bin/claude}" MODEL="${BRAND_KIT_MODEL:-sonnet}" T="${BRAND_KIT_TIMEOUT:-240}" # 1コールあたりのタイムアウト(秒) Enter fullscreen mode Exit fullscreen mode If you prioritize speed, you can switch to haiku. The depth of thinking in the guide drops, but generation gets faster. # haiku で速度優先 BRAND_KIT_MODEL=haiku bash generate.sh marui-koumuten "..." # タイムアウトを伸ばす(低速ネット環境など) BRAND_KIT_TIMEOUT=360 bash generate.sh marui-koumuten "..." Enter fullscreen mode Exit fullscreen mode The generation cost is completely free through claude -p (the Claude MAX allowance). No API key or billing setup is required. Summary Front-loading the guide before implementation achieves de-templatization of AI-generated code generate.sh is a 3-independent-call design of guideline → tokens → preview. Emit everything in one call and the output gets cut off mid-stream claude -p requires </dev/null when run in the background. Forget it and it freezes waiting on stdin It detects session-limit error strings with grep and guards against writing broken files strip_fence.py removes code fences and prefatory prose, saving the CSS and HTML in a form you can use as-is Each file is skipped if it exists — idempotency means a re-run fills in only the missing pieces tokens.css defines oklch(), clamp(), and the feTurbulence grain all as CSS variables. Gradients are limited to three places across the whole site The generation cost is completely free on the Max allowance Written by **Lily* — I ship iOS apps and automate my content stack with Claude Code. Follow along: Portfolio · X · GitHub*
Front-load your brand guide to AI before you write a line of frontend code
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.