Appearance
Vibe Design System
A multi-tenant CSS design system and Vue component library built for enterprise B2B white-labeling.
Architecture
The design system uses CSS cascade layers for deterministic specificity ordering. One declaration in the root @vibe-labs/design package controls the entire cascade:
css
@layer vibe.reset, vibe.tokens, vibe.utilities, vibe.components, vibe.theme, vibe.accessibility;| Layer | Priority | Purpose |
|---|---|---|
vibe.reset | Lowest | Global resets (box-sizing, form normalization, tap highlight removal) |
vibe.tokens | ↓ | CSS custom properties on :root — the tunable knobs |
vibe.utilities | ↓ | Single-property utility classes (.flex, .p-4, .text-lg) |
vibe.components | ↓ | Multi-property compositions (.btn, .card, .input, .modal) |
vibe.theme | ↓ | Reserved for theme-level overrides |
vibe.accessibility | ↑ | Reduced motion, focus visibility, screen reader utilities |
| Unlayered CSS | Highest | Tenant overrides — wins automatically without !important |
White-Labeling
Tenant CSS is written outside any @layer block. Because unlayered CSS always beats layered CSS in the cascade, tenant overrides work with zero specificity gymnastics:
css
/* vibe-design/src/index.css — brand defaults (inside @layer vibe.tokens) */
@layer vibe.tokens {
:root {
--surface-base: #0f1115;
--color-accent: #7c5cff;
}
}
/* tenant.css — unlayered, wins automatically */
:root {
--surface-base: #1a1a2e;
--color-accent: #e94560;
}Dark / Light / System Modes
- Dark (default): Base
:rootvalues in@layer vibe.tokens - Light:
:root[data-mode="light"]overrides - System:
@media (prefers-color-scheme: light)for:root:not([data-mode])and:root[data-mode="system"]
Zero Cross-Package Dependencies
CSS packages communicate exclusively through token naming conventions (e.g. --color-{name}-500, --surface-{semantic}). No @import between utility packages, no bundler dependency graph required. Any package can be loaded or dropped independently.
Packages
Design Entry Point
| Package | Description |
|---|---|
@vibe-labs/design | Root package — declares layer order, imports all utility packages, sets dark/light/system mode tokens |
Utility Packages
All ship with @vibe-labs/design. Each follows the same structure: src/index.css (tokens) + scripts/build.mjs (generates utility classes) → dist/ (tokens + .g.css generated utilities).
| Package | Tokens | Utilities |
|---|---|---|
@vibe-labs/design-colors | Neutral 0-1000 scale with extended dark precision (25, 50, 75, 750, 825, 850, 875, 950 stops), status colors (success, warning, danger, info), swappable neutral palettes (charcoal, sand, slate, stone, zinc) | Color utilities for text, background, border |
@vibe-labs/design-typography | Font sizes (xs → hero-xl), weights (light → black), tracking, leading, font families (sans, mono, display) | .text-{size}, .font-{weight}, .tracking-{value}, .leading-{value}, alignment, decoration, truncation, line-clamp |
@vibe-labs/design-spacing | Space scale 0-24 (0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24) | .p-{n}, .px-{n}, .py-{n}, .pt/r/b/l-{n}, .m-{n} (same axes), .gap-{n}, .gap-x/y-{n} |
@vibe-labs/design-borders | Radius scale (sm → full), border widths (1-4), semantic + scale colors | .rounded-{size}, .border-{width}, .border-{color}, .divide-{axis} |
@vibe-labs/design-surfaces | Opacity, blur, overlays, semantic surfaces (background, base, elevated, subtle, overlay, modal) | .opacity-{n}, .backdrop-blur-{size}, .bg-{semantic}, .fg-{semantic}, .overlay-{type}, .bg-blend-{mode} |
@vibe-labs/design-elevation | Box shadows (xs → 2xl), inset shadows, focus ring system | .shadow-{size}, .shadow-inset-{size}, .ring, .ring-{color}, .ring-offset-{n} |
@vibe-labs/design-forms | Global reset in vibe.reset layer, input/button sizing, disabled states, placeholder styling | Input heights (sm/md/lg), padding, focus ring integration |
@vibe-labs/design-gradients | Semantic gradients (card, accent, shimmer, scroll fade, depth) | .gradient-{name}, .gradient-fade-{direction} |
@vibe-labs/design-display | Z-index scale (base → toast → max) | .block, .flex, .grid, .hidden, .invisible, .visible, .overflow-{value}, .z-{level}, position utilities, inset utilities |
@vibe-labs/design-flex | — | .flex-{direction}, .flex-wrap, .flex-{grow/shrink}, .items-{align}, .justify-{content}, .self-{align}, .order-{n} |
@vibe-labs/design-grids | — | .grid, .grid-cols-{1-12}, .col-span-{n}, .row-span-{n}, .col-start/end-{n} |
@vibe-labs/design-layouts | Container breakpoints (sm → xl), safe area insets | .container, .mx-auto, alignment primitives, --safe-top/bottom/left/right for notched devices |
@vibe-labs/design-sizing | — | .w-{value}, .h-{value}, .min-w/h-{value}, .max-w/h-{value}, .aspect-{ratio}, .object-{fit} (keyword + fractional sizing) |
@vibe-labs/design-images | — | .bg-{size}, .bg-{position}, .bg-{repeat}, .bg-{attachment} |
@vibe-labs/design-interactions | — | .cursor-{type}, .select-{value}, .pointer-events-{value}, .touch-{action}, .scroll-{behavior}, .snap-{type} |
@vibe-labs/design-transitions | Duration (instant → lazy), easing (linear, spring, bounce, elastic, smooth, decel, accel, sharp) | .duration-{value}, .ease-{curve}, .transition-{property}, .delay-{value}, .animate-{name}, .motion-reduce, .motion-safe |
@vibe-labs/design-responsive | — | Container queries, viewport-based visibility, responsive layout helpers |
Component Packages (CSS + TypeScript Types)
Each component package generates framework-agnostic CSS (in @layer vibe.components) and exports TypeScript type definitions. The CSS uses data-attribute selectors for variants (e.g. [data-size="sm"], [data-variant="accent"]), configurable between attr and flat modes.
| Package | Components |
|---|---|
@vibe-labs/design-components-avatars | Avatar, avatar groups |
@vibe-labs/design-components-badges | Badge variants (neutral, accent, success, warning, danger, info) |
@vibe-labs/design-components-buttons | Button variants, sizes, icon buttons, button groups |
@vibe-labs/design-components-cards | Card, card header/body/footer |
@vibe-labs/design-components-core | Shared base styles |
@vibe-labs/design-components-dropdowns | Dropdown menu, items, sections, keyboard navigation styles |
@vibe-labs/design-components-graphs | Graph container, tooltip, legend, axis, annotations |
@vibe-labs/design-components-images | Image container, aspect ratio, loading states |
@vibe-labs/design-components-inputs | Text input, textarea, select, field wrapper, labels, messages, validation states |
@vibe-labs/design-components-menus | Vertical/horizontal menus, menu items |
@vibe-labs/design-components-modals | Modal, overlay, header/body/footer, animation states |
@vibe-labs/design-components-pagination | Pagination bar, page items, ellipsis |
@vibe-labs/design-components-progress | Progress bar, progress ring |
@vibe-labs/design-components-spinners | Spinner variants with shimmer animation |
@vibe-labs/design-components-tabs | Tab list, tab triggers, tab panels |
@vibe-labs/design-components-toasts | Toast container, toast item, status variants |
@vibe-labs/design-components-toggles | Toggle switch, toggle field |
@vibe-labs/design-components-uploads | Upload zone, upload item, upload indicator, progress overlay |
Vue Component Library
Full Vue 3 bindings with TypeScript generics, composables, and accessibility support.
| Package | Exports |
|---|---|
@vibe-labs/design-vue-avatars | VibeAvatar — auto-generates initials and background color from name |
@vibe-labs/design-vue-buttons | VibeButton — variants, sizes, loading states, icon support |
@vibe-labs/design-vue-cards | VibeCard, VibeCardHeader, VibeCardBody, VibeCardFooter |
@vibe-labs/design-vue-dropdowns | VibeDropdown, VibeDropdownItem, VibeDropdownSection — keyboard navigation, click-outside |
@vibe-labs/design-vue-forms | VibeForm, VibeInput, VibeTextarea, VibeSelect, VibeField + useVibeForm composable |
@vibe-labs/design-vue-images | VibeImage — loading states, error fallback, lazy loading |
@vibe-labs/design-vue-modals | VibeModal, VibeModalHeader, VibeModalBody, VibeModalFooter + useModal composable |
@vibe-labs/design-vue-pagination | VibePagination + usePagination composable |
@vibe-labs/design-vue-progress | VibeProgressBar, VibeProgressRing + useProgress, useProgressTimer composables |
@vibe-labs/design-vue-spinners | VibeSpinner — size variants |
@vibe-labs/design-vue-tabs | VibeTabs, VibeTabList, VibeTabTrigger, VibeTabPanel |
@vibe-labs/design-vue-toasts | VibeToast, VibeToastContainer + useToast composable |
@vibe-labs/design-vue-toggles | VibeToggle — switch with label positioning |
@vibe-labs/design-vue-uploads | VibeUploadZone, VibeUploadItem, VibeUploadList, VibeUploadIndicator + useUploadQueue composable |
Key Composables
| Composable | What it does |
|---|---|
useVibeForm | Reactive form state, validation (submit/blur/change), field registration via provide/inject, dirty tracking, typed field() helper for v-bind |
useUploadQueue | Concurrent upload management with configurable parallelism, AbortController cancellation, validation, retry, preview URL lifecycle |
useFocusTrap | Keyboard focus trapping within a container, Tab/Shift+Tab wrapping, restores previously focused element |
useScrollLock | Body scroll lock with reference counting for nested modals, scrollbar width compensation to prevent layout shift |
useTooltip | Hover position tracking and datum management for graph components |
useModal | Simple reactive open/close/toggle state for modal control |
usePagination | Computes visible page range with ellipsis, boundary pages, and windowed navigation |
useProgress | Shared percentage/clamping/auto-color resolution for progress components |
useProgressTimer | ETA estimation from progress samples with smoothed rate calculation |
Graph Package
| Package | Description |
|---|---|
@vibe-labs/design-vue-graphs | d3-scale/d3-shape integration — VibeSparkline, VibeBarChart, VibeLineChart with tooltip, legend, and annotation support |
Build System
Each package follows a consistent build pattern:
vibe-design-{name}/
├── package.json
├── src/
│ └── index.css ← Tokens (CSS custom properties in @layer vibe.tokens)
├── scripts/
│ └── build.mjs ← Generates utility classes
└── dist/
├── index.css ← Copied from src
└── {name}.g.css ← Generated utilities (in @layer vibe.utilities)Build command: rimraf dist && ncp src dist && node ./scripts/build.mjs
Generated files use the .g.css suffix to distinguish hand-written CSS from generated output.
Variant System
Component CSS uses data-attribute selectors by default, with a configurable SelectorMode:
html
<!-- attr mode (default) — self-documenting HTML -->
<button class="btn" data-variant="accent" data-size="lg">Click me</button>
<!-- flat mode — traditional classes -->
<button class="btn btn-accent btn-lg">Click me</button>The build system generates both modes from the same source definitions.
Token Ownership
Each token family has a single owning package to prevent conflicts:
| Token Family | Owner | Examples |
|---|---|---|
--color-* | colors | --color-neutral-900, --color-success-500 |
--text-* | typography | --text-primary, --text-muted, --text-lg |
--font-* | typography | --font-sans, --font-bold |
--space-* | spacing | --space-0 through --space-24 |
--surface-* | surfaces | --surface-base, --surface-elevated |
--border-*, --radius-* | borders | --border-subtle, --radius-md |
--shadow-* | elevation | --shadow-xs through --shadow-2xl |
--duration-*, --ease-* | transitions | --duration-fast, --ease-spring |
--z-* | display | --z-modal, --z-toast |
--input-*, --btn-* | forms | --input-height-md, --btn-radius |
--gradient-* | gradients | --gradient-card, --gradient-fade-b |
--safe-* | layouts | --safe-top |
--overlay-*, --blur-* | surfaces | --overlay-scrim, --blur-lg |
Color System
Neutral Scale (Extended Dark Precision)
The neutral scale runs 0-1000 with additional stops in the dark range for subtle surface distinctions:
0 (white) → 25 → 50 → 75 → 100 → 200 → 300 → 400 → 450 → 500 →
550 → 600 → 650 → 700 → 750 → 800 → 825 → 850 → 875 → 900 → 950 → 1000 (black)The 750-950 range maps directly to the surface hierarchy in dark mode:
| Surface | Token |
|---|---|
--surface-background | --color-neutral-950 |
--surface-base | --color-neutral-900 |
--surface-subtle | --color-neutral-875 |
--surface-elevated | --color-neutral-850 |
--surface-overlay | --color-neutral-800 |
--surface-modal | --color-neutral-750 |
Swappable Neutral Palettes
Alternative neutral scales available: charcoal, sand, slate, stone, zinc. Import the desired palette to shift the entire system's neutral tones.
Status Colors
Four status families, each with a full 50-900 scale: success, warning, danger, info.
Browser Requirements
This design system targets modern browsers:
| Browser | Minimum Version |
|---|---|
| Chrome | 99+ |
| Edge | 99+ |
| Safari | 15.4+ |
| Firefox | 131+ |
Key features requiring modern browser support: CSS @layer, revert-layer, container queries, dynamic viewport units (dvh/dvw), :has() selector.
Design Principles
- Tokens are defaults, not mandates — every token has a sensible default; the design package overrides per mode; tenants override as needed
- No cross-package dependencies — packages communicate only through CSS variable naming conventions
- Utilities are single-property — multi-property combinations belong in the component layer
- Generated output is small — only utilities that matter, not every possible combination
- Mobile-first — dynamic viewport units, safe area insets, touch-action, tap-highlight removal built in from the start
- Framework-agnostic CSS — Vue bindings are a separate layer; the token and component CSS works with any framework