Skip to content

Espresso Cloud — Design System v1.0

Netlify for business apps. A SaaS open-source application hosting platform that makes deploying Frappe/ERPNext and modern frontends as simple as git push.

Implementation: Tailwind CSS via tailwind.config.js + VitePress custom theme. All tokens below map to Tailwind utility classes.


1. Brand Identity & Philosophy

Brand Essence

An espresso is precision-engineered: exact temperature, exact pressure, exact timing. The result is something concentrated, reliable, and unmistakably premium. That is the experience this platform delivers for infrastructure deployment.

Design Principles

  1. Precision over decoration. Every visual element serves a purpose. Ornament is earned through clarity.
  2. Warmth signals trust. Enterprise platforms default to cold blues and stark whites. Espresso Cloud uses warm tones to communicate approachability without sacrificing authority.
  3. Craft implies quality. The serif-meets-grotesk typography pairing signals intentional design — the same intention applied to the platform's engineering.
  4. Motion is functional. Animations orient the user, confirm actions, and create rhythm. They never delay or distract.
  5. Density with breathing room. Information-dense when needed (pricing, features), spacious when persuading (hero, CTA).

Voice & Tone

  • Technical but not jargon-heavy
  • Confident but not aggressive
  • Warm but not casual
  • Precise language, short sentences, active voice

Competitor Differentiation

AspectSevallaPikaPodsEspresso Cloud
Primary hueOrange #FA7216GreenCopper #C4501A
TypographySans-serif onlySystem fontsSerif + Grotesk pairing
MotionCascading slide-upsMinimalPurposeful reveals + warm glow micro-interactions
PersonalityClean minimalistUtilitarianWarm premium craft
BackgroundPure whiteWhiteWarm cream #FBF8F4

2. Color System

2.1 Core Palette

SwatchNameHexUsage
#1C1210Deep Espresso#1C1210Near-black, dark backgrounds, primary text
#2E1F1ARoast#2E1F1ADark elevated surfaces
#C4501ACopper#C4501APrimary CTA, links, brand accents
#A84315Copper Hover#A84315Hover state for copper elements
#8E3812Copper Active#8E3812Active/pressed state
#D4956ACrema Gold#D4956AWarm decorative accent, overlines, highlights
#FBF8F4Foam White#FBF8F4Primary light background
#F3EDE6Parchment#F3EDE6Alternate light background
#E8DFD5Latte#E8DFD5Soft background, dividers

Why Copper instead of Orange? #C4501A is distinctly burnt/brown-orange — firmly separated from Sevalla's pure orange #FA7216. It reads as warm and earthy, not energetic and citrus.

Tailwind usage: bg-ec-copper, text-ec-crema, bg-ec-foam, border-ec-latte, text-ec-espresso, etc.

2.2 Warm Neutral Grays

All grays have a subtle warm undertone (shifted toward brown/amber) instead of the typical blue-gray used in most SaaS products.

Tailwind usage: bg-warmgray-100, text-warmgray-600, border-warmgray-300, etc.

warmgray-50:    #FAF8F6
warmgray-100:   #F3EDE6
warmgray-200:   #E8DFD5
warmgray-300:   #D4C8BA
warmgray-400:   #B5A899
warmgray-500:   #8F8277
warmgray-600:   #6B6059
warmgray-700:   #4D4440
warmgray-800:   #352E2B
warmgray-900:   #1C1210

2.3 Semantic Colors

Tailwind usage: text-steam, bg-mint, text-amber-dark, bg-rosso-solid, etc.

CategoryText (.dark)DefaultSolid BGSoft BG
Info (steam)#4A7FA8 (.light: #8CBCE0)#5B8FB9#6BA0CArgba(91,143,185,0.12)
Success (mint)#2D8F6F (.light: #6DDAB4)#3DB88C#4AC99Argba(61,184,140,0.12)
Warning (amber)#9A6B2C (.light: #E0B060)#C4872E#D49A3Argba(196,135,46,0.12)
Danger (rosso)#B83340 (.light: #F07080)#D94052#E55565rgba(217,64,82,0.12)

2.4 Light Mode Theme

These CSS custom properties are set in docs/.vitepress/theme/style.css to override VitePress defaults. Use the Tailwind utility classes (e.g., bg-ec-foam, text-ec-espresso) rather than referencing these variables directly.

