diff --git a/index.html b/index.html index a09ecbf..35a9031 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,7 @@ + diff --git a/styleguide.html b/styleguide.html index 827faba..817e0b2 100644 --- a/styleguide.html +++ b/styleguide.html @@ -3,44 +3,47 @@ -Style Guide — Make Post Sell +Style Guide — Make Post Sell Design System + - - +
Style Guide — Make Post Sell Design System @@ -109,7 +211,6 @@ Style Guide — Make Post Sell Design System
Home - | Style Guide
@@ -118,170 +219,159 @@ Style Guide — Make Post Sell Design System
- Style Guide + Design System

- Live reference of every component on makepostsell.com. Use this to keep the design consistent as we grow. +

+ Comprehensive design tokens, components, and patterns for makepostsell.com. Built on CSS custom properties. Mobile-first. Accessible. Respects reduced motion. +

+ + + + + +
+
Design Tokens
+ +

All values live in tokens.css as CSS custom properties. Every component references tokens, never raw values. Change a token, change everything.

+ +
/* Include order */ +<link rel="stylesheet" href="tokens.css"> /* tokens first */ +<link rel="stylesheet" href="common.css"> /* shared app */ +<link rel="stylesheet" href="mps.css"> /* ribbon, alerts */ +<link rel="stylesheet" href="landing.css"> /* landing page only */
+ +
tokens.css also provides base resets (box-sizing, font smoothing, scroll-behavior) and prefers-reduced-motion support.
+
-


-
+
Colors
-
Primary & CTA
+
Brand
-
-
-
-
Green CTA
-
#a3c765
-
-
-
-
-
-
Blue Secondary
-
#98b6fa
-
-
-
-
-
-
Navy Brand
-
#5871ad
-
-
-
-
-
-
Light Navy
-
#82A8FF
-
-
-
-
-
-
Purple
-
#9B59B6
-
-
-
-
-
-
Purple Dark
-
#7B2D8E
-
-
+
Green CTA
#a3c765
+
Green Dark
#8ab34e
+
Blue
#98b6fa
+
Navy
#5871ad
+
Navy Light
#82A8FF
+
Purple
#9B59B6
+
Purple Dark
#7B2D8E
-
Status & Feedback
+
Status
-
-
-
-
Check Green
-
#678731
-
-
-
-
-
-
Cross Red
-
#c77765
-
-
-
-
-
-
Danger Red
-
#CC6958
-
-
+
Check
#678731
+
Cross
#c77765
+
Danger
#CC6958
+
Gold
#d4a843
-
Text & Background
+
Surfaces
-
-
-
-
Text Dark
-
#0b0b0b
-
-
-
-
-
-
Text Body
-
#515151
-
-
-
-
-
-
Text Desc
-
#666666
-
-
-
-
-
-
Text Muted
-
#777777
-
-
-
-
-
-
Text Faint
-
#999999
-
-
+
Base
#FFFFFF
+
Dim
#F9F9FA
+
Container
#F4F4F5
+
Container High
#EEEEEF
+
Inverse
#0b0b0b
-
+
+ +
+
Text
-
-
-
-
BG Light
-
#F9F9FA
-
-
-
-
-
-
White
-
#FFFFFF
-
-
-
-
-
-
Border
-
#e0e0e0
-
-
-
-
-
-
Border Light
-
#eeeeee
-
-
+
Primary
#0b0b0b
+
Body
#515151
+
Secondary
#666666
+
Muted
#777777
+
Faint
#999999
+
Disabled
#BBBBBB
+
+
+ +
+
Borders
+
+
Default
#e0e0e0
+
Light
#eeeeee
+
Focus
#5871ad
+
Error
#CC6958
+ -
+
Typography
-
App Typography (common.css)
+
Type scale (major third 1.250)
+
+
Display
+
Headline 1
+
Headline 2
+
Headline 3
+
Title Large
+
Title
+
Title Small
+
Body Large — ideal for landing pages and long reads.
+
Body — default for app interfaces and dashboard text.
+
Body Small — secondary information, metadata, timestamps.
+
Label Large
+
LABEL — SECTION HEADERS, GROUP TITLES
+
Caption — smallest text, footnotes
+
OVERLINE — CATEGORY TAGS
+
+
.type-display clamp(2.25rem, 5vw, 3.75rem) 700 tight +.type-headline-1 3rem (48px) 700 tight +.type-headline-2 2.25rem (36px) 700 tight +.type-headline-3 1.875rem (30px) 700 snug +.type-title-lg 1.5rem (24px) 600 snug +.type-title 1.25rem (20px) 600 snug +.type-title-sm 1rem (16px) 600 normal +.type-body-lg 1.125rem (18px) 400 relaxed +.type-body 1rem (16px) 400 relaxed +.type-body-sm 0.875rem (14px) 400 relaxed +.type-label-lg 0.875rem (14px) 600 wide +.type-label 0.75rem (12px) 600 UPPERCASE wider +.type-caption 0.75rem (12px) 400 normal +.type-overline 11px 700 UPPERCASE widest +.type-code monospace 0.9em inline code
+
+ +
+
App typography classes (common.css)
Huge Bold Dark

