Appearance
vela-brand
The canonical design system for VelaPay. Single source of truth for color, typography, motion, OG image generation, and shared React UI primitives. Every VelaPay UI surface imports from here — no surface hardcodes brand values.
Published as the local workspace package @vela/brand (v1.0.2 at the time of writing). WCAG 2.2 AA-aligned with first-class dark-mode support; light + dark are unified rather than divergent themes.
Purpose and Role
- Centralize all design tokens (color, type, spacing, radii, shadows, motion easing) so every UI surface renders identically.
- Provide pre-bundled font CSS for the Fustat / Manrope / Geist Mono stack — surfaces import a single CSS file rather than wiring
next/fontper repo. - Ship reusable React primitives (buttons, inputs, dialogs, OG image generator) so marketing and dashboard surfaces don't reinvent each other.
- Define the global motion easing constant (
cubic-bezier(0.32, 0.72, 0, 1)at 180–220 ms) used for every interactive transition in the workspace. - Be the only place where brand decisions live. Any change to color, font, or motion happens in
vela-brandfirst, then ripples to consumers via a version bump.
Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| TypeScript | ^5.8.x | Token + helper authoring |
| CSS | — | Tokens, theme, fonts, base, components, animations split across discrete files |
| React | 19.x | Shared UI primitives (react/index.ts and react/*.tsx) |
| Tailwind CSS | 4.2.x | Tokens flow into @theme {} blocks consumed by Tailwind v4's CSS-first config |
Package Exports
| Subpath | What it provides |
|---|---|
@vela/brand/tokens.css | Color, spacing, radii, shadow, typography, motion tokens as CSS custom properties |
@vela/brand/theme.css | Light + dark theme application — sets the right tokens against :root and .dark |
@vela/brand/fonts.css | Bundled @fontsource/... imports for Fustat, Manrope, Geist Mono |
@vela/brand/base.css | Base resets, link styles, focus rings — wrapped in @layer base |
@vela/brand/components.css | Pre-styled primitives (buttons, badges, pills) |
@vela/brand/animations.css | Reusable @keyframes and motion utility classes |
@vela/brand/tokens | Tokens as TypeScript constants (for non-CSS consumers — OG generator, JS-driven canvases) |
@vela/brand/og | Open Graph image generator (consumed by vela-web, vela-dashboard for share cards) |
@vela/brand/react | React primitives barrel export |
@vela/brand/react/* | Per-component imports for tree-shaking |
@vela/brand/assets/* | Brand assets (logos, favicons, OG fallback images) |
@vela/brand/brand.md | Source-of-truth brand spec (voice, tone, layout principles) |
Directory Structure
vela-brand/
├── tokens.css # Design tokens (CSS custom properties)
├── theme.css # Light + dark token application
├── fonts.css # Fontsource bundles (Fustat, Manrope, Geist Mono)
├── base.css # @layer base resets and primitives
├── components.css # Component-level pre-styles
├── animations.css # @keyframes + motion utilities
├── tokens.ts # Tokens as TS constants
├── og.ts # Open Graph image generator
├── react/ # React primitives
│ ├── index.ts
│ └── *.tsx # Individual components
├── assets/ # Logos, favicons, brand images
├── brand.md # Brand spec (voice, tone, motion, type rules)
└── README.mdConsumers
| Consumer | What it imports |
|---|---|
vela-dashboard | Tokens, theme, fonts, base, components, react primitives |
vela-portal | Tokens, theme, fonts, base |
vela-checkout | Tokens, theme, fonts, base |
vela-admin | Tokens, theme, fonts, base, components |
vela-widget | Tokens, theme (the iframe checkout surface) |
vela-web | Tokens, theme, fonts, OG generator |
vela-docs | Tokens, theme, fonts (Starlight theme overrides) |
vela-demo | Tokens, theme, fonts, base, react primitives |
Notable Design Decisions
@layer base Wrapping
base.css rules must be wrapped in @layer base so consumer Tailwind utility classes can override them. An earlier release shipped without the wrap and broke pill button text styling globally — a recurring trap when CSS files are imported in the wrong order. The brand package now enforces the wrap and consumers don't need to think about layer ordering.
Unified Light + Dark
Tokens have a single semantic name (--color-surface-base, --color-text-primary) with light and dark values resolved via theme.css based on :root vs .dark. Consumers never branch on theme — they reference the token, and the right value resolves automatically.
Motion as a Constant
The 180–220 ms / cubic-bezier(0.32, 0.72, 0, 1) easing is treated as a brand asset on par with color. Surfaces that deviate feel "off" — the consistent micro-rhythm is part of what makes VelaPay feel polished.
Source of Truth for Marketing AND Dashboard
A previous era split brand patterns between marketing (vela-web) and product (vela-dashboard); they drifted. v1.0 of @vela/brand consolidated both — vela-web's voice and visual language is canonical. Any divergence in the dashboard is a bug.
Lifecycle
- Versioned: SemVer-tagged. Breaking visual changes bump major.
- Linked locally: Consumed via
file:../vela-brandworkspace links — no npm publish step in the development loop. - Coordination required: Any visual change is reviewed across consumers. Tokens that ship without consumer updates create drift.
Related
- Source-of-truth brand spec:
vela-brand/brand.md - Consumer matrix above maps to the Repo Contracts → ownership rules.