css
:root {
  /* Backgrounds */
  --ec-bg:              #FBF8F4;   /* Foam White — main canvas */
  --ec-bg-alt:          #F3EDE6;   /* Parchment — alternate sections */
  --ec-bg-elevated:     #FFFFFF;   /* Cards, dialogs floating above bg */
  --ec-bg-soft:         #F3EDE6;   /* Subtle distinction areas */
  --ec-bg-inverse:      #1C1210;   /* Dark sections for contrast (footer) */

  /* Text */
  --ec-text-1:          #1C1210;   /* Primary text — Deep Espresso */
  --ec-text-2:          #6B6059;   /* Secondary/muted text */
  --ec-text-3:          #8F8277;   /* Tertiary/placeholder text */
  --ec-text-inverse:    #FBF8F4;   /* Text on dark backgrounds */

  /* Borders */
  --ec-border:          #D4C8BA;   /* Interactive component borders */
  --ec-divider:         #E8DFD5;   /* Section dividers */
  --ec-gutter:          #E8DFD5;   /* Layout gutters */

  /* Brand / CTA */
  --ec-brand-1:         #C4501A;   /* Copper — links, text accents */
  --ec-brand-2:         #A84315;   /* Copper Hover */
  --ec-brand-3:         #C4501A;   /* Copper — solid button bg */
  --ec-brand-soft:      rgba(196, 80, 26, 0.10);

  /* Accent */
  --ec-accent-1:        #D4956A;   /* Crema Gold — decorative, highlights */
  --ec-accent-soft:     rgba(212, 149, 106, 0.15);

  /* Shadows — warm-tinted */
  --ec-shadow-sm:       0 1px 2px rgba(28,18,16,0.05), 0 1px 2px rgba(28,18,16,0.04);
  --ec-shadow-md:       0 4px 12px rgba(28,18,16,0.06), 0 1px 4px rgba(28,18,16,0.04);
  --ec-shadow-lg:       0 12px 32px rgba(28,18,16,0.08), 0 2px 6px rgba(28,18,16,0.04);
  --ec-shadow-xl:       0 18px 56px rgba(28,18,16,0.12), 0 4px 12px rgba(28,18,16,0.06);

  /* Glow — for hover/focus states */
  --ec-glow-brand:      0 0 0 3px rgba(196, 80, 26, 0.15);
  --ec-glow-accent:     0 0 20px rgba(212, 149, 106, 0.20);
}

2.5 Dark Mode Theme