@@ -291,128 +381,360 @@ Style Guide — Make Post Sell Design System

Body text in #515151 on Helvetica.

- Link style — green #a3c765, weight 600 + Link style — green, weight 600
-
.huge-bold-dark — 2.7em, weight 550, color #0b0b0b -.big-bold-dark — 1.4em, weight 550, color #0b0b0b -.bold-dark — weight 550, color #0b0b0b -body text — default size, color #515151 -a (links) — color #a3c765, weight 600
+
Legacy classes. New code should prefer .type-* classes from tokens.css.
-
Landing Typography (landing.css)
-
-

Hero Title

-

Hero subtitle line

-

Section Title

-

Landing body text with inline link and proper line-height.

-

Muted caption text, centered.

-
-
.landing-hero-title — 2.4em mobile / 4.5em desktop, weight 700 -.landing-hero-subtitle — 1.25em, weight 500 -.landing-section-title — 2em, weight 700, centered -.landing-body — 1.05em, line-height 1.7, color #515151 -.landing-muted — 14px, color #999, centered
+
Font rendering
+
html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + font-size: 16px; /* 1rem = 16px base */ +}
+
Antialiased rendering on macOS/iOS. optimizeLegibility enables kerning and ligatures. 16px base ensures rem values map cleanly to pixel equivalents.
+ + +
+
Elevation
+ +

Five shadow levels. Higher = more prominent = closer to the viewer. Every interactive element should shift elevation on hover.

+ +
+
+
0
+
1
+
2
+
3
+
4
+
5
+
+
+
--elevation-0: none +--elevation-1: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04) /* cards at rest */ +--elevation-2: 0 2px 6px rgba(0,0,0,0.06), 0 2px 4px rgba(0,0,0,0.04) /* cards hovered */ +--elevation-3: 0 4px 12px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.04) /* dropdown, popover */ +--elevation-4: 0 8px 24px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04) /* modal, dialog */ +--elevation-5: 0 16px 48px rgba(0,0,0,0.10), 0 4px 12px rgba(0,0,0,0.05) /* toast, overlay */ + +Usage: element rests at N, hovers at N+1 or N+2 +Cards: rest 1, hover 3 +Buttons: rest 1, hover 2 +Modals: 4 +Toasts: 5
+
+ + + +
+
Motion
+ +
+
Easing curves
+
+
+
Standard
+
Decel
+
Accel
+
Spring
+
+
+
--ease-standard: cubic-bezier(0.2, 0, 0, 1) /* default for most transitions */ +--ease-decelerate: cubic-bezier(0, 0, 0, 1) /* entering elements (fade in, slide in) */ +--ease-accelerate: cubic-bezier(0.3, 0, 1, 1) /* exiting elements (fade out) */ +--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275) /* playful overshoot */ + +Hover each box above to feel the curve difference.
+
+ +
+
Durations
+
--duration-instant: 50ms /* micro-feedback (opacity toggle) */ +--duration-fast: 100ms /* state layer opacity changes */ +--duration-normal: 200ms /* color, shadow, border transitions */ +--duration-slow: 300ms /* transform transitions (scale, translate) */ +--duration-slower: 400ms /* complex animations (ripple) */ +--duration-entrance: 250ms /* element enters viewport */ +--duration-exit: 200ms /* element leaves (shorter = snappier) */
+
+ +
+
Entrance animations
+
+
+
fade-in
+
fade-up
+
scale
+
slide-R
+
+
+
.animate-fade-in /* opacity 0 to 1 */ +.animate-fade-in-up /* fade + translate up 16px */ +.animate-fade-in-down /* fade + translate down 16px */ +.animate-fade-in-scale /* fade + scale from 0.95 */ +.animate-slide-in-right /* fade + translate from right 24px */ +.animate-slide-in-left /* fade + translate from left 24px */ +.animate-pulse /* breathing opacity loop */ +.animate-spin /* continuous rotation */ + +/* Stagger delays for lists */ +.stagger-1 through .stagger-6 (50ms increments)
+
+ +
+
Reduced motion
+
@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +Automatically applied. No opt-in needed. All animations and +transitions collapse to instant for users who prefer reduced motion.
+
+
+ + + +
+
Spacing
+ +

4px base unit. Consistent spacing prevents the "off by 3px" disease.

+ +
+
+
 
+
2
+
3
+
4
+
5
+
6
+
8
+
10
+
12
+
16
+
20
+
24
+
+
+
--space-1: 4px --space-8: 32px +--space-2: 8px --space-10: 40px +--space-3: 12px --space-12: 48px +--space-4: 16px --space-16: 64px +--space-5: 20px --space-20: 80px +--space-6: 24px --space-24: 96px + +Utilities: .mt-{n} .mb-{n} .p-{n} .gap-{n}
+
+ + + +
+
Shape
+ +
+
+
none
+
sm 4px
+
md 8px
+
lg 12px
+
xl 16px
+
2xl 24px
+
full
+
+
+
--radius-none: 0 /* hard edges */ +--radius-sm: 4px /* inputs, code blocks, app buttons */ +--radius-md: 8px /* small cards, wells */ +--radius-lg: 12px /* feature cards, landing cards */ +--radius-xl: 16px /* pricing cards, comparison cards */ +--radius-2xl: 24px /* large containers */ +--radius-full: 9999px /* pills, avatars, badges */ + +Rule of thumb: bigger element = bigger radius.
+
+ + + +
+
Interactive States
+ +

