Build consistent.
Move with purpose.
Stay on brand.
A living reference for brand colors, GSAP motion presets, and component
customizations. shadcn/ui (Maia style, Gray theme) is the design
system — this repo documents everything we add on top.
Two logo variants: the way*ID pill badge and the Lineage*Labs text wordmark.
Both have distinct light and dark mode treatments and animated GSAP reveals.
way*ID Badge
Plain text wordmark — no border, no background. Navy text on light, near-white on dark. The * uses brand offset blue.
Light Mode default
way*IDway*IDway*ID
Dark Mode inverted
way*IDway*IDway*ID
Animated Reveal back.out(1.7)center stagger
Light
way*ID
Dark
way*ID
Lineage*Labs Wordmark
Text wordmark in Lora. Weight 400 on both modes. Light: --brand-highlight-navy text + asterisk. Dark: --brand-surface text, --brand-offset-green asterisk.
Light Mode Lora 400--brand-highlight-navy
Lineage*LabsLineage*LabsLineage*Labs
Dark Mode Lora 400--brand-surface
Lineage*LabsLineage*LabsLineage*Labs
Animated Reveal left staggerasterisk pop
Light
Lineage*Labs
Dark
Lineage*Labs
Usage Rules
Clear Space
Maintain at least 1x the badge height as clear space on all sides of the logo.
Minimum Size
Badge text must never be smaller than 12px. Below that, use the badge-only variant.
No Modifications
Never rotate, stretch, recolor, add shadows, or apply effects to the logo. Use it exactly as specified.
Mode Matching
Always use the light logo on light backgrounds and the dark logo on dark backgrounds. Never mix.
Phase 1: chars stagger 0.04s from left, blur 6→0 + x -16→0 + y 8→0, power2.out, 400ms
Phase 2: asterisk scale pop 1→1.28→1, sine.out + power2.inOut, 700ms (keyframes)
Section 03
Brand Colors
These are additive — they never replace shadcn semantic tokens.
Apply via --brand-* only when a design explicitly calls for brand treatment.
Highlights
Navy
#0E1233
--brand-highlight-navy
Light
#FAFAFA
--brand-highlight-light
Surfaces
Surface Light
#E8E5DE
--brand-surface-light
Surface Dark
#0E1233
--brand-surface-dark
Offset Accents — Light surface
Lavender
#7267E2
--brand-offset-lavender-light
Green
#A0D246
--brand-offset-green-light
Yellow
#F8BC4D
--brand-offset-yellow
Coral
#FF7236
--brand-offset-coral-light
Blue
#006CDB
--brand-offset-blue-light
Highlights + Offsets — Dark / Blue surface
Light
#FAFAFA
--brand-highlight-light
Lavender
#B4AFE7
--brand-offset-lavender-dark
Green
#D5FD8D
--brand-offset-green-dark
Yellow
#F8BC4D
--brand-offset-yellow
Coral
#FF814C
--brand-offset-coral-dark
Blue
#2886E6
--brand-offset-blue-dark
When to use
Hero sections, marketing headings, CTAs, status badges, illustrations, gradient accents — any element the design explicitly marks as brand colored.
When NOT to use
Buttons, inputs, cards, body text, borders, error states, focus rings — use shadcn semantic tokens for all standard UI.
Sidebar Tokens
Eight --sidebar-* tokens power the shadcn Sidebar component. Do not use them as general color tokens outside of sidebar contexts.
Token
Purpose
--sidebar
Sidebar panel background
--sidebar-foreground
Sidebar text
--sidebar-primary
Active nav item background
--sidebar-primary-foreground
Active nav item text
--sidebar-accent
Hover state background
--sidebar-accent-foreground
Hover state text
--sidebar-border
Sidebar border / divider
--sidebar-ring
Focus ring inside sidebar
Tailwind v4 @theme block
tokens/colors.css includes a commented-out @theme inline block (Section 3). Copy it into your project's root CSS when using Tailwind v4 — it maps all CSS variables to Tailwind utilities (bg-primary, text-muted-foreground, etc.).
Sidebar vs. standard tokens
For page layouts that include a sidebar, use standard --background, --border, etc. tokens. Only use --sidebar-* tokens when styling the shadcn Sidebar component itself.
Section 04
Spacing
4px grid — every value is a multiple of 4px (0.25rem).
Maia style means generous spacing: when in doubt, size up.
Base Scale
Token
rem
px
Tailwind
Visual
0.5
0.125
2
0.5
1
0.25
4
1
2
0.5
8
2
3
0.75
12
3
4
1
16
4
5
1.25
20
5
6
1.5
24
6
8
2
32
8
10
2.5
40
10
12
3
48
12
16
4
64
16
20
5
80
20
24
6
96
24
Semantic Tokens
Inline
inline-xs · 4px · Icon-to-label gapinline-sm · 8px · Button icon gapinline-md · 12px · Nav link gap
/* ✅ Safe — collapses to 100% on narrow screens */
grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
/* ❌ Unsafe — overflows at 320px when min > 272px */
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
Vertical Content & Fixed Heights
Fixed-height containers (height: 100vh + overflow: hidden) are the most common cause of vertical content clipping on mobile.
Content that fits on desktop often doesn't fit on a phone.
#
Rule
Implementation
1
min-height not height
Use min-height: 100dvh — content can grow beyond viewport
2
Slides scroll on mobile
Slide/carousel containers: overflow-y: auto below md:
3
No overflow: hidden on content
Only for decorative wrappers (border-radius, image crops)
4
dvh not vh
100vh includes browser chrome on mobile. Use 100dvh.
5
Reduce density or scroll
If 3 cards don't fit, scroll within slide or show fewer per slide
Use a shadcn Sheet component (entrance: slideInLeft, exit: slideOutLeft) triggered by a hamburger button. Sheet width: min(280px, calc(100vw - 3rem)). Do not stack the sidebar below content — it pushes critical content below the fold.
Section 06
Motion Tokens
All animations use GSAP v3. Every animation must check prefers-reduced-motion.
Durations
Token
Value
Visual
Use
instant
100ms
Active/pressed
fast
150ms
Hover, toggles
normal
300ms
Modals, fades
slow
500ms
Page sections
slower
700ms
Staggered
slowest
1000ms
Hero/cinematic
Note on 400ms
The slideIn* entrance presets use 400ms and slideOut* exit presets use 300ms. These are intentional intermediate values hardcoded in animations/presets.js — they do not map to a named duration token. When referencing these presets' timing, write 400ms explicitly.
Easings
Name
GSAP
Use
out (default)
power2.out
Entrances, general UI
in
power2.in
Exits
in_out
power2.inOut
Layout shifts
overshoot
back.out(1.7)
Modals, bounces
spring
elastic.out(1, 0.3)
Springy — GSAP only
bounce
bounce.out
Bounce — GSAP only
Reduced Motion
Mandatory accessibility requirement
Every animation must respect prefers-reduced-motion: reduce. The design system handles this automatically in most cases — read the rules below.
GSAP — use applyPreset()
The recommended path. applyPreset(gsap, target, "scaleIn") handles reduced motion automatically. For raw gsap.fromTo() calls, check manually: window.matchMedia("(prefers-reduced-motion: reduce)").matches and set duration: 0.
Scroll triggers — self-managed
All factory functions in scroll-triggers.js check reduced motion internally. revealUp/Left/Right and staggerReveal call gsap.set() (instant). parallax() and pinSection() return null. No extra guard needed.
CSS — already handled
All classes in animations/transitions.css include a @media (prefers-reduced-motion: reduce) block that sets all durations to 0ms and removes all transforms. No extra work needed.
Rule
Detail
Never use raw gsap.to/from without a duration guard
Use applyPreset() or check window.matchMedia() directly
Never hand-roll scrubbing without a reduced-motion guard
Use the parallax() factory — it returns null automatically
Opacity-only fades ≤ 150ms are permissible
Even under reduced motion, pure fades under 150ms are acceptable
Translate and scale transforms → duration: 0
All movement must be instant when reduced motion is active
Section 07
Animation Presets
Click any card to replay. Presets live in animations/presets.js.
Entrance
fadeIn
opacity 0→1 · 300ms
Click to replay
slideInUp
y:24→0 · 400ms
Click to replay
slideInDown
y:-24→0 · 400ms
Click to replay
slideInLeft
x:-24→0 · 400ms
Click to replay
slideInRight
x:24→0 · 400ms
Click to replay
scaleIn
scale:0.95→1 · 300ms
Click to replay
expandIn
scaleY:0→1 · 300ms
Click to replay
Exit
fadeOut
opacity 1→0 · 200ms
Click to play
slideOutUp
y:0→-24 · 300ms
Click to play
slideOutDown
y:0→24 · 300ms
Click to play
scaleOut
scale:1→0.95 · 200ms
Click to play
Hover Interactions
Glow on hover (brand grass green)
CSS Transitions vs. GSAP
Use CSS Transitions for
Simple hover/focus/active states. Effects limited to transform, opacity, box-shadow. Anything reversible by removing a CSS class.
Use GSAP for
Component mount/unmount (Dialog, Sheet, Toast). Multi-step sequences or timelines. Scroll-triggered animations. Anything needing JS callbacks or ScrollTrigger.
CSS Class
Effect
Duration
.transition-instant
Sets transition-duration
100ms
.transition-fast
Sets transition-duration
150ms
.transition-normal
Sets transition-duration
300ms
.transition-slow
Sets transition-duration
500ms
.hover-lift
translateY(-4px) + shadow on :hover
200ms
.hover-press
scale(0.97) on :active
100ms
.hover-fade
opacity: 0.8 on :hover
150ms
.focus-ring
box-shadow ring via var(--ring) on :focus-visible
150ms
Scroll-Triggered Presets
Preset
Trigger
Effect
Scrub
revealUp
top 85%
y: 40 → 0, fade in
No
revealLeft
top 85%
x: −40 → 0, fade in
No
revealRight
top 85%
x: 40 → 0, fade in
No
parallax
top bottom → bottom top
yPercent: −15
Yes
staggerReveal
top 85%
Stagger 0.08s, y: 30, fade in
No
pinSection(gsap, trigger, pinTarget, options)
Pins pinTarget in place while the user scrolls through trigger. Returns null under reduced motion — no pin applied. Use for sticky image / sticky sidebar layouts. Import from animations/scroll-triggers.js.
Logo Reveal
way*IDLineage HQ
Icon: back.out(1.7), wordmark: power2.out.
Section 08
Live Components
Interactive Maia-styled components with their assigned GSAP animations.
Maia = soft, rounded, generous spacing. Pill-shaped buttons & inputs,
ring-1 cards, and smooth motion.
Button rounded-4xl (pill)press: scale(0.97)
Input rounded-4xl (pill)bg-input/30
Badge rounded-4xl (pill)
DefaultOutlineFeaturedActivePro
Icons HugeiconscurrentColor20px default
Icons use currentColor so they adapt to light/dark mode automatically. Never hardcode a stroke or fill color — let the icon inherit from its parent.
Inline / Toolbar
Hover: --muted bg · Focus: --ring outline · Tab to test
Quick Action Tile
Add
Schedule
Focus
Reports
Hover: 18% muted · Focus: --ring outline · Tab to test
Navigation
Hover: subtle bg · Focus: --ring outline · Tab to test
Dark Mode
Because icons use currentColor, they flip automatically when --foreground changes from dark to light.
Never set explicit colors like stroke="#333" or fill="black" — these break in dark mode.
Icon container backgrounds use color-mix() with semantic tokens, so they adapt too.
Stroke Weight by Context
Context
stroke-width
Notes
Inline / toolbar (standard)
1.5
Default fallback for uncategorized contexts
Inline / toolbar (emphasized)
1.75
When extra visual weight is needed
Quick-action tile
2
Larger tap target, needs more presence
FAB (floating action button)
2.5
Prominent primary action
Navigation bar
1.5
Consistent with inline standard
Do not mix stroke weights within a single context. The 1.5 default is the fallback for contexts not listed above.
Uses ring-1 instead of border for the Maia aesthetic.
Static Card
Non-interactive cards don't need hover effects.
Card with Image rounded-2xlaspect-ratio: 16/9overflow: hidden
Project Alpha
Image area uses aspect-ratio: 16/9 with muted background.
Active
Media Upload
Interactive cards use hover-lift and shadow escalation.
Static Variant
Custom ratio (4:3). No hover effect on non-interactive cards.
Chart --chart-1..5rounded-2xlRecharts
Weekly Activity
Tasks completed per day
Mon
Tue
Wed
Thu
Fri
Sat
Sun
Weekday
Weekend
Color Reference
All five chart tokens
chart-1
chart-2
chart-3
chart-4
chart-5
chart-1
chart-2
chart-3
chart-4
chart-5
Chart Color Token Reference
Token
Light
Dark
Data Series Role
--chart-1
Warm orange
Blue
Primary / first series
--chart-2
Teal
Green
Secondary series
--chart-3
Navy
Amber
Tertiary series
--chart-4
Yellow
Purple
Quaternary series
--chart-5
Amber
Red
Quinary series
Assignment rule
Assign tokens by series order — --chart-1 for the first data series, --chart-2 for the second, and so on. Never skip a token or assign by color preference. Exact OKLCH values are in tokens/colors.css.
Field label + input + descriptionerror state
We'll never share your email.
Password must be at least 8 characters.
Choose your primary role.
Max 280 characters.
Dialog scaleIn / scaleOutback.out(1.7)
Sheet (right) slideInRight / slideOutRight
Dropdown Menu slideInDown / fadeOut
Tooltip fadeIn / fadeOut (150ms)
This is a tooltip
Another tooltip here
Toast slideInRight / slideOutUp
Accordion expandIn (scaleY)rounded-2xl
Maia is one of shadcn/ui's built-in styles — "soft and rounded, with generous spacing." It uses pill-shaped buttons and inputs (rounded-4xl), rounded cards (rounded-2xl), and ring borders instead of traditional borders.
GSAP offers physics-based easings (spring, bounce), fine-grained timeline control, ScrollTrigger integration, and a consistent API across all browsers. It also makes it easy to respect prefers-reduced-motion by setting duration to 0.
No. Never edit files inside components/ui/ directly so the shadcn CLI remains updatable. Customise via CSS variable overrides, Tailwind utility composition, or thin wrapper components.
Tabs fadeIn (150ms)
The Maia style brings a consumer-friendly, warm, approachable aesthetic to shadcn/ui. Key features include pill-shaped buttons (rounded-4xl), generous spacing (gap-6), and blue-tinted gray neutrals (OKLCH hue ~262).
Design tokens are defined in tokens/colors.css (Gray theme OKLCH values), tokens/motion.yaml (duration & easing), and tokens/brand-colors.yaml (additive brand palette). The --radius is set to 0.875rem (large).
Install shadcn/ui via their CLI, choose Maia style and Gray base color. Copy tokens/colors.css into your global stylesheet. Import GSAP presets from animations/presets.js. Use Lora for h0/h1, Poppins for h2–h4, and the system font stack for everything else.
Animation Assignment Reference
Component
Entrance
Exit
Hover
Notes
Dialog / AlertDialog
scaleIn
scaleOut
—
Scale + overshoot
Sheet (left)
slideInLeft
slideOutLeft
—
Mobile nav drawer
Sheet (right)
slideInRight
slideOutRight
—
Matches open side
DropdownMenu
slideInDown
fadeOut
—
Drops down, fades out
Popover
scaleIn
fadeOut
—
Scale from trigger
Tooltip
fadeIn150ms
fadeOut150ms
—
Fast & subtle
Toast
slideInRight
slideOutUp
—
Slides in, dismisses up
Accordion
expandIn
reverse expandIn
—
Height from top
Tabs
fadeIn150ms
—
—
Quick crossfade
NavigationMenu
fadeIn
—
—
Dropdown submenu
Card (interactive)
—
—
hover-liftCSS
Shadow escalation
Button
—
—
hover-pressCSS
scale(0.97) on :active
Are you sure?
This action cannot be undone. This will permanently delete your account and remove your data from our servers.
Sheet Panel
This is a sheet that slides in from the right using the slideInRight GSAP preset.
Section 09
Images
Patterns, CSS utilities, aspect ratios, focal points, loading attributes, overlay recipes,
and shadcn AspectRatio / Card / Avatar mappings.
All demos use hero-market.jpg.
Section Image — Side-by-side
img-text-gridgrid 1:1badge + headline + body + CTAsstacks on mobile
Seasonal
Grown within 50 miles.
Every item is sourced from farms within a 50-mile radius. We verify provenance at intake and publish full supply-chain transparency reports monthly.
/* Image left, text right */
<divclass="img-text-grid">
<divclass="img-ar" style="--ar:4/3; border-radius:var(--radius);">
<imgsrc="photo.jpg" alt="…" loading="lazy" />
</div>
<divclass="img-text-grid-body">
<h3>Headline</h3>
<p>Body copy.</p>
</div>
</div>
/* Text left, image right — swap DOM order */
<divclass="img-text-grid">
<divclass="img-text-grid-body">…</div> <!-- text first = left col -->
<divclass="img-ar">…</div> <!-- img second = right col -->
</div>
Section Image — Figure + Caption
<figure>img-figureimg-captioncredit line
Riverside Farmers Market — Weekly outdoor market featuring 40+ local vendors. Open every Saturday, 8am–2pm.
Photo: Jane Doe / Maia Studio
Marketing heroes, event banners, editorial covers with atmospheric photography
Wrap shrinks to 40dvh; content stays bottom-anchored
Split panel
.img-hero-split
Sign-up/auth heroes, landing page primary CTAs, feature intros requiring a form or bullet list
Stacks: image top (35dvh) + panel below at ≤600px
Image + text grid
.img-text-grid + .img-text-grid-body
Feature rows, "about" sections, editorial articles — when copy needs equal visual weight to image
Single column; image first by DOM order
Figure + caption
<figure> + .img-figure + .img-caption
Articles, press releases, documentation — anywhere attribution or context is needed below the image
Full width; caption wraps naturally
Aspect Ratios by Image Type
Type
Ratio
CSS
Notes
Hero / Full-bleed
Variable height
.img-hero · height: 60dvh
Height in dvh units, not a fixed ratio
Section / editorial
21 : 9
.img-ar · --ar: 21/9
Wide editorial break between sections
Card cover (default)
16 : 9
.maia-card-image-area
Override inline for 4:3 or 1:1
Blog thumbnail
4 : 3
.img-ar · --ar: 4/3
Compact, good for sidebars
Square / product
1 : 1
.img-ar · --ar: 1/1
Product shots, avatar grids
Portrait / profile
3 : 4
.img-ar · --ar: 3/4
Team cards, author bios
Avatar
1:1 circular
.img-avatar.img-avatar-{size}
50% border-radius on container
object-position Focal Point System
Value
Effect
When to use
center center
Default · geometric centre
Symmetric subjects, products
28% center
Left-of-centre
hero-market.jpg default — subject occupies left third
60% center
Right-of-centre
Subject right, space left for text overlay
center top
Upper portion visible
Faces, sky-dominant landscapes
center bottom
Lower portion visible
Ground-level subjects, food dishes
50% 20%
Custom fine-tune
When keyword values are insufficient
Border-radius by Image Type
Type
Container radius
<img> radius
Rule
Hero / full-bleed
0
0
Bleeds edge-to-edge, no radius
Section / editorial
var(--radius)
0
Radius on .img-ar container only
Card cover
1rem via .maia-card-image
0
overflow:hidden on card clips the image
Avatar
50% via .img-avatar
0
Container is circular; image fills it
Cardinal rule: never put border-radius on <img>. Apply it to the container with overflow: hidden.
This prevents pixel gaps in scaled states and avoids double-radius artefacts.
A realistic mobile app — "Focus" — built entirely from design system components.
Buttons, inputs, badges, cards, tabs, accordion, bottom sheet, toasts, FAB, and bottom nav
all working together in a phone-frame layout.
Fully interactive — click, scroll, check tasks, fire toasts.
Components used
• App bar + status bar mock• Maia pill search input• Stat cards (maia-card)• Featured project card + progress bar• Quick actions grid (4-up)• Task list with checkboxes• Tabs (Today / Upcoming / Completed)• Accordion (Project Notes)• Bottom sheet (add task)• FAB• Bottom navigation bar• Toast notifications• Badges (lavender / blue / navy / default)
Animation patterns
• Staggered entrance sequence on load• Progress bar animated fill (0→62%)• Button micro-lift + back.out(2) spring• FAB scale bounce on hover• Task check — back.out(2.2) pop• Bottom sheet — slide up from 100%• Toast — back.out(1.5) slide-up entry• Accordion chevron nudge on hover• Tab panel y:8→0 fade-slide• Scroll reveal for lower sections
A Series A pitch slide for "Aria" — a fictional AI payments startup. Dark navy background,
Lora headline for emotional impact, System UI for all data and labels. 16:9 desktop format.
• Dark navy bg — authority + brand depth• Lora for the headline only — max emotional pull• System UI for all data — stays crisp at small sizes• Right column abstract visual avoids literal imagery• Stats use weight + size hierarchy, not color
Fonts in use on this screen
Lora · Serif · 700The AI layerh1 · slide headline only
System UI · Sans · 400–700$2.4M ARRstats · labels · subtitle · wordmark
2 families · Poppins not loaded
SvelteKit
Open standalone
View at full 800×450 resolution or inspect source.
Above-the-fold hero for "Aria" — light mode. Full nav + eyebrow badge, Lora
headline, CTA pair, and social proof strip. Desktop 800×500.
SvelteKit + shadcn-svelte — static export
Page components
• Flat nav bar (logo + links + CTA)• Full-bleed photo — extends behind entire hero area• Solid white card (45%) overlaid right — no fade• Eyebrow badge "Now in private beta"• Lora 700 headline (2rem)• Subtitle paragraph (System UI)• CTA pair: filled primary + ghost• Social proof strip (4 company names)
Design decisions
• Hero photo anchors human context; copy stays minimal• Hard card edge — white panel sits over photo, border-left divider• Lora upright — authority without decoration• Ghost CTA lowers barrier vs. single primary• Social proof muted — present but not competing
Fonts in use on this screen
Lora · Serif · 700Payments that feel human.h1 · page headline only
System UI · Sans · 400–700Everything elsenav · buttons · badge · subtitle · proof
2 families · Poppins not loaded
SvelteKit
Open standalone
View at full 800×500 resolution or inspect source.
A mobile review screen for "Trustflow" — showing the Aria product page with a rating summary,
star breakdown bars, filter chips, and review cards. System UI only — no web font needed.
SvelteKit + shadcn-svelte — fully interactive
Screen components
• App bar (back + title + kebab)• Rating summary: 4.7 ★, 2,341 reviews• 5-row star breakdown (CSS-only width bars)• Filter chips: All / 5★ / 4★ / Verified / Recent• Review cards (name, stars, date, text, helpful)• Verified badge (green pill)• Write review input bar (pinned bottom)
Design decisions
• 1 font family — no headings warrant Lora/Poppins• Star bars via CSS width — zero JS needed• Active chip uses --foreground fill, not brand blue• Verified badge mirrors eyebrow pill (offset-green, subtle)• Helpful CTA low-prominence — secondary action
Fonts in use on this screen
System UI · Sans · 400–7004.7 ★★★★★entire screen — app bar · ratings · reviews
1 family · Poppins not loaded · Lora not loaded
SvelteKit
Open standalone
View at 390×780 or inspect source in a separate tab.