Dark mode activates via the .dark class (VitePress toggle). Tailwind dark variants use dark: prefix (e.g., dark:bg-[#0F0B09]). VitePress components auto-adapt via these overrides:

css
.dark {
  /* Backgrounds — rich warm darks, never pure black */
  --ec-bg:              #0F0B09;
  --ec-bg-alt:          #1A1311;
  --ec-bg-elevated:     #241B17;
  --ec-bg-soft:         #1A1311;
  --ec-bg-inverse:      #FBF8F4;

  /* Text — warm off-whites */
  --ec-text-1:          #EDE6DC;
  --ec-text-2:          #A89A8E;
  --ec-text-3:          #6B6059;
  --ec-text-inverse:    #1C1210;

  /* Borders */
  --ec-border:          #3D322C;
  --ec-divider:         #2A221E;
  --ec-gutter:          #0A0706;

  /* Brand — lighter copper for dark bg readability */
  --ec-brand-1:         #E0875A;
  --ec-brand-2:         #C4501A;
  --ec-brand-3:         #B8481A;
  --ec-brand-soft:      rgba(224, 135, 90, 0.14);

  /* Accent */
  --ec-accent-1:        #D4956A;
  --ec-accent-soft:     rgba(212, 149, 106, 0.12);

  /* Semantic — adjusted for dark mode contrast */
  --ec-info-1:          #8CBCE0;
  --ec-success-1:       #6DDAB4;
  --ec-warning-1:       #E0B060;
  --ec-danger-1:        #F07080;

  /* Shadows — deeper on dark */
  --ec-shadow-sm:       0 1px 2px rgba(0,0,0,0.3);
  --ec-shadow-md:       0 4px 12px rgba(0,0,0,0.4);
  --ec-shadow-lg:       0 12px 32px rgba(0,0,0,0.5);
  --ec-shadow-xl:       0 18px 56px rgba(0,0,0,0.6);

  --ec-glow-brand:      0 0 0 3px rgba(224, 135, 90, 0.20);
  --ec-glow-accent:     0 0 24px rgba(212, 149, 106, 0.15);
}

2.6 Accessibility — Contrast Ratios

CombinationRatioWCAG
Copper #C4501A on Foam #FBF8F44.7:1AA (normal), AAA (large)
Deep Espresso #1C1210 on Foam #FBF8F415.8:1AAA
#E0875A on Dark #0F0B095.2:1AA
#EDE6DC on Dark #0F0B0914.1:1AAA

2.7 VitePress Variable Mapping

These are already applied in style.css. Shown here for reference — you don't need to write these again.

css
:root {
  --vp-c-brand-1: var(--ec-brand-1);
  --vp-c-brand-2: var(--ec-brand-2);
  --vp-c-brand-3: var(--ec-brand-3);
  --vp-c-brand-soft: var(--ec-brand-soft);

  --vp-c-bg: var(--ec-bg);
  --vp-c-bg-alt: var(--ec-bg-alt);
  --vp-c-bg-elv: var(--ec-bg-elevated);
  --vp-c-bg-soft: var(--ec-bg-soft);

  --vp-c-text-1: var(--ec-text-1);
  --vp-c-text-2: var(--ec-text-2);
  --vp-c-text-3: var(--ec-text-3);

  --vp-c-border: var(--ec-border);
  --vp-c-divider: var(--ec-divider);
  --vp-c-gutter: var(--ec-gutter);

  --vp-c-gray-1: #D4C8BA;
  --vp-c-gray-2: #E8DFD5;
  --vp-c-gray-3: #F3EDE6;
  --vp-c-gray-soft: rgba(143, 130, 119, 0.14);

  --vp-c-green-1: var(--ec-success-1);
  --vp-c-green-2: var(--ec-success-2);
  --vp-c-green-3: var(--ec-success-3);
  --vp-c-green-soft: var(--ec-success-soft);

  --vp-c-yellow-1: var(--ec-warning-1);
  --vp-c-yellow-2: var(--ec-warning-2);
  --vp-c-yellow-3: var(--ec-warning-3);
  --vp-c-yellow-soft: var(--ec-warning-soft);

  --vp-c-red-1: var(--ec-danger-1);
  --vp-c-red-2: var(--ec-danger-2);
  --vp-c-red-3: var(--ec-danger-3);
  --vp-c-red-soft: var(--ec-danger-soft);

  --vp-shadow-1: var(--ec-shadow-sm);
  --vp-shadow-2: var(--ec-shadow-md);
  --vp-shadow-3: var(--ec-shadow-lg);
  --vp-shadow-4: var(--ec-shadow-xl);
  --vp-shadow-5: var(--ec-shadow-xl);

  /* Buttons */
  --vp-button-brand-border: transparent;
  --vp-button-brand-text: #FBF8F4;
  --vp-button-brand-bg: var(--ec-brand-3);
  --vp-button-brand-hover-border: transparent;
  --vp-button-brand-hover-text: #FBF8F4;
  --vp-button-brand-hover-bg: var(--ec-brand-2);
  --vp-button-brand-active-border: transparent;
  --vp-button-brand-active-text: #FBF8F4;
  --vp-button-brand-active-bg: var(--ec-brand-1);

  --vp-button-alt-border: var(--ec-border);
  --vp-button-alt-text: var(--ec-text-1);
  --vp-button-alt-bg: var(--ec-bg-alt);
  --vp-button-alt-hover-border: var(--ec-brand-1);
  --vp-button-alt-hover-text: var(--ec-brand-1);
  --vp-button-alt-hover-bg: var(--ec-bg-elevated);

  /* Nav */
  --vp-nav-bg-color: rgba(251, 248, 244, 0.85);
  --vp-nav-height: 72px;

  /* Hero gradient */
  --vp-home-hero-name-color: var(--ec-brand-1);
  --vp-home-hero-name-background: linear-gradient(135deg, #C4501A 0%, #D4956A 100%);
}

.dark {
  --vp-c-gray-1: #4D4440;
  --vp-c-gray-2: #3D322C;
  --vp-c-gray-3: #2A221E;
  --vp-c-gray-soft: rgba(107, 96, 89, 0.16);

  --vp-nav-bg-color: rgba(15, 11, 9, 0.85);
  --vp-home-hero-name-background: linear-gradient(135deg, #E0875A 0%, #D4956A 100%);
}

3. Typography

3.1 Font Families

RoleFamilySourceCharacterWeights
Display / HeroInstrument SerifGoogle FontsElegant, editorial, high-contrast serif. Unexpected in tech — immediately differentiates. The "espresso shot" of the type system.400, 400 Italic
HeadingsSpace GroteskGoogle FontsGeometric, monolinear, modern. The technical counterpart to Instrument Serif.300, 400, 500, 600, 700
BodyDM SansGoogle FontsGeometric sans-serif with generous x-height and open apertures. Reads cleanly at all sizes.400, 500, 600, 700 + Italics
MonospaceJetBrains MonoGoogle FontsDeveloper-friendly monospace with coding ligatures. Signals technical credibility.400, 500, 700

3.2 Google Fonts Import

css
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400;1,9..40,500&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500;700&family=Space+Grotesk:wght@300;400;500;600;700&display=swap');

3.3 Font Tokens

Tailwind usage:

html
<h1 class="font-display">Hero Headline</h1>
<h2 class="font-heading">Section Title</h2>
<p class="font-body">Body paragraph</p>
<code class="font-mono">code snippet</code>

Defined in tailwind.config.jstheme.extend.fontFamily:

  • font-display → Instrument Serif, Georgia, serif
  • font-heading → Space Grotesk, system-ui, sans-serif
  • font-body → DM Sans, system-ui, sans-serif (also set as VitePress base)
  • font-mono → JetBrains Mono, Menlo, Monaco, Consolas, monospace

3.4 Size Scale

css
:root {
  --ec-text-xs:    0.75rem;    /* 12px */
  --ec-text-sm:    0.875rem;   /* 14px */
  --ec-text-base:  1rem;       /* 16px */
  --ec-text-lg:    1.125rem;   /* 18px */
  --ec-text-xl:    1.25rem;    /* 20px */
  --ec-text-2xl:   1.5rem;     /* 24px */
  --ec-text-3xl:   1.875rem;   /* 30px */
  --ec-text-4xl:   2.25rem;    /* 36px */
  --ec-text-5xl:   3rem;       /* 48px */
  --ec-text-6xl:   3.75rem;    /* 60px */
  --ec-text-7xl:   4.5rem;     /* 72px */
}

3.5 Line Height & Spacing

css
:root {
  /* Line heights */
  --ec-leading-none:    1;
  --ec-leading-tight:   1.15;
  --ec-leading-snug:    1.3;
  --ec-leading-normal:  1.5;
  --ec-leading-relaxed: 1.65;
  --ec-leading-loose:   1.8;

  /* Letter spacing */
  --ec-tracking-tighter: -0.03em;
  --ec-tracking-tight:   -0.015em;
  --ec-tracking-normal:  0;
  --ec-tracking-wide:    0.025em;
  --ec-tracking-wider:   0.05em;
  --ec-tracking-widest:  0.1em;

  /* Font weights */
  --ec-weight-light:     300;
  --ec-weight-regular:   400;
  --ec-weight-medium:    500;
  --ec-weight-semibold:  600;
  --ec-weight-bold:      700;
}

3.6 Type Scale Application

ElementFontSizeWeightLine HeightLetter Spacing
Hero headline--ec-font-display72px (48px mobile)4001-0.03em
Hero subtitle--ec-font-body20px4001.650
Section title (H2)--ec-font-display48px (36px mobile)4001.15-0.015em
Feature heading (H3)--ec-font-heading24px6001.3-0.015em
Card title (H4)--ec-font-heading20px6001.30
Subheading (H5)--ec-font-heading18px5001.30
Overline / Label--ec-font-heading12px5001.50.1em (uppercase)
Body--ec-font-body16px4001.650
Body large--ec-font-body18px4001.650
Body small--ec-font-body14px4001.50
Nav links--ec-font-heading14px50010.025em
Button text--ec-font-heading14px60010.025em
Inline code--ec-font-mono14px4001.50
Code block--ec-font-mono14px4001.70
Price number--ec-font-mono48px7001-0.03em

4. Spacing & Layout

4.1 Spacing Scale (8px base)

css
:root {
  --ec-space-0:    0;
  --ec-space-px:   1px;
  --ec-space-0-5:  0.125rem;   /*  2px */
  --ec-space-1:    0.25rem;    /*  4px */
  --ec-space-1-5:  0.375rem;   /*  6px */
  --ec-space-2:    0.5rem;     /*  8px — base unit */
  --ec-space-3:    0.75rem;    /* 12px */
  --ec-space-4:    1rem;       /* 16px */
  --ec-space-5:    1.25rem;    /* 20px */
  --ec-space-6:    1.5rem;     /* 24px */
  --ec-space-8:    2rem;       /* 32px */
  --ec-space-10:   2.5rem;     /* 40px */
  --ec-space-12:   3rem;       /* 48px */
  --ec-space-16:   4rem;       /* 64px */
  --ec-space-20:   5rem;       /* 80px */
  --ec-space-24:   6rem;       /* 96px */
  --ec-space-32:   8rem;       /* 128px */
  --ec-space-40:   10rem;      /* 160px */
}

4.2 Semantic Spacing

css
:root {
  --ec-section-gap:      var(--ec-space-32);   /* 128px — between major SPA sections */
  --ec-section-gap-sm:   var(--ec-space-24);   /* 96px — smaller section gaps */
  --ec-content-gap:      var(--ec-space-16);   /* 64px — between content blocks */
  --ec-card-padding:     var(--ec-space-8);    /* 32px — card internal padding */
  --ec-card-gap:         var(--ec-space-6);    /* 24px — gap between cards */
  --ec-inline-gap:       var(--ec-space-4);    /* 16px — between inline elements */
}

4.3 Container Widths

css
:root {
  --ec-container-xs:   480px;
  --ec-container-sm:   640px;
  --ec-container-md:   768px;
  --ec-container-lg:   1024px;
  --ec-container-xl:   1200px;   /* Primary content width */
  --ec-container-2xl:  1400px;   /* Max layout width */
  --ec-gutter-page:    var(--ec-space-6);   /* 24px page gutters */
}

4.4 Layout Pattern

Each SPA section spans full viewport width with contained content.

Tailwind usage:

html
<!-- Section wrapper -->
<section class="w-full py-32 px-6 bg-ec-foam">
  <!-- Contained content -->
  <div class="w-full max-w-container-xl mx-auto">
    <!-- Feature grid with auto-fit -->
    <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
      <!-- cards -->
    </div>
  </div>
</section>

Custom container widths available: max-w-container-xs through max-w-container-2xl.

4.5 Border Radius

css
:root {
  --ec-radius-sm:    4px;
  --ec-radius-md:    8px;
  --ec-radius-lg:    12px;
  --ec-radius-xl:    16px;
  --ec-radius-2xl:   24px;
  --ec-radius-full:  9999px;   /* Pills, avatars */
}

5. Component Patterns

All components can be built with Tailwind utility classes. The examples below show the Tailwind approach alongside the semantic intent.

5.1 Buttons

Three tiers: Primary (copper, high emphasis), Secondary (outlined, medium emphasis), Ghost (text-only, low emphasis).

Primary Button:

html
<button class="font-heading text-sm font-semibold tracking-wide
  px-6 py-3 bg-ec-copper text-ec-foam border-2 border-transparent rounded-md
  cursor-pointer transition-all duration-normal ease-ec-out
  hover:bg-ec-copper-hover hover:shadow-glow-brand hover:-translate-y-px
  active:translate-y-0 active:shadow-none">
  Get Started
</button>

Secondary Button:

html
<button class="font-heading text-sm font-semibold tracking-wide
  px-6 py-3 bg-transparent text-ec-espresso border-2 border-warmgray-300 rounded-md
  cursor-pointer transition-all duration-normal ease-ec-out
  hover:border-ec-copper hover:text-ec-copper hover:bg-ec-copper/10">
  View on GitHub
</button>

Ghost Button:

html
<button class="font-heading text-sm font-medium tracking-wide
  px-4 py-2 bg-transparent text-ec-copper border-none
  cursor-pointer transition-colors duration-fast
  hover:text-ec-copper-hover hover:underline hover:underline-offset-4">
  Learn more
</button>

Large variant (hero CTAs): Add px-8 py-4 text-base rounded-lg

5.2 Cards

Base card:

html
<div class="bg-white border border-warmgray-200 rounded-xl p-8
  transition-all duration-300 ease-ec-out
  hover:border-ec-crema hover:shadow-ec-md hover:-translate-y-0.5">
  <!-- card content -->
</div>

Feature card with icon:

html
<div class="bg-white border border-warmgray-200 rounded-xl p-8
  transition-all duration-300 ease-ec-out
  hover:border-ec-crema hover:shadow-ec-md hover:-translate-y-0.5">
  <div class="w-12 h-12 rounded-lg bg-ec-copper/10 flex items-center justify-center mb-4 text-ec-copper">
    <!-- Lucide icon -->
  </div>
  <h3 class="font-heading text-xl font-semibold text-ec-espresso mb-2">Feature Title</h3>
  <p class="font-body text-base text-warmgray-600 leading-relaxed">Feature description.</p>
</div>

Pricing card — featured variant uses border-ec-copper border-2 relative with a positioned badge.

5.3 Navigation (Fixed Top Nav)

html
<nav class="fixed top-0 inset-x-0 z-50 h-18 px-6
  bg-ec-foam/85 backdrop-blur-nav backdrop-saturate-nav
  border-b border-warmgray-200
  transition-all duration-300">

  <div class="max-w-container-xl mx-auto h-full flex items-center justify-between">
    <!-- Logo: Instrument Serif wordmark -->
    <a class="font-display text-2xl text-ec-espresso tracking-tight no-underline">
      <span class="text-ec-copper">Espresso</span> Cloud
    </a>

    <!-- Nav links -->
    <ul class="flex items-center gap-8">
      <li><a href="#features" class="font-heading text-sm font-medium tracking-wide text-warmgray-600
        hover:text-ec-espresso transition-colors duration-fast">Features</a></li>
      <!-- ... more links ... -->
    </ul>

    <!-- CTA -->
    <button class="font-heading text-sm font-semibold px-5 py-2
      bg-ec-copper text-ec-foam rounded-md
      hover:bg-ec-copper-hover hover:shadow-glow-brand
      transition-all duration-normal">
      Get Started
    </button>
  </div>
</nav>

Nav link underline animation uses a CSS ::after pseudo-element (defined in style.css) since Tailwind can't target pseudo-element width transitions.

5.4 Badges

html
<!-- Brand badge -->
<span class="inline-flex items-center font-heading text-xs font-semibold
  tracking-wider uppercase px-3 py-1 rounded-full
  bg-ec-copper/10 text-ec-copper">
  New
</span>

<!-- Success badge -->
<span class="... bg-mint/10 text-mint-dark">Active</span>

<!-- Tech stack badge (Supported Tech section) -->
<span class="inline-flex items-center gap-2 font-heading text-sm font-semibold
  px-4 py-2 rounded-full bg-white border border-warmgray-200 text-ec-espresso">
  <img src="..." class="w-5 h-5" /> React
</span>

5.5 Overline / Section Label

A distinctive element — the small uppercase label above section titles, with a decorative line.

html
<div class="flex items-center gap-3 mb-4">
  <span class="w-6 h-0.5 bg-ec-crema rounded-full"></span>
  <span class="font-heading text-xs font-medium tracking-widest uppercase text-ec-crema">
    CAPABILITIES
  </span>
</div>

5.6 Stat / Metric Display

For trust indicators (99.9% uptime, etc.).

html
<div class="text-center">
  <span class="font-mono text-4xl font-bold text-ec-espresso leading-none tabular-nums">
    99.9%
  </span>
  <p class="font-body text-sm text-warmgray-600 mt-2">Uptime SLA</p>
</div>

Use tabular-nums (Tailwind utility) for aligned number columns in pricing.

5.7 Dividers

html
<!-- Standard -->
<hr class="w-full h-px bg-warmgray-200 border-none" />

<!-- Decorative gradient fade -->
<hr class="h-px border-none bg-divider-fade opacity-30" />

5.8 Focus States

Global :focus-visible with warm copper glow is defined in style.css — applies to all interactive elements automatically.


6. Motion & Animation

6.1 Principles

  1. Purposeful — every animation communicates state, draws attention, or orients the user.
  2. Performant — only animate transform and opacity (compositor-friendly). Never animate layout properties.
  3. Respectful — all animations disabled for prefers-reduced-motion: reduce.

6.2 Easing & Duration (Tailwind utilities)

Easingease-{name} classes:

ClassCurveUsage
ease-ec-outcubic-bezier(0.25, 0.46, 0.45, 0.94)Standard interactions
ease-ec-entercubic-bezier(0.0, 0.0, 0.2, 1.0)Elements appearing
ease-ec-exitcubic-bezier(0.4, 0.0, 1.0, 1.0)Elements disappearing
ease-ec-springcubic-bezier(0.34, 1.56, 0.64, 1.0)Subtle bounce for emphasis
ease-ec-smoothcubic-bezier(0.4, 0.0, 0.2, 1.0)Gentle, relaxed motion

Durationduration-{name} classes:

ClassValue
duration-instant75ms
duration-fast150ms
duration-normal250ms
duration-slow400ms
duration-slower600ms
duration-slowest1000ms

6.3 Keyframe Animations (Tailwind utilities)

All keyframes are defined in tailwind.config.js and available as animate-{name} classes:

ClassEffect
animate-reveal-upFade in + rise 24px (600ms)
animate-reveal-leftFade in + slide from left 24px
animate-reveal-rightFade in + slide from right 24px
animate-reveal-scaleFade in + scale from 95%
animate-steam-riseAmbient vapor rising (infinite, hero only)
animate-glow-pulseWarm copper glow pulse (infinite)
animate-shimmerGradient shimmer sweep (infinite)

Stagger delays (defined in style.css):

html
<div class="animate-reveal-up stagger-1">First</div>
<div class="animate-reveal-up stagger-2">Second (80ms delay)</div>
<div class="animate-reveal-up stagger-3">Third (160ms delay)</div>

6.4 Scroll Reveal System

Elements use data-reveal attributes and animate in when entering the viewport. The CSS is in style.css; a lightweight Intersection Observer composable adds the .revealed trigger class.

Usage in Vue templates:

html
<div data-reveal="up" class="stagger-1">Fades up on scroll</div>
<div data-reveal="left" class="stagger-2">Slides from left</div>
<div data-reveal="scale" class="stagger-3">Scales in</div>

Companion Vue composable (composables/useScrollReveal.ts):

ts
export function useScrollReveal() {
  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          entry.target.classList.add('revealed')
          observer.unobserve(entry.target)
        }
      })
    },
    { threshold: 0.15, rootMargin: '0px 0px -40px 0px' }
  )
  document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el))
  return observer
}