State layer: a semi-transparent overlay that responds to interaction. Apply .state-layer to any interactive element.

+ +
+
+
Hover me
+ +
+
+
.state-layer::after { + content: ''; + position: absolute; + inset: 0; + background: currentColor; + opacity: 0; + transition: opacity 100ms ease; + pointer-events: none; +} + +.state-layer:hover::after { opacity: 0.04; } +.state-layer:focus-visible::after { opacity: 0.08; } +.state-layer:active::after { opacity: 0.10; } + +State overlay tokens: +--state-hover: rgba(0,0,0, 0.04) +--state-focus: rgba(88,113,173, 0.12) +--state-pressed: rgba(0,0,0, 0.08) +--state-dragged: rgba(0,0,0, 0.12)
+
+ + + +
+
Focus Rings
+ +

Keyboard-only focus rings. Visible on Tab, invisible on click. Navy blue, 2px, 2px offset.

+ +
+
+ + Tab to focus this link +
+
+
:focus-visible { + outline: 2px solid var(--border-focus); /* navy #5871ad */ + outline-offset: 2px; +} + +:focus:not(:focus-visible) { + outline: none; /* hide on mouse click */ +} + +This is set globally in tokens.css. Works on all elements.
+
+ + + +
+
Ripple Effect
+ +

Material-style touch feedback. Add .ripple class and initialize with JS.

+ +
+
+
Click me
+
Click me
+
+
+
/* CSS */ +.ripple { position: relative; overflow: hidden; } +.ripple-wave { + position: absolute; + border-radius: 50%; + background: rgba(255,255,255, 0.35); + transform: scale(0); + animation: ripple-expand 400ms ease-out forwards; +} +.ripple-dark .ripple-wave { background: rgba(0,0,0, 0.12); } + +/* JS initialization */ +function createRipple(e) { + const el = e.currentTarget; + const rect = el.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const wave = document.createElement('span'); + wave.className = 'ripple-wave'; + wave.style.width = wave.style.height = size + 'px'; + wave.style.left = (e.clientX - rect.left - size/2) + 'px'; + wave.style.top = (e.clientY - rect.top - size/2) + 'px'; + el.appendChild(wave); + wave.addEventListener('animationend', () => wave.remove()); +}
+
+ + -
+
Buttons
-
App Buttons (common.css)
+
App buttons (common.css)
-
Green CTA: .shop-new-button.mps-button -Blue: .product-edit-button.mps-button -Purple: .source-button.mps-button -Navy: .log-out-button.mps-button -Danger: .cart-delete-button.mps-button -Outline: .shop-switch-button.mps-button +
Green CTA: .shop-new-button.mps-button #a3c765 +Blue: .product-edit-button.mps-button #98b6fa +Purple: .source-button.mps-button #9B59B6 +Navy: .log-out-button.mps-button #5871ad +Danger: .cart-delete-button.mps-button #CC6958 +Outline: .shop-switch-button.mps-button border only -border-radius: 4px, min-width: 250px
+All: border-radius 4px, min-width 250px, white text, bold
-
Landing Page CTAs (landing.css)
+
Disabled state
- -
Add .landing-cta for pill shape + hover lift - -border-radius: 50px, padding: 16px 32px -box-shadow: 0 2px 8px rgba(0,0,0,0.08) -hover: translateY(-1px), deeper shadow - -.landing-cta-large — 18px font, 48px horizontal padding
+
[disabled], .disabled { + opacity: 0.38; + pointer-events: none; + cursor: not-allowed; +}
- -
-
Landing Sections
-
-
Alternating sections (no HR dividers)
-
-
-

White section (.landing-section)

-
-
-

Alt section (.landing-section-alt)

-
-
-

White section

