Brand as Code: Engineering Visual Identity Systems
A brand is not a mood board. It is a set of constraints that produce consistent output across every surface where your product exists.
Most brand work ends as a PDF. A style guide that gets ignored the moment the first engineer opens their editor. We build brand systems that live in the codebase — enforced by tooling, not by memory.
What "Brand as Code" Actually Means
A brand system in code has four properties:
- Parseable -- machines can read and validate it
- Single source of truth -- one change propagates everywhere
- Version-controlled -- the history of brand decisions is in git
- Composable -- pieces combine predictably without designer intervention
This is the difference between a color palette in Figma and a color palette in tokens.ts that feeds Tailwind, generates CSS custom properties, and exports a React Native StyleSheet simultaneously.
The Token Architecture
We structure tokens in three tiers. Each tier is more specific than the last. Nothing from a lower tier leaks into a higher tier.
// Tier 1: Primitive values — raw design decisions, no semantic meaning
const primitive = {
color: {
orange: {
500: '#ED6D40',
400: '#F08C66',
600: '#D45A2F',
},
teal: {
500: '#5C93A0',
400: '#7AABB6',
600: '#4A7D89',
},
dark: {
950: '#0A0A0A',
900: '#111111',
800: '#1C1C1C',
700: '#252422',
},
light: {
100: '#FFFCF2',
200: '#F0F2F5',
300: '#DADADA',
400: '#C4C7CC',
},
},
font: {
mono: '"IBM Plex Mono", monospace',
sans: '"Inter", system-ui, sans-serif',
display: '"Inter", system-ui, sans-serif',
},
} as const;
// Tier 2: Semantic tokens — meaning without context
// References primitives, never raw values
const semantic = {
color: {
brand: primitive.color.orange[500],
brandSubtle: primitive.color.orange[400],
accent: primitive.color.teal[500],
surface: primitive.color.dark[800],
surfaceRaised: primitive.color.dark[700],
text: primitive.color.light[100],
textMuted: primitive.color.light[400],
border: 'rgba(255, 255, 255, 0.08)',
},
} as const;
// Tier 3: Component tokens — scoped decisions for specific components
// References semantic tokens, never primitives
const component = {
button: {
primary: {
bg: semantic.color.brand,
text: primitive.color.dark[900],
hover: primitive.color.orange[600],
},
ghost: {
bg: 'transparent',
text: semantic.color.textMuted,
border: semantic.color.border,
},
},
card: {
bg: semantic.color.surface,
border: semantic.color.border,
},
} as const;The discipline: when you change primitive.color.orange[500], every component token that references semantic.color.brand updates automatically. No find-and-replace across 40 files.
CSS Custom Properties as the Runtime Layer
Tokens defined in TypeScript generate CSS custom properties at build time. This enables theming, dark/light mode transitions, and runtime overrides without JavaScript.
// scripts/generate-css-vars.ts
// Runs at build time, outputs to globals.css
import { semantic } from './tokens';
function generateCSSVars(obj: Record<string, string>, prefix = ''): string {
return Object.entries(obj)
.map(([key, value]) => ` --${prefix}${key}: ${value};`)
.join('\n');
}
const vars = generateCSSVars(semantic.color, 'color-');
// Output: CSS file with --color-brand: #ED6D40; etc./* globals.css — generated, do not edit manually */
:root {
--color-brand: #ED6D40;
--color-accent: #5C93A0;
--color-surface: #1C1C1C;
--color-text: #FFFCF2;
--color-text-muted: #C4C7CC;
--color-border: rgba(255, 255, 255, 0.08);
}Every Tailwind class that uses these values references the CSS variable, not a hardcoded hex. This means a theme change is a single CSS file update.
Typography as a System
Typography is not a font choice. It is a set of relationships between scale, weight, tracking, and line height.
// typescale.ts — every text variant defined once
export const type = {
display: {
size: 'clamp(3rem, 9vw, 9rem)',
weight: 900,
tracking: '-0.04em',
lineHeight: '0.88',
transform: 'uppercase',
},
heading: {
size: 'clamp(1.5rem, 4vw, 3.5rem)',
weight: 800,
tracking: '-0.03em',
lineHeight: '1.1',
transform: 'uppercase',
},
body: {
size: '1rem',
weight: 400,
tracking: '0',
lineHeight: '1.65',
},
label: {
size: '0.6875rem',
weight: 500,
tracking: '0.14em',
lineHeight: '1',
transform: 'uppercase',
font: 'mono',
},
mono: {
size: '0.75rem',
weight: 400,
tracking: '0.06em',
lineHeight: '1.6',
font: 'mono',
},
} as const;A <Text variant="label"> component uses these values directly. No one writes text-[11px] tracking-[0.14em] uppercase font-mono in a component. They write <Text variant="label">.
Motion as Brand
Motion is the most underspecified part of most brand systems. "Smooth" and "snappy" are not engineering specs.
// motion.ts — every animation defined as data
export const motion = {
duration: {
instant: 100, // immediate feedback: button press, checkbox
fast: 200, // micro-interactions: hover states, tooltips
base: 350, // standard transitions: panel open, card expand
slow: 500, // emphasis: modal enter, page transition
crawl: 800, // dramatic: hero animations, number counters
},
easing: {
// Standard: most UI transitions
standard: 'cubic-bezier(0.4, 0, 0.2, 1)',
// Enter: elements arriving from off-screen
enter: 'cubic-bezier(0.0, 0, 0.2, 1)',
// Exit: elements leaving the screen
exit: 'cubic-bezier(0.4, 0, 1, 1)',
// Spring: playful, physical feel
spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
},
} as const;/* Tailwind extension from motion tokens */
.animate-enter {
animation: slideUp 350ms cubic-bezier(0.0, 0, 0.2, 1) forwards;
}
.animate-exit {
animation: fadeOut 200ms cubic-bezier(0.4, 0, 1, 1) forwards;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}Every animation in the product uses values from this table. No transition: all 0.3s ease anywhere.
Linting the Brand
The most dangerous phrase in a codebase is "just this once." We enforce token usage with ESLint.
// eslint-plugin-brand-tokens (custom rule)
// Flags hardcoded hex values in className props
module.exports = {
rules: {
'no-raw-colors': {
create(context) {
return {
JSXAttribute(node) {
if (node.name.name !== 'className') return;
const value = node.value?.value ?? '';
const hexPattern = /#[0-9a-fA-F]{3,8}/g;
const matches = value.match(hexPattern);
if (matches) {
context.report({
node,
message: `Raw color value "${matches[0]}" found. Use a token instead (e.g. bg-brand, text-neutral-200).`,
});
}
},
};
},
},
},
};CI fails on any hardcoded color. The brand is enforced at the pull request level, not in a design review.
What a Brand System Ships With
When we deliver a brand ecosystem to a client, it includes:
- Token files in TypeScript, JSON (for Figma tokens plugin), and CSS custom properties
- Tailwind config wired to the token layer
- Type system with a
<Text>component, heading scale, and mono utility - Motion system with named durations and easings exported as Tailwind extensions
- ESLint plugin to enforce token usage over raw values
- Storybook with a token viewer showing every color, spacing value, and animation
The brand lives in git. It ships with the product. It evolves through pull requests.
That is the difference between a brand guide and a brand system.