Respects prefers-reduced-motion: reduce — elements show instantly with no animation.

6.5 Ambient Steam Effect (Hero Only)

Soft, blurred shapes that drift upward in the hero background. Uses pseudo-elements with animate-steam-rise:

html
<!-- Hero section uses ec-hero class defined in style.css -->
<section class="ec-hero relative overflow-hidden">
  <!-- ::before and ::after create the steam blobs via CSS -->
  <!-- Content here -->
</section>

The ec-hero class and its pseudo-elements are defined in style.css since Tailwind can't generate ::before/::after content with radial gradients and blur.

6.6 Micro-interactions

Card hover lift: hover:-translate-y-0.5 hover:shadow-ec-md transition-all duration-300 ease-ec-out

Button glow: hover:shadow-glow-brand hover:-translate-y-px transition-all duration-normal ease-ec-out

Smooth scroll: Set globally in style.css with scroll-padding-top: 88px (nav + buffer).


7. Iconography

7.1 Library

Lucide Iconslucide.dev

  • Open source, MIT licensed
  • Consistent 24x24 grid with 2px stroke
  • Vue components via lucide-vue-next
  • Clean geometric style pairs well with Space Grotesk

7.2 Icon Sizing

css
:root {
  --ec-icon-xs:   16px;   /* Inline with small text, badges */
  --ec-icon-sm:   20px;   /* Inline with body text */
  --ec-icon-md:   24px;   /* Default / nav */
  --ec-icon-lg:   32px;   /* Feature card icons */
  --ec-icon-xl:   48px;   /* Section illustrations */
}