-
+ +
+
Landing CTAs
+ +
+ -
<section class="landing-section">...</section> -<section class="landing-section landing-section-alt">...</section> - -.landing-section — padding: 64px mobile, 80px tablet, 100px desktop -.landing-section-alt — background: #F9F9FA -.landing-final-cta — gradient: #f8faf5 to #f0f6ff
-
-
- - -
-
Pricing Cards
- -
-
-
- 📅 - $14/month - Pay as you go. Cancel anytime. -
- +
+
-
.landing-pricing-card — white bg, 1px border, 16px radius -.landing-pricing-featured — 2px green border, green shadow -.landing-pricing-badge — absolute pill, green bg, white text -.landing-pricing-amount — 3em, weight 700 -.landing-pricing-period — 0.35em, color #999
+
.landing-cta: + border-radius: 50px (pill) + padding: 16px 32px + box-shadow: elevation-1 + hover: translateY(-1px) + elevation-2 + +.landing-cta-large: + font-size: 18px + padding: 18px 48px + +Nav CTA (.nav-cta): + dark pill in nav bar, #0b0b0b bg
- -
+ + +
Feature Cards
@@ -420,56 +742,82 @@ hover: translateY(-1px), deeper shadow
💰 Card Title - Card description with details about the feature goes here. + Card description with supporting text.
📈 - Linked Card Title - Cards with links in the title use green hover. + Linked Card + Cards in .landing-content get 12px radius, subtle shadow, hover lift.
-
<div class="mps-card-grid"> - <div class="mps-card"> - <span class="mps-card-icon">...</span> - <span class="mps-card-title">Title</span> - <span class="mps-card-desc">Description</span> - </div> -</div> +
Grid: .mps-card-grid (1col mobile, 2col tablet+) + .mps-card-grid.three-col (3col at tablet+) -Landing: 12px radius, subtle shadow, hover lift -Grid: 1 col mobile, 2 col tablet+, .three-col for 3
+Card: .mps-card + App: border 1px #e0e0e0, radius 10px, padding 20px + Landing: border 1px #eee, radius 12px, shadow elevation-1 + Hover: elevation-3 + translateY(-2px)
- -
+ + +
+
Pricing Cards
+ +
+
+
+
Monthly
+ $14/mo + Pay as you go. +
+ +
+
+
.landing-pricing-card white, radius-xl, overflow hidden +.landing-pricing-featured 2px green border, green shadow +.landing-pricing-banner gray top strip, uppercase +.landing-pricing-banner-featured green bg, white text +.landing-pricing-amount 3em, weight 700 +.landing-pricing-period 0.35em, color faint
+
+ + + +
Step Cards
1 - First Step - Description of the first step. + First + Description.
2 - Second Step - Description of the second step. + Second + Description.
3 - Third Step - Description of the third step. + Third + Description.
-
.landing-step-card — centered text -.landing-step-number — 44px green circle, white number, weight 700
+
.landing-step-number: 44px green circle, white number, weight 700 +.landing-step-card: centered text alignment
- -
+ + +
Protection Lists
@@ -482,175 +830,253 @@ Grid: 1 col mobile, 2 col tablet+, .three-col for 3
    -
  • Positive item — Details
  • -
  • Another positive — More details
  • +
  • Positive — Details
  • +
  • Another — More
-
.landing-list-bad — bg #fef2f2, border #f5d5d5 -.landing-list-good — bg #f0f9e8, border #d5e8c0 -.landing-x — color #c77765 -.landing-check — color #678731 -.landing-list-detail — color #666 - -Grid: stacked mobile, 2 col at 800px+
+
.landing-list-bad: bg #fef2f2, border #f5d5d5 +.landing-list-good: bg #f0f9e8, border #d5e8c0 +.landing-x: color --color-cross +.landing-check: color --color-check +Items separated by 1px border-top at 5% opacity
- -
-
Comparison Cards
+ + +
+
Alerts
-
-
- $14.00 - Marketplace (30% commission) -
-
- $19.12 - Make Post Sell (0% commission) -
+
+
Success — --alert-success-bg
+
+
+
Info — --alert-info-bg
+
+
+
Warning — --alert-warning-bg
+
+
+
Danger — --alert-danger-bg
-
.mps-comparison-card.marketplace — bg #f8d7da, border #c77765 -.mps-comparison-card.mps — bg #e8f5d4, border #678731 -.mps-comparison-amount — 2.4em, weight 600 - -Landing: 16px radius, 32px padding
+
Variants: .alert-success, .alert-info, .alert-warning, .alert-danger +Token pairs: --alert-{variant}-bg, --alert-{variant}-text, --alert-{variant}-border
- -
-
Ribbons
+ + +
+
Loading States
-
Message Ribbon (top banner, desktop only)
+
Skeleton
-
- Sell digital downloads or physical products — commission free! +
+
+
+
+
+
+
+
+
+
+
+
-
<div class="message-ribbon"> - Your message here -</div> +
.skeleton shimmer gradient animation (1.5s loop) +.skeleton-text height: 1em, small radius +.skeleton-circle border-radius: full -Gradient: #5871ad → #82A8FF -Hidden on mobile, visible at 800px+
+The shimmer runs through surface-container to surface-container-high.
+
+
Spinner
+
+
+ + + +
+
+
.spinner 20px, 2px border, navy top +.spinner-sm 14px +.spinner-lg 32px, 3px border + +Spins at 0.6s linear infinite.
+
+ + +
+
Scroll Reveal
+ +

Elements with .reveal start invisible and slide up when scrolled into view. JS adds .revealed.

