Skip to content

Upgrade Implementation Plan (v3)

Implement the formal brand system architecture where brands are infrastructure (owned at monorepo level) and sites are consumers. Logos are created as semantic, theme-agnostic SVG files using currentColor and semantic classes. Brand-specific colors are applied via CSS variables in wrapper components.

KEY UPDATE (v3): The S ↔ $D animation applies to ALL SD logo variants (circle, leaf-dot, leaf-square, leaf-rect), not just the circle variant. Every SD SVG must contain S, $, and D character paths.

  1. Brands are infrastructure - Live at monorepo root, not in sites

  2. Logos are semantic - Use currentColor with semantic classes (.logo-bg, .logo-primary, .logo-secondary)

  3. Colors are external - Applied via brand-specific CSS variables in wrapper components

  4. Animation for ALL SD variants - Every SD logo (all backgrounds) supports S ↔ $D animation

  5. Template demonstrates - Never owns or defines brand assets

ALL SmartDebt logo variants must support the S ↔ $D animation:

  • sd-circle.svg - Animated ✓
  • sd-leaf-dot.svg - Animated ✓
  • sd-leaf-square.svg - Animated ✓
  • sd-leaf-rect.svg - Animated ✓

Why: User wants animation available for every background variation. The background shape changes between variants, but the character structure (S, $, D) remains identical.

Every SD logo SVG must contain:

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <!-- Background varies: circle, leaf, square, rect --> <path class="logo-bg" d="..." fill="currentColor"/> <!-- Characters are IDENTICAL across all variants --> <path class="logo-primary logo-s" d="..." fill="currentColor"/> <path class="logo-primary logo-dollar" d="..." fill="currentColor"/> <path class="logo-primary logo-d" d="..." fill="currentColor"/> <!-- Optional secondary accent (varies by type) --> <path class="logo-secondary" d="..." fill="currentColor"/> </svg>

Inkscape workflow: Create the S, $, D character paths once, then copy them identically into all four background variations. Only the background shape and optional accents change.

  • Logo defined inline in BrandLogo.astro component (no asset files)

  • Existing logo SVGs have hardcoded colors and wrong viewBox (256x256)

  • Color system at Template level

  • No formal brand separation (SD vs TS)

  • No brand contract

provides baseprovides baseimportsMonorepo RootBrand Layersrc/brand/sd/SmartDebt Brandts/TalbotStevens Brandlogos/svg-paths-base/sd-circle.svgsd-leaf-dot.svgsd-leaf-square.svgsd-leaf-rect.svgALL with S,$,D pathscolors.oklch.jsonBrand palettestokens.cssGenerated CSSindex.tsBrand Contractlogos/svg-paths-base/ts-circle.svgcolors.oklch.jsontokens.cssindex.tsShared Tokenssrc/design-tokens/stone, gray palettesTemplate SiteDemonstrates all SD variantswith animation

Each SVG file contains paths with semantic classes and currentColor:

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> <!-- Background shape (circle, leaf, square, etc.) --> <path class="logo-bg" d="..." fill="currentColor"/> <!-- Primary marks (S, D characters) --> <path class="logo-primary logo-s" d="..." fill="currentColor"/> <path class="logo-primary logo-d" d="..." fill="currentColor"/> <!-- Dollar sign for animation (all SD variants) --> <path class="logo-primary logo-dollar" d="..." fill="currentColor"/> <!-- Secondary accent (leaf, dot, highlights) --> <path class="logo-secondary" d="..." fill="currentColor"/> </svg>

Key principles:

  • NO hardcoded colors

  • NO brand-specific names in SVG

  • NO CSS inside SVG

  • Uses currentColor to inherit from wrapper

  • Semantic classes describe role, not brand

The wrapper component maps brand tokens to semantic classes:

/* SD brand wrapper */ .sd-logo { /* Base color for .logo-bg and .logo-primary */ color: var(--sd-logo-primary); /* Secondary color as CSS variable */ --logo-secondary-color: var(--sd-logo-secondary); } /* Target secondary elements */ .sd-logo .logo-secondary { color: var(--logo-secondary-color); }

Design tokens define brand-specific colors that adapt to theme:

/* Light theme */ [data-theme="light"] { --sd-logo-primary: hsl(var(--primary-8)); /* Treasury Emerald */ --sd-logo-secondary: hsl(var(--secondary-7)); /* Wealth Gold */ } /* Dark theme */ [data-theme="dark"] { --sd-logo-primary: hsl(var(--primary-2)); /* Light tint */ --sd-logo-secondary: hsl(var(--secondary-3)); }

Result: Same SVG works for all themes, all brands, infinite color variations.


Create the following directories:

src/ brand/ sd/ logos/ svg-paths-base/ (logo SVG files go here) favicons/ ts/ logos/ svg-paths-base/ (logo SVG files go here) favicons/ design-tokens/ (shared tokens move here)

Move shared color palettes (stone, gray) from sites/Template/src/design-tokens/ to src/design-tokens/:

  • Create src/design-tokens/colors.oklch.json with only stone and gray palettes

  • These become shared infrastructure, not Template-owned

  • Generator script will merge these with brand-specific colors


Phase 2: Create Logo SVG Files (Inkscape → SVG)

Section titled “Phase 2: Create Logo SVG Files (Inkscape → SVG)”

For each logo variant, in Inkscape:

  1. Design with visual distinction:
  • Primary shapes (characters, main elements) → black fill

  • Secondary shapes (accents, highlights) → mid-gray fill

  • Background shapes → light gray fill

  1. Convert all text and objects to paths

  2. Set document to 100×100 units

  3. Normalize viewBox to viewBox="0 0 100 100"

  4. Export as Plain SVG

For each exported SVG, manually edit to apply semantic structure:

Before (Inkscape output):

<path d="..." fill="#000000"/> <path d="..." fill="#808080"/> <circle cx="50" cy="50" r="50" fill="#cccccc"/>

After (semantic version):

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> <circle class="logo-bg" cx="50" cy="50" r="50" fill="currentColor"/> <path class="logo-primary logo-s" d="..." fill="currentColor"/> <path class="logo-primary logo-dollar" d="..." fill="currentColor"/> <path class="logo-primary logo-d" d="..." fill="currentColor"/> <path class="logo-secondary" d="..." fill="currentColor"/> </svg>

Create these files in src/brand/sd/logos/svg-paths-base/:

  1. sd-circle.svg - SD logo with circular background

  2. sd-leaf-dot.svg - SD logo with leaf shape and dot accent

  3. sd-leaf-square.svg - SD logo with leaf shape and square background

  4. sd-leaf-rect.svg - SD logo with leaf shape and rectangular background

CRITICAL: Animation Support for ALL SD Variants

Every SD logo variant must contain ALL three character paths (S, $, D) to support the S ↔ $D animation:

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <!-- Background shape (varies by variant: circle, leaf, square, rect) --> <path class="logo-bg" d="..." fill="currentColor"/> <!-- OR --> <circle class="logo-bg" cx="50" cy="50" r="50" fill="currentColor"/> <!-- Base S character (visible by default, opacity animated) --> <path class="logo-primary logo-s" d="..." fill="currentColor"/> <!-- Dollar sign (hidden by default, fades in/out for animation) --> <path class="logo-primary logo-dollar" d="..." fill="currentColor"/> <!-- D character (always visible) --> <path class="logo-primary logo-d" d="..." fill="currentColor"/> <!-- Optional: Secondary accent (dot, highlight, etc.) --> <path class="logo-secondary" d="..." fill="currentColor"/> </svg>

Why all variants need all three characters:

  • User wants S ↔ $D animation available for every background variation

  • Each logo type (circle, leaf-dot, etc.) must be animation-ready

  • Component decides whether to activate animation via animate prop

  • Background shape changes, but character structure stays consistent

Design implication: When creating variants in Inkscape, the S, $, D character paths should be copied identically across all four background variations. Only the background shape and optional secondary accents change between variants.

Create in src/brand/ts/logos/svg-paths-base/:

  1. ts-circle.svg - TS logo with circular background
  • Circle background: .logo-bg

  • T character: .logo-primary

  • S character: .logo-primary

  • No animation needed (TS doesn’t have dollar variant)

Delete obsolete files in src/assets/brand/logos/:

  • logo_$D-paths.svg

  • logo_$D-text.svg

  • logo_SD-paths.svg

  • logo_SD-text.svg

  • logo_TS-paths.svg

  • logo_TS-text.svg


Create src/design-tokens/colors.oklch.json:

{ "meta": { "name": "Shared Design Tokens", "version": "1.0.0", "description": "Neutral palettes shared across all brands" }, "palettes": { "stone": { "name": "Warm Stone", "anchor": "#4E4640", "anchorPosition": 7, "semanticMappings": { /* ... */ } }, "gray": { "name": "Pure Gray", "anchor": "#484848", "anchorPosition": 7, "semanticMappings": { /* ... */ } } } }

Create src/brand/sd/colors.oklch.json:

{ "meta": { "name": "SmartDebt", "version": "1.0.0", "brandId": "sd", "inkscapePaletteName": "SmartDebt" }, "palettes": { "primary": { "name": "Treasury Emerald", "anchor": "#004425", "anchorPosition": 8, "semanticMappings": { "8": ["light:primary", "dark:primary"], "2": ["light:logo-primary-on-dark"], "8": ["dark:logo-primary-on-light"] } }, "secondary": { "name": "Wealth Gold", "anchor": "#D5B038", "anchorPosition": 7, "semanticMappings": { "7": ["light:secondary"], "3": ["light:logo-secondary-on-dark"] } }, "tertiary": { "name": "Financial Red", "anchor": "#DD2729", "anchorPosition": 6, "semanticMappings": { /* ... */ } } } }

Create src/brand/ts/colors.oklch.json:

{ "meta": { "name": "TalbotStevens", "version": "1.0.0", "brandId": "ts" }, "palettes": { "primary": { "name": "Professional Blue", "anchor": "#0053B3", "anchorPosition": 8, "semanticMappings": { "8": ["light:primary", "dark:primary"] } } // Inherits secondary/tertiary from SmartDebt or defines own } }

Modify sites/Template/scripts/generate-color-system.mjs:

Add brand flag support:

// Parse command line args const brandFlag = process.argv.indexOf('--brand'); const brand = brandFlag > -1 ? process.argv[brandFlag + 1] : 'sd'; // Read brand-specific colors const brandColorPath = resolve(ROOT, '..', '..', 'src', 'brand', brand, 'colors.oklch.json'); const sharedColorPath = resolve(ROOT, '..', '..', 'src', 'design-tokens', 'colors.oklch.json'); const brandColors = JSON.parse(readFileSync(brandColorPath, 'utf-8')); const sharedColors = JSON.parse(readFileSync(sharedColorPath, 'utf-8')); // Merge: brand colors override shared const colorConfig = { meta: brandColors.meta, palettes: { ...sharedColors.palettes, ...brandColors.palettes } };

Output to brand folder:

const outputPaths = { tokensCSS: resolve(ROOT, '..', '..', 'src', 'brand', brand, 'tokens.css'), // Keep theme files at Template level for now baseCSS: resolve(ROOT, 'src', 'styles', 'themes', 'base.css'), // ... };

Update package.json scripts:

{ "scripts": { "colors": "node ./scripts/generate-color-system.mjs --brand sd", "colors:sd": "node ./scripts/generate-color-system.mjs --brand sd", "colors:ts": "node ./scripts/generate-color-system.mjs --brand ts" } }

Run the generator for both brands:

pnpm run colors:sd pnpm run colors:ts

This creates:

  • src/brand/sd/tokens.css
  • src/brand/ts/tokens.css

Create src/brand/sd/index.ts:

export const brand = { id: 'sd', name: 'SMART DEBT', displayName: 'SmartDebt', // Lazy-loaded tokens tokens: () => import('./tokens.css'), // Logo variants (all support S ↔ $D animation) logos: { circle: () => import('./logos/svg-paths-base/sd-circle.svg?raw'), leafDot: () => import('./logos/svg-paths-base/sd-leaf-dot.svg?raw'), leafSquare: () => import('./logos/svg-paths-base/sd-leaf-square.svg?raw'), leafRect: () => import('./logos/svg-paths-base/sd-leaf-rect.svg?raw'), }, // Color anchors (for reference/documentation) colors: { primary: '#004425', // Treasury Emerald secondary: '#D5B038', // Wealth Gold tertiary: '#DD2729', // Financial Red }, // Logo CSS variable mappings logoVars: { light: { primary: 'var(--primary-8)', secondary: 'var(--secondary-7)', }, dark: { primary: 'var(--primary-2)', secondary: 'var(--secondary-3)', }, }, } as const; export type Brand = typeof brand;

Create src/brand/ts/index.ts:

export const brand = { id: 'ts', name: 'Talbot Stevens', displayName: 'TalbotStevens', tokens: () => import('./tokens.css'), logos: { circle: () => import('./logos/svg-paths-base/ts-circle.svg?raw'), }, colors: { primary: '#0053B3', // Professional Blue secondary: '#D5B038', // Inherited Wealth Gold }, logoVars: { light: { primary: 'var(--primary-8)', secondary: 'var(--secondary-7)', }, dark: { primary: 'var(--primary-2)', secondary: 'var(--secondary-3)', }, }, } as const; export type Brand = typeof brand;

Update tsconfig.json:

{ "compilerOptions": { "baseUrl": ".", "paths": { "@brand/*": ["src/brand/*"], "@components/*": ["packages/components/*"], "@design-tokens/*": ["packages/design-tokens/*"], "@utils/*": ["packages/utils/*"] } } }

Update sites/Template/tsconfig.json to reference monorepo root config and add local alias:

{ "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@brand/*": ["../../src/brand/*"] } } }


Create simplified favicon sources:

  • src/brand/sd/favicons/sd-favicon-source.svg - Simplified SD at 600 weight

  • src/brand/ts/favicons/ts-favicon-source.svg - Simplified TS at 600 weight

These should be based on the circle logos but optimized for 16×16 and 32×32 sizes (possibly with slightly bolder strokes).

Update sites/Template/scripts/generate-favicon.mjs:

// Read brand from site.config.json const brandId = config.brand?.id || 'sd'; // Read favicon source from brand folder const faviconSourcePath = resolve( ROOT, '..', '..', 'src', 'brand', brandId, 'favicons', `${brandId}-favicon-source.svg` ); const faviconSvg = readFileSync(faviconSourcePath, 'utf-8'); // Apply colors from config const primaryColor = config.brand.favicon.textColor || '#004425'; // Process SVG to apply colors... // Write to public/ writeFileSync(resolve(ROOT, 'public', 'favicon.svg'), processedSvg);

Update sites/Template/site.config.json:

{ "brand": { "id": "sd", "favicon": { "textColor": "#004425" } } }


Phase 6: Refactor BrandLogo Component (CRITICAL - Preserve Animation)

Section titled “Phase 6: Refactor BrandLogo Component (CRITICAL - Preserve Animation)”

The existing animation in sites/Template/src/components/brand/BrandLogo.astro:

  • Has inline SVG paths for S, $, and D

  • Uses CSS @keyframes show-dollar to animate opacity of $ character

  • S and D are always visible

  • $ fades in/out on 4-second cycle

  • Animation pauses on hover

  • Respects prefers-reduced-motion

This functionality MUST be preserved for ALL SD logo variants.

Strategy: Import SVG as raw string, inject into DOM, apply CSS classes for animation.

Create new sites/Template/src/components/brand/BrandLogo.astro:

--- interface Props { variant?: 'smart-debt' | 'talbot-stevens'; logoType?: 'circle' | 'leaf-dot' | 'leaf-square' | 'leaf-rect'; animate?: boolean; class?: string; } const { variant = 'smart-debt', logoType = 'circle', animate = false, class: className = '' } = Astro.props; // Import brand contracts const { brand: sdBrand } = await import('@brand/sd'); const { brand: tsBrand } = await import('@brand/ts'); // Select brand const currentBrand = variant === 'smart-debt' ? sdBrand : tsBrand; // Select logo variant (dynamically) const logoImporter = currentBrand.logos[logoType]; if (!logoImporter) { throw new Error(`Logo type "${logoType}" not found for brand "${currentBrand.id}"`); } // Import SVG as raw string const logoSvg = await logoImporter(); // Generate unique ID for this instance (for CSS scoping) const instanceId = `logo-${currentBrand.id}-${Math.random().toString(36).slice(2, 9)}`; const ariaLabels = { 'smart-debt': 'Smart Debt Logo', 'talbot-stevens': 'Talbot Stevens Logo' } as const; // Animation applies to ALL SD logos when animate=true const canAnimate = variant === 'smart-debt'; --- <div id={instanceId} class:list={[ "brand-logo-wrapper", `${currentBrand.id}-logo`, animate && canAnimate && "brand-logo--animate", className ]} role="img" aria-label={ariaLabels[variant]} set:html={logoSvg} /> <style> .brand-logo-wrapper { width: 100%; height: auto; display: block; } .brand-logo-wrapper :global(svg) { width: 100%; height: auto; display: block; } /* SD brand color mapping */ .sd-logo { /* Primary color for background and main characters */ color: var(--sd-logo-primary, hsl(var(--primary-8))); } .sd-logo :global(.logo-secondary) { color: var(--sd-logo-secondary, hsl(var(--secondary-7))); } /* TS brand color mapping */ .ts-logo { color: var(--ts-logo-primary, hsl(var(--primary-8))); } .ts-logo :global(.logo-secondary) { color: var(--ts-logo-secondary, hsl(var(--secondary-7))); } /* Animation for SD logo (S ↔ $D) - applies to ALL SD variants */ .sd-logo :global(.logo-s) { opacity: 1; } .sd-logo :global(.logo-dollar) { opacity: 0; } @keyframes show-dollar { 0% { opacity: 0; } 37.5% { opacity: 0; } 50% { opacity: 1; } 87.5% { opacity: 1; } 100% { opacity: 0; } } /* Animation activates for ANY SD logo when animate prop is true */ .brand-logo--animate.sd-logo :global(.logo-dollar) { animation: show-dollar 4s ease-in-out infinite; } .brand-logo--animate.sd-logo:hover :global(.logo-dollar) { animation-play-state: paused; } @media (prefers-reduced-motion: reduce) { .brand-logo--animate.sd-logo :global(.logo-dollar) { animation: none !important; opacity: 0; } } @media print { .sd-logo :global(.logo-dollar) { opacity: 0 !important; } } </style>

IMPORTANT: Animation applies to ALL SD logo variants, not just circle.

Ensure every SD SVG (sd-circle.svg, sd-leaf-dot.svg, sd-leaf-square.svg, sd-leaf-rect.svg) has:

  1. Three character path groups:
  • .logo-s - S character

  • .logo-dollar - $ character (hidden by default)

  • .logo-d - D character

  1. All use same base class .logo-primary (for color inheritance)

  2. Consistent structure across all variants:<svg viewBox="0 0 100 100"> <!-- Background varies by type: circle, leaf, square, rect --> <path class="logo-bg" d="..." fill="currentColor"/> <!-- Characters are IDENTICAL across all variants --> <path class="logo-primary logo-s" d="..." fill="currentColor"/> <path class="logo-primary logo-dollar" d="..." fill="currentColor"/> <path class="logo-primary logo-d" d="..." fill="currentColor"/> <!-- Optional secondary varies by type --> <path class="logo-secondary" d="..." fill="currentColor"/> </svg>

Design implication: When creating variants in Inkscape, the S, $, D character paths should be copied identically across all four background variations. Only the background shape changes between variants.

Update all files that import BrandLogo:

  1. Header: sites/Template/src/components/layout/Header.astro

<BrandLogo variant="smart-debt" logoType="circle" animate={true} class="w-12 h-12" />

  1. Logo Test Page: sites/Template/src/pages/logo-test.astro
  • Update to test all logo variants (circle, leaf-dot, leaf-square, leaf-rect)
  • Test animation explicitly for EACH variant (all should animate)
  1. Logo Showcase: sites/Template/src/components/showcase/LogoShowcase.astro
  • Show all SD variants: circle, leaf-dot, leaf-square, leaf-rect

  • Show TS circle variant

  • Demonstrate light/dark theme switching

  • Demonstrate animation for ALL SD variants


Update docs/design/logo.md:Major changes:

  • Document semantic class approach (.logo-bg, .logo-primary, .logo-secondary)

  • Update paths to src/brand/sd/logos/svg-paths-base/

  • Document all SD variants (circle, leaf-dot, leaf-square, leaf-rect)

  • Emphasize that ALL SD variants support animation

  • Update viewBox from 512×512 to 100×100

  • Document color application via currentColor

  • Add section on “Creating New Logo Variants” with Inkscape workflow

  • Document animation structure (S, $, D paths in ALL SD variants)

Add section:

## Animation Support ### Universal Animation for SmartDebt **ALL SmartDebt logo variants** support the S ↔ $D animation: - sd-circle.svg ✓ - sd-leaf-dot.svg ✓ - sd-leaf-square.svg ✓ - sd-leaf-rect.svg ✓ Each variant contains identical S, $, D character paths. The background shape changes, but the animation structure remains consistent. ### How It Works The animation uses CSS opacity keyframes: - S character: Always visible (opacity: 1) - $ character: Fades in/out (opacity: 0 → 1 → 0) - D character: Always visible (opacity: 1) The component activates animation via the `animate` prop: \`\`\`astro <BrandLogo variant="smart-debt" logoType="leaf-dot" animate={true} /> \`\`\` ## Color System Integration Logos use semantic classes with `currentColor`: | Class | Purpose | Color Source | |-------|---------|--------------| | `.logo-bg` | Background shape | `color` property on wrapper | | `.logo-primary` | Main characters/marks | `color` property on wrapper | | `.logo-secondary` | Accent elements | `--logo-secondary-color` CSS variable | ### Theme Adaptation Colors automatically adapt to theme via brand tokens: ```css [data-theme="light"] .sd-logo { color: hsl(var(--primary-8)); /* Dark green for light bg */ } [data-theme="dark"] .sd-logo { color: hsl(var(--primary-2)); /* Light tint for dark bg */ }

Same SVG, infinite themes.

### 7.2 Update Color Documentation Update [docs/design/colors.md](docs/design/colors.md): **Add sections**: - Document shared vs brand-specific tokens - Explain token merge strategy (shared + brand) - Update file paths - Document logo color tokens (`--sd-logo-primary`, etc.) - Add example of brand-scoped tokens ### 7.3 Update Favicon Documentation Update [docs/design/favicon.md](docs/design/favicon.md): **Changes**: - Document new generation approach (from brand source SVGs) - Update source file locations - Document brand configuration in `site.config.json` - Explain simplified favicon design (600 weight) ### 7.4 Create Brand Contract Documentation **Create** `docs/design/brand-contract.md`: ```markdown # Brand Contract ## Overview The brand contract defines the stable interface that sites use to consume brand assets. Each brand exposes a minimal, versioned API that sites import. ## Philosophy > **Sites consume brands via contract, never via direct file paths.** This ensures: - Clear ownership boundaries - Easy refactoring of brand internals - Ability to version brands independently - Future package extraction capability ## Contract Structure Each brand (`sd`, `ts`) exposes: \`\`\`typescript { id: string; // Short identifier name: string; // Full brand name displayName: string; // Display-friendly name tokens: () => Promise; // CSS tokens (lazy-loaded) logos: { // Logo variants (lazy-loaded) [key: string]: () => Promise<string> }; colors: { // Color anchors (reference) [key: string]: string }; logoVars: { // Logo color mappings light: { primary: string, secondary: string }, dark: { primary: string, secondary: string } }; } \`\`\` ## Usage \`\`\`typescript import { brand } from '@brand/sd'; // Load tokens await brand.tokens(); // Load logo const logoSvg = await brand.logos.circle(); \`\`\` ## Naming Conventions ### Folder Names - Lowercase, short codes - `sd` - SmartDebt - `ts` - TalbotStevens ### File Names - Brand-prefixed, kebab-case - `sd-circle.svg`, `sd-leaf-dot.svg` - `ts-circle.svg` ### CSS Tokens - Brand-namespaced - `--sd-logo-primary`, `--sd-logo-secondary` - `--ts-logo-primary`, `--ts-logo-secondary` ## Logo Variants ### SmartDebt (SD) All variants support S ↔ $D animation: - `circle` - Circular background - `leafDot` - Leaf shape with dot accent - `leafSquare` - Leaf shape with square background - `leafRect` - Leaf shape with rectangular background ### TalbotStevens (TS) - `circle` - Circular background ## Template's Role Template demonstrates brand consumption: \`\`\`astro import { brand } from '@brand/sd'; import BrandLogo from '@/components/brand/BrandLogo.astro'; <BrandLogo variant="smart-debt" logoType="circle" animate={true} /> \`\`\` Template: - ✅ Imports via brand contract - ✅ Demonstrates all variants - ✅ Validates contract completeness - ❌ Never owns brand assets - ❌ Never hardcodes brand paths

Update docs/design/README.md:Add section:

## Brand System The monorepo implements a formal brand system where: 1. **Brands are infrastructure** - Live at `src/brand/{sd,ts}/` 2. **Sites are consumers** - Import via `@brand/*` contracts 3. **Logos are semantic** - Use `currentColor` with semantic classes 4. **Colors are external** - Applied via brand-specific CSS variables 5. **Animation universal** - All SD logo variants support S ↔ $D animation See [Brand Contract](./brand-contract.md) for details. ### Brand Structure

src/brand/

sd/ # SmartDebt brand

logos/

svg-paths-base/ # Semantic SVG files (all with S,$,D paths)

favicons/

colors.oklch.json # Brand color definitions

tokens.css # Generated CSS tokens

index.ts # Brand contract

ts/ # TalbotStevens brand

(same structure)


Phase 8: Create Template Showcase Components

Section titled “Phase 8: Create Template Showcase Components”

Create sites/Template/src/components/showcase/BrandLogoShowcase.astro:

--- import BrandLogo from '@/components/brand/BrandLogo.astro'; import { brand as sdBrand } from '@brand/sd'; const sdLogoTypes = Object.keys(sdBrand.logos); --- <section class="logo-showcase"> <h2>SmartDebt Logo Variants</h2> <p class="text-muted">All variants support S ↔ $D animation</p> <div class="logo-grid"> {sdLogoTypes.map(logoType => ( <div class="logo-card"> <h3>{logoType}</h3> <div class="logo-demo"> <div class="demo-item"> <p>Static</p> <BrandLogo variant="smart-debt" logoType={logoType} class="w-32 h-32" /> </div> <!-- ALL SD logos can animate, not just circle --> <div class="demo-item"> <p>Animated (S ↔ $D)</p> <BrandLogo variant="smart-debt" logoType={logoType} animate={true} class="w-32 h-32" /> </div> </div> <div class="theme-demo"> <div data-theme="light" class="theme-box"> <p>Light Theme</p> <BrandLogo variant="smart-debt" logoType={logoType} class="w-24 h-24" /> </div> <div data-theme="dark" class="theme-box dark-bg"> <p>Dark Theme</p> <BrandLogo variant="smart-debt" logoType={logoType} class="w-24 h-24" /> </div> </div> </div> ))} </div> <h2>TalbotStevens Logo</h2> <BrandLogo variant="talbot-stevens" logoType="circle" class="w-32 h-32" /> </section>

Update sites/Template/docs/README.md:

Clarify Template’s role:

## Template's Role The Template site serves as: 1. **Reference Implementation** - Demonstrates correct brand consumption 2. **Testing Sandbox** - Validates all components and patterns 3. **Living Documentation** - Shows brand assets in use ### Key Principle > **Template demonstrates, never defines.** Template: - ✅ Imports brands via `@brand/*` contracts - ✅ Showcases all logo variants (all SD variants animate) - ✅ Tests theme switching - ✅ Validates animations - ❌ Never owns brand assets - ❌ Never hardcodes brand paths - ❌ Never defines brand tokens This ensures Template reflects the same consumption pattern that production sites use.


Run builds to ensure everything compiles:

cd sites/Template pnpm run build

Test in browser:

  1. All SD logo variants render correctly

  2. Logo animation works for ALL SD variants (circle, leaf-dot, leaf-square, leaf-rect)

  3. Animation pauses on hover for all variants

  4. Colors adapt to light/dark theme

  5. Both SD and TS logos display

  6. Favicons generate correctly

Verify:

  • No hardcoded brand colors in SVG files

  • All SVGs use semantic classes

  • All SVGs use currentColor

  • All SD SVGs contain S, $, D paths

  • BrandLogo component imports via brand contract

  • No direct file path imports to brand assets

  • TypeScript compilation succeeds

  • All documentation updated


  • Create src/brand/sd/ directory structure

  • Create src/brand/ts/ directory structure

  • Create src/design-tokens/ directory

  • Move shared tokens to new location

Phase 2: Logos (CRITICAL - All SD variants need S,$,D paths)

Section titled “Phase 2: Logos (CRITICAL - All SD variants need S,$,D paths)”
  • Create sd-circle.svg (with S, $, D paths for animation)

  • Create sd-leaf-dot.svg (with S, $, D paths for animation)

  • Create sd-leaf-square.svg (with S, $, D paths for animation)

  • Create sd-leaf-rect.svg (with S, $, D paths for animation)

  • Create ts-circle.svg (no animation needed)

  • Delete old logo files from src/assets/brand/logos/

  • Create src/design-tokens/colors.oklch.json (shared)

  • Create src/brand/sd/colors.oklch.json

  • Create src/brand/ts/colors.oklch.json

  • Update generate-color-system.mjs for brand flag

  • Generate SD tokens (pnpm run colors:sd)

  • Generate TS tokens (pnpm run colors:ts)

  • Create src/brand/sd/index.ts

  • Create src/brand/ts/index.ts

  • Update tsconfig.json with @brand/* alias

  • Update sites/Template/tsconfig.json

  • Create src/brand/sd/favicons/sd-favicon-source.svg

  • Create src/brand/ts/favicons/ts-favicon-source.svg

  • Update generate-favicon.mjs to use brand sources

  • Update site.config.json

Phase 6: Component (CRITICAL - Preserve animation for ALL SD variants)

Section titled “Phase 6: Component (CRITICAL - Preserve animation for ALL SD variants)”
  • Rewrite BrandLogo.astro to use SVG imports

  • Verify animation works for ALL SD variants (S ↔ $D)

  • Update Header.astro

  • Update logo-test.astro (test all SD variants animate)

  • Update LogoShowcase.astro (show all SD variants animate)

  • Update docs/design/logo.md (emphasize universal animation)

  • Update docs/design/colors.md

  • Update docs/design/favicon.md

  • Create docs/design/brand-contract.md

  • Update docs/design/README.md

  • Create BrandLogoShowcase.astro component (show all variants animate)
  • Update sites/Template/docs/README.md
  • Build succeeds (pnpm run build)

  • Visual testing: ALL SD variants animate

  • Code review checklist complete


| File | Purpose | Animation Support |

|------|---------|-------------------|

| src/brand/sd/logos/svg-paths-base/sd-circle.svg | SD circle logo | ✓ S,$,D paths |

| src/brand/sd/logos/svg-paths-base/sd-leaf-dot.svg | SD leaf-dot variant | ✓ S,$,D paths |

| src/brand/sd/logos/svg-paths-base/sd-leaf-square.svg | SD leaf-square variant | ✓ S,$,D paths |

| src/brand/sd/logos/svg-paths-base/sd-leaf-rect.svg | SD leaf-rect variant | ✓ S,$,D paths |

| src/brand/ts/logos/svg-paths-base/ts-circle.svg | TS circle logo | N/A |

| src/brand/sd/colors.oklch.json | SD color definitions | - |

| src/brand/ts/colors.oklch.json | TS color definitions | - |

| src/design-tokens/colors.oklch.json | Shared tokens (stone, gray) | - |

| src/brand/sd/index.ts | SD brand contract | - |

| src/brand/ts/index.ts | TS brand contract | - |

| docs/design/brand-contract.md | Brand contract documentation | - |

| sites/Template/src/components/showcase/BrandLogoShowcase.astro | Logo showcase (all variants animate) | - |

| File | Changes |

|------|---------|

| sites/Template/src/components/brand/BrandLogo.astro | Complete rewrite: import SVGs, preserve animation for ALL SD variants |

| sites/Template/scripts/generate-color-system.mjs | Add brand flag, merge shared + brand tokens |

| sites/Template/scripts/generate-favicon.mjs | Use brand source SVGs |

| tsconfig.json | Add @brand/* alias |

| sites/Template/tsconfig.json | Add @brand/* alias |

| docs/design/logo.md | Document semantic classes, currentColor, universal animation |

| docs/design/colors.md | Document shared + brand token structure |

| docs/design/favicon.md | Document brand-based generation |

| docs/design/README.md | Add brand system overview |

| sites/Template/docs/README.md | Clarify Template’s consumer role |---

Brands are infrastructure (monorepo root) ↓ Sites are consumers (via @brand/* imports) ↓ Logos are semantic (currentColor + classes) ↓ ALL SD variants support animation (S,$,D paths) ↓ Colors are external (CSS variables) ↓ Template demonstrates (never owns)

This establishes:

  • ✅ Single source of truth for brand assets

  • ✅ Zero duplication across sites

  • ✅ Theme-agnostic, reusable logos

  • ✅ Clear ownership boundaries

  • Animation for ALL SD variants (S ↔ $D)

  • ✅ Scalability to multiple SD sites