7.3 Icon Color Rules

ContextColor
Defaultvar(--ec-text-2) (muted)
Hover / Activevar(--ec-text-1) or var(--ec-brand-1)
Feature cardsvar(--ec-brand-1) inside var(--ec-brand-soft) container
SemanticCorresponding semantic color (success-1, danger-1, etc.)
SectionIcons
CI/CD & DeploymentGitBranch, Workflow, Rocket
EnvironmentsLayers, Server, Globe
Zero-downtimeRefreshCcw, ShieldCheck, Timer
StorageDatabase, HardDrive, Cloud
Secrets / RBACLock, KeyRound, UserCheck
ObservabilityBarChart3, Activity, Eye
How It Works stepsLink, Zap, CheckCircle
Pricing checkmarksCheck (success color)
Footer socialGithub, Twitter, MessageCircle
Theme toggleSun, Moon

8. SPA Section Structure

8.1 Page Skeleton

┌─────────────────────────────────────────────────────┐
│ [Fixed Nav]                                         │
│ Logo  │  Features  How It Works  Stack  Pricing  │ Get Started │
├─────────────────────────────────────────────────────┤
│                                                     │
│ [Hero]  id="hero"                    bg: --ec-bg    │
│   Overline: "OPEN SOURCE APP HOSTING"               │
│   H1: "Deploy with the precision                    │
│        of espresso."                                │
│   Subtitle: One-paragraph value prop                │
│   CTAs: "Get Started" + "View on GitHub"            │
│   Stats: 99.9% Uptime │ Your Cloud │ Open Source    │
│   ✦ Ambient steam effect in background              │
│                                                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│ [Features]  id="features"         bg: --ec-bg-alt   │
│   Overline: "CAPABILITIES"                          │
│   H2: "Everything your team needs to ship."         │
│   3×2 grid of feature cards                         │
│                                                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│ [How It Works]  id="how-it-works"    bg: --ec-bg    │
│   Overline: "HOW IT WORKS"                          │
│   H2: "Three steps. Zero friction."                 │
│   Horizontal 3-step flow:                           │
│   Connect Repo → Configure → Deploy                 │
│                                                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│ [Supported Tech]  id="stack"      bg: --ec-bg-alt   │
│   Overline: "ECOSYSTEM"                             │
│   H2: "Built for the frameworks you love."          │
│   Cloud providers: AWS, GCP, Azure                  │
│   Frameworks: Frappe, ERPNext, React, Vue, etc.     │
│                                                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│ [Pricing]  id="pricing"              bg: --ec-bg    │
│   Overline: "PRICING"                               │
│   H2: "Transparent, predictable pricing."           │
│   2–3 pricing cards side by side                    │
│   Featured card with copper border                  │
│   Price numbers in JetBrains Mono                   │
│                                                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│ [CTA / Footer]  id="get-started"                    │
│   bg: --ec-bg-inverse (Deep Espresso)               │
│   H2: "Ready to brew your first deployment?"        │
│   CTA button (inverted)                             │
│   Footer links, copyright, social icons             │
│                                                     │
└─────────────────────────────────────────────────────┘

