www.makepostsell.com/tokens.css
russell@unturf.com d8425867c5 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.
2026-03-06 08:17:47 -05:00

651 lines
18 KiB
CSS

/* ============================================
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);
}