From d8425867c5750c94f9c38ae2dc6cd24ab71e2065 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Fri, 6 Mar 2026 08:17:47 -0500 Subject: [PATCH] Full design system: tokens.css + comprehensive styleguide tokens.css: CSS custom properties for colors, surfaces, text, borders, typography scale (major third), spacing (4px base), shape (7 radius levels), elevation (5 shadow levels), motion (4 easing curves, 7 durations), state layers, ripple effect, entrance animations (6 types + stagger), skeleton loading, spinner, scroll reveal, disabled states, spacing/margin utilities, prefers-reduced-motion, font smoothing, box-sizing reset. styleguide.html: live interactive demos for every token and component. Elevation boxes, motion curve hover demos, ripple click demo, skeleton shimmer, spinner sizes, focus ring demo, state layer demo, full TOC with anchor links. --- index.html | 1 + styleguide.html | 1263 ++++++++++++++++++++++++++++++++--------------- tokens.css | 651 ++++++++++++++++++++++++ 3 files changed, 1507 insertions(+), 408 deletions(-) create mode 100644 tokens.css 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
@@ -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); +}