8.2 Section Background Rhythm

Alternating backgrounds create visual separation without hard dividers:

SectionBackgroundText
Hero--ec-bg (Foam White)--ec-text-1
Features--ec-bg-alt (Parchment)--ec-text-1
How It Works--ec-bg (Foam White)--ec-text-1
Supported Tech--ec-bg-alt (Parchment)--ec-text-1
Pricing--ec-bg (Foam White)--ec-text-1
CTA / Footer--ec-bg-inverse (Deep Espresso)--ec-text-inverse

8.3 Smooth Scrolling

Configured in docs/.vitepress/theme/index.ts — routes with hash anchors smooth-scroll automatically. Global scroll-behavior: smooth and scroll-padding-top: 88px set in style.css.


9. Responsive Breakpoints

9.1 Breakpoint Scale (Mobile-First)

Defined in tailwind.config.jstheme.screens:

Tailwind prefixWidthDescription
sm:640pxLarge phone / small tablet
md:768pxTablet portrait
lg:960pxVitePress sidebar breakpoint
xl:1200pxDesktop
2xl:1400pxWide desktop

Example — responsive hero title:

html
<h1 class="font-display text-5xl md:text-6xl xl:text-7xl tracking-tighter leading-none">
  Deploy with the precision of espresso.
</h1>

