VRL-Design-System

Valley Real Life — Design System

Overview

Warm, atmospheric, and grounded. Valley Real Life’s interface evokes a quiet evening — deep, faintly green-tinted darks that feel organic rather than clinical, broken open by a single luminous green that glows wherever it lands. The aesthetic is photographic and welcoming, not flat or sterile; surfaces have layered depth from subtle gradients and soft brand-tinted glows.

The system is dark-first but ships a light theme via a single semantic-token override. Both modes share the same primitive scales — light mode flips which stops the semantic tokens point at. No pure black, no pure white. Even the darkest backgrounds carry a hint of green undertone (hue 135); even the brightest text sits a step below pure white.

The voice should feel like a trusted invitation. Headlines are large and slightly tracked-in; body copy is comfortable, never crowded. Eyebrow labels — uppercase, heavily tracked, brand-colored — are the system’s signature, reinforcing a hierarchical, sectioned reading experience inspired by church bulletins and editorial design.

Architecture is three-tier and cascading:

  1. Primitives — raw color scales (v-green-50…950, v-neutral-50…950), theme-stable.
  2. Semantic — contextual tokens (primary, surface, on-surface, etc.) reference primitives.
  3. Components — consume semantic tokens only. Components do not reference primitives directly.

To re-skin: change a primitive, every semantic token cascades, every component updates. To override locally: change a semantic token at any scope.

Colors

The palette is built from two OKLCH-derived scales. A single saturated green carries the entire brand identity; everything else is a warm gray-greige with a faint green undertone (OKLCH hue 135) that keeps the neutrals from feeling cold.

Primitives

v-green is the brand scale, anchored at v-green-500 (#9BC93C). The 300 stop is the brightest highlight; the 600 stop is the natural hover step-down; the 800–950 stops are reserved for pressed states and deep backgrounds.

v-neutral is a 22-stop neutral ramp. It is not pure gray — every stop carries chroma ≈ 0.008–0.016 at hue 135, giving the system a subtle warm-green undertone that ties the neutrals to the brand without competing with it.

Semantic Roles

Theme Behavior

Both themes pull from the same primitive scales. The light theme override only changes which stops the semantic tokens reference (e.g. surface swaps from v-neutral-800v-neutral-100). Brand stays identical in both modes for instant recognition.

For brand-colored text on light backgrounds (e.g. eyebrow labels in light theme), use v-green-700 instead of v-green-500 to maintain WCAG AA contrast. The system token brand-text handles this swap automatically.

Status Colors

success, info, warning, and error are tuned to read clearly on dark surfaces without competing with brand green for attention. They are currently one-offs; when a second use case appears, promote them into full v-amber, v-blue, v-red primitive scales using the same 50-950 pattern.

Typography

Montserrat carries the entire system — headings, body, labels. Weight and tracking do the work that a second typeface would normally do. Six weights ship: 300 (sparingly, very large display only), 400 (body), 500–600 (UI), 700–800 (headings).

The type scale is fully rem-based and modulated by a single --font-scale multiplier (default 1.0625, ≈ 17px root). Changing that one value lifts or shrinks every size proportionally while remaining compatible with the user’s browser font-size preference.

Roles

Tracking

Headings get -0.02em to -0.01em (tracking-tight). Body stays neutral. UI labels get +0.01em to +0.04em. Eyebrow labels get +0.25em (tracking-widest) and text-transform: uppercase.

Layout

The page is centered in a 1180px max-width container with 24px side gutters. Inside, a rem-based spacing scale anchors a consistent vertical rhythm.

The spacing scale runs 3xs (2px) through 4xl (96px) on a roughly Fibonacci-ish curve. Most UI uses md (16px) for internal padding and lg (24px) for the gap between related blocks. Use 2xl (48px) or 3xl (64px) between distinct page sections. Always prefer logical properties (padding-inline, margin-block) for future-proofing.

Aspect Ratios

Two ratios cover nearly all imagery:

Apply via aspect-ratio: var(--thumb-ratio) or aspect-ratio: var(--video-ratio). Never crop content inside these — always object-fit: cover the image and let the ratio drive the box.

Grid

No strict grid system. Cards and tiles use display: grid with grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)) to flex naturally without breakpoint juggling. Tile grids commonly use 4 columns wide, 3 columns, or 2 columns depending on density.

Elevation & Depth

Elevation comes from two layers: tonal layering (surfaces stack from bgbg-elevatedsurfacesurface-hoversurface-active, each ~3-5% lighter in dark mode, darker in light mode), and shadow + glow on top.

Shadow Scale

All shadows use oklch(0 0 0 / α) for true neutrality and reduce ~3× in opacity in light mode.

Brand Glow

The signature elevation effect. A soft radial glow in brand green, used on:

Two glow tokens: shadow-glow (default) and shadow-glow-lg (hover). Both auto-derive from the brand color via the relative color syntax — change the brand once and every glow updates.

Shapes

Generously rounded, with pill shapes as the signature for any interactive element.

Radius Scale

Squircle

A separate shape primitive — an iOS-style superellipse, “square but not square.” Implemented as a CSS mask-image (inline SVG) rather than a numeric border-radius, because superellipses can’t be expressed with border-radius alone. Use the .squircle utility class or mask-image: var(--squircle-mask). Note: mask-image clips outer box-shadow, so wrap the element if you need a drop shadow, or use filter: drop-shadow() on the parent.

Components

Component styling consumes semantic tokens only. The component layer never references primitives or hex values directly — change primary and every primary-based component updates.

Buttons

Pill-shaped (rounded-full), generously padded, with label-button typography. Five variants:

Sizes: sm, default, lg. Icon-only variant collapses padding to a square shape (aspect-ratio: 1).

Cards

All cards use rounded-lg (16px) by default. Three primary patterns:

Inputs

rounded-md (12px) for most fields. Border defaults to border-strong; hover lifts to border-strong-hover (a step up); focus removes the outline and replaces it with a 1px brand border plus a 4px tinted ring (oklch(from primary l c h / 0.2)).

Search Pill — A signature input shape. rounded-full container with a 4px padding gap and an embedded circular brand-filled button on the right. Used for filter/browse at the top of list views.

Selection Controls — Custom-styled checkboxes (square + animated checkmark), radio buttons (circle + filled dot), and toggle switches (pill with brand-filled knob when on). All built with the native input hidden and a styled adjacent element.

Tags / Chips

Tiny pill labels (caption typography). Default variant inverts per theme — dark pill on light surfaces, light pill on dark surfaces — for guaranteed contrast. Brand variant uses the full primary fill. Ghost variant has a transparent background and a thin border.

Compatible with Rock RMS markup (<ol class="breadcrumb"> with <li> items, .active or aria-current="page" on the current page). Three variants:

Page Headers

Two patterns for internal pages (the homepage gets its own treatment):

Theme Toggle

A pill-shaped track containing a brand-filled circular knob that translates horizontally between sun and moon icons. The knob carries the brand glow. State persists in localStorage; initial state respects prefers-color-scheme.

Do’s and Don’ts