Skip to content

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/font per 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-brand first, then ripples to consumers via a version bump.

Tech Stack

TechnologyVersionPurpose
TypeScript^5.8.xToken + helper authoring
CSSTokens, theme, fonts, base, components, animations split across discrete files
React19.xShared UI primitives (react/index.ts and react/*.tsx)
Tailwind CSS4.2.xTokens flow into @theme {} blocks consumed by Tailwind v4's CSS-first config

Package Exports

SubpathWhat it provides
@vela/brand/tokens.cssColor, spacing, radii, shadow, typography, motion tokens as CSS custom properties
@vela/brand/theme.cssLight + dark theme application — sets the right tokens against :root and .dark
@vela/brand/fonts.cssBundled @fontsource/... imports for Fustat, Manrope, Geist Mono
@vela/brand/base.cssBase resets, link styles, focus rings — wrapped in @layer base
@vela/brand/components.cssPre-styled primitives (buttons, badges, pills)
@vela/brand/animations.cssReusable @keyframes and motion utility classes
@vela/brand/tokensTokens as TypeScript constants (for non-CSS consumers — OG generator, JS-driven canvases)
@vela/brand/ogOpen Graph image generator (consumed by vela-web, vela-dashboard for share cards)
@vela/brand/reactReact 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.mdSource-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.md

Consumers

ConsumerWhat it imports
vela-dashboardTokens, theme, fonts, base, components, react primitives
vela-portalTokens, theme, fonts, base
vela-checkoutTokens, theme, fonts, base
vela-adminTokens, theme, fonts, base, components
vela-widgetTokens, theme (the iframe checkout surface)
vela-webTokens, theme, fonts, OG generator
vela-docsTokens, theme, fonts (Starlight theme overrides)
vela-demoTokens, 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-brand workspace 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.
  • Source-of-truth brand spec: vela-brand/brand.md
  • Consumer matrix above maps to the Repo Contracts → ownership rules.

Internal knowledge base for the Vela Labs workspace.