9.2 Responsive Behavior

ElementMobile (<640px)Tablet (640–960px)Desktop (>960px)
NavHamburger menuHamburger menuHorizontal links
Hero title48px60px72px
Section title36px42px48px
Feature grid1 column2 columns3 columns
How It WorksVertical stackVertical stackHorizontal 3-step
Pricing cards1 column2 columns3 columns
Section padding64px block96px block128px block
Container gutter16px24px24px
Nav height64px64px72px

9.3 Mobile Navigation

On mobile/tablet (< lg:), the nav links collapse into a slide-out panel:

html
<!-- Mobile nav overlay (hidden by default, shown via Vue ref) -->
<div class="fixed inset-y-0 right-0 w-70 bg-white flex flex-col
  pt-20 px-8 gap-6 shadow-ec-xl z-[200]
  translate-x-full transition-transform duration-slow ease-ec-out
  lg:hidden"
  :class="{ 'translate-x-0': menuOpen }">
  <a class="font-heading text-lg">Features</a>
  <!-- ... -->
</div>

10. Implementation Reference

10.1 File Structure

espresso_webfe/
  tailwind.config.js        ← All design tokens (colors, fonts, spacing, animations)
  postcss.config.js         ← Tailwind + Autoprefixer
  docs/.vitepress/
    config.mts              ← VitePress config: font preloads, nav, meta
    theme/
      index.ts              ← Custom theme extending DefaultTheme
      style.css             ← Tailwind import + VitePress overrides + scroll reveal CSS
      components/
        EcNav.vue           ← Fixed top navigation
        EcHero.vue          ← Hero section with steam effect
        EcFeatures.vue      ← Feature cards grid
        EcHowItWorks.vue    ← 3-step flow
        EcTechStack.vue     ← Supported frameworks/cloud providers
        EcPricing.vue       ← Pricing cards
        EcFooter.vue        ← CTA + footer
      composables/
        useScrollReveal.ts  ← Intersection Observer for scroll animations
        useActiveSection.ts ← Track active nav section on scroll

10.2 Key Config Files (already created)

  • tailwind.config.js — All design tokens as Tailwind theme extensions
  • postcss.config.js@tailwindcss/postcss + autoprefixer
  • docs/.vitepress/theme/style.css — Tailwind import, Google Fonts, VitePress variable overrides, scroll reveal system, steam effect, stagger utilities
  • docs/.vitepress/theme/index.ts — Custom theme entry, smooth scroll routing
  • docs/.vitepress/config.mts — Font preconnect/preload, site meta

10.3 Development

bash
npm run docs:dev      # Start dev server with Tailwind + VitePress
npm run docs:build    # Production build (Tailwind auto-purges unused classes)
npm run docs:preview  # Preview production build

10.4 Accessibility Checklist

  • All text color combinations pass WCAG AA minimum (see Section 2.6)
  • Interactive elements have visible :focus-visible with warm copper glow
  • All animations respect @media (prefers-reduced-motion: reduce)
  • Semantic <nav> with aria-label for navigation
  • Proper heading hierarchy: single H1 in hero, H2 per section
  • scroll-padding-top accounts for fixed nav height
  • tabular-nums on pricing for aligned number columns