+ +
+
+
reveal
+
scale
+
+
+
.reveal { + opacity: 0; + transform: translateY(20px); + transition: opacity 300ms ease-out, transform 300ms ease-out; +} +.reveal.revealed { + opacity: 1; + transform: translateY(0); +} + +/* JS — IntersectionObserver */ +const observer = new IntersectionObserver((entries) => { + entries.forEach(e => { + if (e.isIntersecting) { + e.target.classList.add('revealed'); + observer.unobserve(e.target); + } + }); +}, { threshold: 0.15 }); + +document.querySelectorAll('.reveal, .reveal-scale') + .forEach(el => observer.observe(el));
+
+ + -
+
Wells
Well (gray background)
- Content inside a gray well with rounded corners. + Content inside a gray well.
-
<section class="one-column well">...</section> - -Background: #F9F9FA, border-radius: 10px, padding: 10px
Well White
-
+
- Content inside a white well with rounded corners. + Content inside a white well.
-
<section class="one-column well well-white">...</section> - -Background: #FFFFFF
+
.well bg: surface-dim, radius: 10px +.well-white bg: surface-base
- -
-
Alerts
+ + +
+
Message Ribbon
-
-
Success alert — green background #a3c765
-
-
-
Info alert — blue background #98b6fa
-
-
-
Warning alert — yellow background #fff3cd
-
-
-
Danger alert — red background #f8d7da
+
+ Sell digital downloads or physical products — commission free!
-
Variants: alert-success, alert-info, alert-warning, alert-danger
+
.message-ribbon + gradient: navy #5871ad to navy-light #82A8FF + white text, bold, 18px, centered + hidden on mobile, visible at 800px+
+ + +
+
Landing Sections
+ +
+
+
.landing-section (white)
+
+
+
.landing-section-alt (#F9F9FA)
+
+
+
.landing-final-cta (gradient)
+
+
+
.landing-hero (top gradient)
+
+
+
.landing-section padding: 64px mobile, 80px tablet, 100px desktop +.landing-section-alt background: surface-dim +.landing-final-cta gradient: #f4f6fb to #f0f3fa +.landing-hero gradient: #f4f6fb to white (top-down) + +Alternating white/dim sections replace HR dividers.
+
+ + -
+
Layout
-
One Column (max-width 800px, centered)
-
<section class="one-column">...</section> -<section class="one-column well">...</section> -<section class="one-column-thin">...</section> (max-width 400px)
-
+
Containers
+
Legacy: + .one-column max-width: 800px, centered + .one-column-thin max-width: 400px -
-
Navigation Grid
-
<section class="nav-grid"> - <section class="nav"> - <section class="logo">...</section> - <section class="middle">...</section> - <section class="log-in">...</section> - </section> -</section> - -Mobile: stacked (1 column) -Desktop (800px+): 3 columns (logo | middle | log-in)
+Token-based: + .container max-width: var(--content-width) 800px, auto padding + .container-narrow max-width: var(--content-narrow) 400px + .container-wide max-width: var(--content-wide) 1200px
Breakpoints
Mobile first (no media query) @media (min-width: 800px) — tablet/desktop -@media (min-width: 1200px) — wide desktop
+@media (min-width: 1200px) — wide desktop + +Tokens: --bp-tablet: 800px, --bp-desktop: 1200px
-
CSS Files
-
common.css — shared app styles (layout, buttons, nav, wells, footer, cards) -mps.css — message ribbon, alerts -landing.css — landing page (hero, sections, pricing, CTAs, lists, steps)
+
Z-index scale
+
--z-base: 0 /* default */ +--z-dropdown: 100 /* dropdown menus */ +--z-sticky: 200 /* sticky headers */ +--z-overlay: 300 /* backdrop overlays */ +--z-modal: 400 /* modal dialogs */ +--z-toast: 500 /* toast notifications */ +--z-ribbon: 600 /* top ribbon banner */
+
+ +
+
CSS files
+
tokens.css — design tokens, base resets, utilities, animations +common.css — shared app styles (layout, buttons, nav, wells, footer, cards) +mps.css — message ribbon, alerts +landing.css — landing page (hero, sections, pricing, CTAs, lists, steps)
+ -
+

See live footer below ↓

@@ -660,14 +1086,10 @@ Desktop (800px+): 4 columns (2fr 1fr 1fr 1fr)
- Style Guide
- -
- - + + diff --git a/tokens.css b/tokens.css new file mode 100644 index 0000000..2ae157a --- /dev/null +++ b/tokens.css @@ -0,0 +1,651 @@ +/* ============================================ + DESIGN TOKENS — Make Post Sell + Single source of truth for the design system. + CSS custom properties, consumed everywhere. + ============================================ */ + +:root { + + /* ---- Color: Brand ---- */ + --color-green: #a3c765; + --color-green-dark: #8ab34e; + --color-green-light: #e8f5d4; + --color-blue: #98b6fa; + --color-blue-dark: #7a9de8; + --color-navy: #5871ad; + --color-navy-light: #82A8FF; + --color-purple: #9B59B6; + --color-purple-dark: #7B2D8E; + --color-danger: #CC6958; + --color-danger-dark: #b5503f; + --color-check: #678731; + --color-cross: #c77765; + --color-gold: #d4a843; + + /* ---- Color: Surface ---- */ + --surface-base: #FFFFFF; + --surface-dim: #F9F9FA; + --surface-container: #F4F4F5; + --surface-container-high: #EEEEEF; + --surface-inverse: #0b0b0b; + + /* ---- Color: Text ---- */ + --text-primary: #0b0b0b; + --text-body: #515151; + --text-secondary: #666666; + --text-muted: #777777; + --text-faint: #999999; + --text-disabled: #BBBBBB; + --text-inverse: #FFFFFF; + --text-on-green: #FFFFFF; + --text-on-navy: #FFFFFF; + + /* ---- Color: Border ---- */ + --border-default: #e0e0e0; + --border-light: #eeeeee; + --border-focus: #5871ad; + --border-error: #CC6958; + + /* ---- Color: State overlays ---- */ + --state-hover: rgba(0, 0, 0, 0.04); + --state-focus: rgba(88, 113, 173, 0.12); + --state-pressed: rgba(0, 0, 0, 0.08); + --state-dragged: rgba(0, 0, 0, 0.12); + --state-disabled-bg: rgba(0, 0, 0, 0.04); + --state-disabled-text: rgba(0, 0, 0, 0.26); + + /* ---- Color: Alerts ---- */ + --alert-success-bg: #e8f5d4; + --alert-success-text: #155724; + --alert-success-border: #c3e6cb; + --alert-info-bg: #dce8ff; + --alert-info-text: #0c5460; + --alert-info-border: #bee5eb; + --alert-warning-bg: #fff3cd; + --alert-warning-text: #856404; + --alert-warning-border: #ffeeba; + --alert-danger-bg: #f8d7da; + --alert-danger-text: #721c24; + --alert-danger-border: #f5c6cb; + + /* ---- Typography ---- */ + --font-family: helvetica, arial, sans-serif; + --font-mono: 'SF Mono', 'Menlo', 'Consolas', monospace; + + /* Type scale — major third (1.250) */ + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-md: 1.125rem; /* 18px */ + --text-lg: 1.25rem; /* 20px */ + --text-xl: 1.5rem; /* 24px */ + --text-2xl: 1.875rem; /* 30px */ + --text-3xl: 2.25rem; /* 36px */ + --text-4xl: 3rem; /* 48px */ + --text-5xl: 3.75rem; /* 60px */ + + /* Line heights */ + --leading-none: 1; + --leading-tight: 1.15; + --leading-snug: 1.3; + --leading-normal: 1.5; + --leading-relaxed: 1.65; + --leading-loose: 1.8; + + /* Font weights */ + --weight-regular: 400; + --weight-medium: 500; + --weight-semibold: 600; + --weight-bold: 700; + + /* Letter spacing */ + --tracking-tight: -0.02em; + --tracking-normal: 0; + --tracking-wide: 0.02em; + --tracking-wider: 0.05em; + --tracking-widest: 0.08em; + + /* ---- Spacing scale (4px base) ---- */ + --space-0: 0; + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + --space-10: 40px; + --space-12: 48px; + --space-16: 64px; + --space-20: 80px; + --space-24: 96px; + + /* ---- Shape (border radius) ---- */ + --radius-none: 0; + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-2xl: 24px; + --radius-full: 9999px; + + /* ---- Elevation (box-shadow) ---- */ + --elevation-0: none; + --elevation-1: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04); + --elevation-2: 0 2px 6px rgba(0,0,0,0.06), 0 2px 4px rgba(0,0,0,0.04); + --elevation-3: 0 4px 12px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.04); + --elevation-4: 0 8px 24px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04); + --elevation-5: 0 16px 48px rgba(0,0,0,0.10), 0 4px 12px rgba(0,0,0,0.05); + + /* ---- Motion ---- */ + /* Durations */ + --duration-instant: 50ms; + --duration-fast: 100ms; + --duration-normal: 200ms; + --duration-slow: 300ms; + --duration-slower: 400ms; + --duration-entrance: 250ms; + --duration-exit: 200ms; + + /* Easing — based on material curves */ + --ease-standard: cubic-bezier(0.2, 0, 0, 1); + --ease-decelerate: cubic-bezier(0, 0, 0, 1); + --ease-accelerate: cubic-bezier(0.3, 0, 1, 1); + --ease-emphasize: cubic-bezier(0.2, 0, 0, 1); + --ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275); + + /* ---- Z-index scale ---- */ + --z-base: 0; + --z-dropdown: 100; + --z-sticky: 200; + --z-overlay: 300; + --z-modal: 400; + --z-toast: 500; + --z-ribbon: 600; + + /* ---- Layout ---- */ + --content-width: 800px; + --content-narrow: 400px; + --content-wide: 1200px; + --bp-tablet: 800px; + --bp-desktop: 1200px; +} + +/* ============================================ + BASE RESETS & DEFAULTS + ============================================ */ + +*, *::before, *::after { + box-sizing: border-box; +} + +html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + font-size: 16px; + scroll-behavior: smooth; +} + +body { + font-family: var(--font-family); + font-size: var(--text-base); + font-weight: var(--weight-regular); + line-height: var(--leading-normal); + color: var(--text-body); + background-color: var(--surface-dim); + margin: 0; +} + +/* ============================================ + FOCUS — Accessible, visible, consistent + ============================================ */ + +:focus-visible { + outline: 2px solid var(--border-focus); + outline-offset: 2px; +} + +:focus:not(:focus-visible) { + outline: none; +} + +/* ============================================ + SELECTION + ============================================ */ + +::selection { + background: var(--color-navy-light); + color: var(--text-inverse); +} + +/* ============================================ + TYPOGRAPHY UTILITIES + ============================================ */ + +.type-display { + font-size: clamp(var(--text-3xl), 5vw, var(--text-5xl)); + font-weight: var(--weight-bold); + line-height: var(--leading-tight); + letter-spacing: var(--tracking-tight); + color: var(--text-primary); +} + +.type-headline-1 { + font-size: var(--text-4xl); + font-weight: var(--weight-bold); + line-height: var(--leading-tight); + letter-spacing: var(--tracking-tight); + color: var(--text-primary); +} + +.type-headline-2 { + font-size: var(--text-3xl); + font-weight: var(--weight-bold); + line-height: var(--leading-tight); + letter-spacing: var(--tracking-tight); + color: var(--text-primary); +} + +.type-headline-3 { + font-size: var(--text-2xl); + font-weight: var(--weight-bold); + line-height: var(--leading-snug); + color: var(--text-primary); +} + +.type-title-lg { + font-size: var(--text-xl); + font-weight: var(--weight-semibold); + line-height: var(--leading-snug); + color: var(--text-primary); +} + +.type-title { + font-size: var(--text-lg); + font-weight: var(--weight-semibold); + line-height: var(--leading-snug); + color: var(--text-primary); +} + +.type-title-sm { + font-size: var(--text-base); + font-weight: var(--weight-semibold); + line-height: var(--leading-normal); + color: var(--text-primary); +} + +.type-body-lg { + font-size: var(--text-md); + font-weight: var(--weight-regular); + line-height: var(--leading-relaxed); + color: var(--text-body); +} + +.type-body { + font-size: var(--text-base); + font-weight: var(--weight-regular); + line-height: var(--leading-relaxed); + color: var(--text-body); +} + +.type-body-sm { + font-size: var(--text-sm); + font-weight: var(--weight-regular); + line-height: var(--leading-relaxed); + color: var(--text-body); +} + +.type-label-lg { + font-size: var(--text-sm); + font-weight: var(--weight-semibold); + line-height: var(--leading-normal); + letter-spacing: var(--tracking-wide); + color: var(--text-primary); +} + +.type-label { + font-size: var(--text-xs); + font-weight: var(--weight-semibold); + line-height: var(--leading-normal); + letter-spacing: var(--tracking-wider); + text-transform: uppercase; + color: var(--text-muted); +} + +.type-caption { + font-size: var(--text-xs); + font-weight: var(--weight-regular); + line-height: var(--leading-normal); + color: var(--text-faint); +} + +.type-overline { + font-size: 11px; + font-weight: var(--weight-bold); + line-height: var(--leading-normal); + letter-spacing: var(--tracking-widest); + text-transform: uppercase; + color: var(--text-muted); +} + +.type-code { + font-family: var(--font-mono); + font-size: 0.9em; + background: var(--surface-container); + padding: 2px 6px; + border-radius: var(--radius-sm); +} + +/* ============================================ + ELEVATION UTILITIES + ============================================ */ + +.elevation-0 { box-shadow: var(--elevation-0); } +.elevation-1 { box-shadow: var(--elevation-1); } +.elevation-2 { box-shadow: var(--elevation-2); } +.elevation-3 { box-shadow: var(--elevation-3); } +.elevation-4 { box-shadow: var(--elevation-4); } +.elevation-5 { box-shadow: var(--elevation-5); } + +/* ============================================ + SURFACE UTILITIES + ============================================ */ + +.surface-base { background-color: var(--surface-base); } +.surface-dim { background-color: var(--surface-dim); } +.surface-container { background-color: var(--surface-container); } +.surface-inverse { background-color: var(--surface-inverse); color: var(--text-inverse); } + +/* ============================================ + INTERACTIVE STATE LAYER + Overlay that reacts to hover/focus/press. + Apply to any interactive element. + ============================================ */ + +.state-layer { + position: relative; + overflow: hidden; +} + +.state-layer::after { + content: ''; + position: absolute; + inset: 0; + background: currentColor; + opacity: 0; + transition: opacity var(--duration-fast) var(--ease-standard); + pointer-events: none; + border-radius: inherit; +} + +.state-layer:hover::after { + opacity: 0.04; +} + +.state-layer:focus-visible::after { + opacity: 0.08; +} + +.state-layer:active::after { + opacity: 0.10; +} + +/* ============================================ + RIPPLE EFFECT + Add .ripple to any interactive element. + Requires JS initialization (see styleguide). + ============================================ */ + +.ripple { + position: relative; + overflow: hidden; +} + +.ripple-wave { + position: absolute; + border-radius: 50%; + background: rgba(255, 255, 255, 0.35); + transform: scale(0); + animation: ripple-expand var(--duration-slower) var(--ease-decelerate) forwards; + pointer-events: none; +} + +@keyframes ripple-expand { + to { + transform: scale(2.5); + opacity: 0; + } +} + +/* Dark ripple for light backgrounds */ +.ripple-dark .ripple-wave { + background: rgba(0, 0, 0, 0.12); +} + +/* ============================================ + TRANSITION UTILITIES + ============================================ */ + +.transition-all { transition: all var(--duration-normal) var(--ease-standard); } +.transition-colors { transition: color var(--duration-normal) var(--ease-standard), background-color var(--duration-normal) var(--ease-standard), border-color var(--duration-normal) var(--ease-standard); } +.transition-shadow { transition: box-shadow var(--duration-normal) var(--ease-standard); } +.transition-transform { transition: transform var(--duration-normal) var(--ease-standard); } + +/* ============================================ + ENTRANCE ANIMATIONS + ============================================ */ + +@keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes fade-in-up { + from { opacity: 0; transform: translateY(16px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes fade-in-down { + from { opacity: 0; transform: translateY(-16px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes fade-in-scale { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes slide-in-right { + from { opacity: 0; transform: translateX(24px); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes slide-in-left { + from { opacity: 0; transform: translateX(-24px); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes scale-in { + from { transform: scale(0); } + to { transform: scale(1); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +@keyframes shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +} + +.animate-fade-in { animation: fade-in var(--duration-entrance) var(--ease-decelerate) both; } +.animate-fade-in-up { animation: fade-in-up var(--duration-entrance) var(--ease-decelerate) both; } +.animate-fade-in-down { animation: fade-in-down var(--duration-entrance) var(--ease-decelerate) both; } +.animate-fade-in-scale { animation: fade-in-scale var(--duration-entrance) var(--ease-decelerate) both; } +.animate-slide-in-right { animation: slide-in-right var(--duration-slow) var(--ease-decelerate) both; } +.animate-slide-in-left { animation: slide-in-left var(--duration-slow) var(--ease-decelerate) both; } +.animate-pulse { animation: pulse 2s var(--ease-standard) infinite; } +.animate-spin { animation: spin 1s linear infinite; } + +/* Staggered delays for lists */ +.stagger-1 { animation-delay: 50ms; } +.stagger-2 { animation-delay: 100ms; } +.stagger-3 { animation-delay: 150ms; } +.stagger-4 { animation-delay: 200ms; } +.stagger-5 { animation-delay: 250ms; } +.stagger-6 { animation-delay: 300ms; } + +/* Respect reduced motion */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* ============================================ + SKELETON LOADING + ============================================ */ + +.skeleton { + background: linear-gradient(90deg, + var(--surface-container) 25%, + var(--surface-container-high) 50%, + var(--surface-container) 75% + ); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: var(--radius-md); + color: transparent !important; +} + +.skeleton-text { + height: 1em; + margin-bottom: 0.5em; + border-radius: var(--radius-sm); +} + +.skeleton-circle { + border-radius: var(--radius-full); +} + +/* ============================================ + SPINNER + ============================================ */ + +.spinner { + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid var(--border-default); + border-top-color: var(--color-navy); + border-radius: var(--radius-full); + animation: spin 0.6s linear infinite; +} + +.spinner-sm { width: 14px; height: 14px; } +.spinner-lg { width: 32px; height: 32px; border-width: 3px; } + +/* ============================================ + DISABLED STATE + ============================================ */ + +[disabled], +.disabled { + opacity: 0.38; + pointer-events: none; + cursor: not-allowed; +} + +/* ============================================ + SCROLL-TRIGGERED REVEAL + Elements start hidden, JS adds .revealed + ============================================ */ + +.reveal { + opacity: 0; + transform: translateY(20px); + transition: opacity var(--duration-slow) var(--ease-decelerate), + transform var(--duration-slow) var(--ease-decelerate); +} + +.reveal.revealed { + opacity: 1; + transform: translateY(0); +} + +.reveal-scale { + opacity: 0; + transform: scale(0.96); + transition: opacity var(--duration-slow) var(--ease-decelerate), + transform var(--duration-slow) var(--ease-decelerate); +} + +.reveal-scale.revealed { + opacity: 1; + transform: scale(1); +} + +/* ============================================ + SPACING UTILITIES + ============================================ */ + +.mt-0 { margin-top: var(--space-0); } +.mt-1 { margin-top: var(--space-1); } +.mt-2 { margin-top: var(--space-2); } +.mt-3 { margin-top: var(--space-3); } +.mt-4 { margin-top: var(--space-4); } +.mt-6 { margin-top: var(--space-6); } +.mt-8 { margin-top: var(--space-8); } +.mt-10 { margin-top: var(--space-10); } + +.mb-0 { margin-bottom: var(--space-0); } +.mb-1 { margin-bottom: var(--space-1); } +.mb-2 { margin-bottom: var(--space-2); } +.mb-3 { margin-bottom: var(--space-3); } +.mb-4 { margin-bottom: var(--space-4); } +.mb-6 { margin-bottom: var(--space-6); } +.mb-8 { margin-bottom: var(--space-8); } +.mb-10 { margin-bottom: var(--space-10); } + +.p-0 { padding: var(--space-0); } +.p-2 { padding: var(--space-2); } +.p-4 { padding: var(--space-4); } +.p-6 { padding: var(--space-6); } +.p-8 { padding: var(--space-8); } + +.gap-2 { gap: var(--space-2); } +.gap-3 { gap: var(--space-3); } +.gap-4 { gap: var(--space-4); } +.gap-6 { gap: var(--space-6); } +.gap-8 { gap: var(--space-8); } + +/* ============================================ + RESPONSIVE CONTAINER + ============================================ */ + +.container { + width: 100%; + max-width: var(--content-width); + margin-left: auto; + margin-right: auto; + padding-left: var(--space-6); + padding-right: var(--space-6); +} + +.container-narrow { + max-width: var(--content-narrow); +} + +.container-wide { + max-width: var(--content-wide); +}