E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s
273 lines
8.6 KiB
Markdown
273 lines
8.6 KiB
Markdown
# WorkRoot Component Library
|
|
|
|
A reference for all reusable UI components. Every component listed here fully supports dark/light theme switching via Tailwind's `dark:` variant prefix — the `dark` class is toggled on `<html>` by `ThemeToggle.astro`.
|
|
|
|
---
|
|
|
|
## Theme Architecture
|
|
|
|
| Mechanism | Detail |
|
|
|-----------|--------|
|
|
| Strategy | Tailwind `class` dark mode (`darkMode: 'class'` in `tailwind.config.mjs`) |
|
|
| Toggle | `<ThemeToggle />` writes `dark` / `light` to `localStorage` and toggles `html.dark` |
|
|
| FOUC prevention | Inline `<script is:inline>` in `BaseLayout.astro` `<head>` reads `localStorage` synchronously before CSS renders |
|
|
| CSS tokens | `src/styles/design-tokens.css` + legacy aliases in `global.css` under `html.dark {}` |
|
|
|
|
---
|
|
|
|
## Components
|
|
|
|
### Badge
|
|
|
|
**File:** `src/components/ui/Badge.astro`
|
|
|
|
Eyebrow labels used above section headings.
|
|
|
|
```astro
|
|
<Badge label="Why Choose Us" variant="primary" />
|
|
<Badge label="New Feature" variant="accent" />
|
|
<Badge label="Category" variant="secondary" />
|
|
```
|
|
|
|
| Variant | Light | Dark |
|
|
|---------|-------|------|
|
|
| `primary` (default) | `bg-primary-50 text-primary-700` | `bg-primary-900/40 text-primary-300` |
|
|
| `primary-dark` | `bg-primary-400/20 text-primary-300` | same (designed for dark sections) |
|
|
| `accent` | `bg-accent-50 text-accent-700` | `bg-accent-900/40 text-accent-300` |
|
|
| `secondary` | `bg-secondary-100 text-secondary-700` | `bg-secondary-800 text-secondary-300` |
|
|
|
|
> **Usage note:** Use `primary` or `accent` on light/surface sections. Use `primary-dark` on hero/CTA sections that have dark gradient backgrounds (these sections are dark in both themes).
|
|
|
|
---
|
|
|
|
### Card
|
|
|
|
**File:** `src/components/ui/Card.astro`
|
|
|
|
Versatile content container with predefined style variants.
|
|
|
|
```astro
|
|
<Card variant="feature">...</Card>
|
|
<Card variant="service" padding="p-8">...</Card>
|
|
<Card variant="surface">...</Card>
|
|
<Card variant="dark">...</Card> <!-- for use on dark gradient sections -->
|
|
<Card variant="plain">...</Card> <!-- no styles applied -->
|
|
```
|
|
|
|
| Variant | Light | Dark |
|
|
|---------|-------|------|
|
|
| `feature` | `from-secondary-50 to-white`, border `secondary-100` | `from-secondary-800 to-secondary-900`, border `secondary-700` |
|
|
| `service` | `bg-white`, border `secondary-100` | `bg-secondary-800`, border `secondary-700` |
|
|
| `surface` | `bg-secondary-50`, border `secondary-200` | `bg-secondary-800`, border `secondary-700` |
|
|
| `dark` | `bg-white/5` glassmorphism (static) | same — designed for dark gradient backgrounds only |
|
|
|
|
---
|
|
|
|
### SectionHeader
|
|
|
|
**File:** `src/components/ui/SectionHeader.astro`
|
|
|
|
Standard eyebrow + heading + description pattern used in every section.
|
|
|
|
```astro
|
|
<!-- On light/surface sections -->
|
|
<SectionHeader
|
|
badge="Our Services"
|
|
title="What We Build"
|
|
description="From concept to production."
|
|
theme="light"
|
|
badgeColor="primary"
|
|
/>
|
|
|
|
<!-- On dark/hero sections -->
|
|
<SectionHeader
|
|
badge="How It Works"
|
|
title="Simple Process"
|
|
theme="dark"
|
|
badgeColor="accent"
|
|
/>
|
|
```
|
|
|
|
| Prop | Type | Default | Notes |
|
|
|------|------|---------|-------|
|
|
| `badge` | `string` | required | Eyebrow label text |
|
|
| `title` | `string` | required | `<h2>` heading |
|
|
| `description` | `string` | — | Optional paragraph below heading |
|
|
| `theme` | `'light' \| 'dark'` | `'light'` | Controls text/badge colors |
|
|
| `badgeColor` | `'primary' \| 'accent'` | `'primary'` | Badge hue |
|
|
| `maxWidth` | `string` | `'max-w-3xl'` | Tailwind width class |
|
|
|
|
**Theme behavior:**
|
|
|
|
- `theme="light"` — heading `text-secondary-900 dark:text-secondary-50`, description `text-secondary-600 dark:text-secondary-400`
|
|
- `theme="dark"` — heading `text-white`, description `text-secondary-300` (used on dark gradient backgrounds that look the same in both themes)
|
|
|
|
---
|
|
|
|
### ThemeToggle
|
|
|
|
**File:** `src/components/ThemeToggle.astro`
|
|
|
|
Sun/moon icon button that toggles between dark and light mode.
|
|
|
|
```astro
|
|
<ThemeToggle />
|
|
```
|
|
|
|
- Persists preference in `localStorage` under key `theme` (`'dark'` | `'light'`)
|
|
- Falls back to `prefers-color-scheme` when no stored preference
|
|
- Updates `meta[name="theme-color"]` for browser chrome color
|
|
- Fully keyboard accessible — ARIA label updates dynamically
|
|
- Respects `prefers-reduced-motion`
|
|
|
|
---
|
|
|
|
### Header
|
|
|
|
**File:** `src/components/Header.astro`
|
|
|
|
Fixed top navigation with desktop menu, mobile slide-out panel, and theme toggle.
|
|
|
|
```astro
|
|
<Header /> <!-- used in BaseLayout, no props needed -->
|
|
```
|
|
|
|
**Dark mode classes:**
|
|
|
|
| Element | Light | Dark |
|
|
|---------|-------|------|
|
|
| Header bar | `bg-white/95 border-secondary-100` | `bg-secondary-900/95 border-secondary-800` |
|
|
| Active nav link | `text-primary bg-primary-50` | `text-primary-400 bg-primary-900/30` |
|
|
| Inactive nav link hover | `hover:bg-secondary-50` | `dark:hover:bg-secondary-800` |
|
|
| Mobile panel | `bg-white` | `bg-secondary-900` |
|
|
| Mobile nav items | same pattern as desktop | same dark variants |
|
|
|
|
---
|
|
|
|
### Footer
|
|
|
|
**File:** `src/components/Footer.astro`
|
|
|
|
Always dark (`bg-secondary-900`) — no light/dark switching needed as the footer is intentionally dark in both modes.
|
|
|
|
---
|
|
|
|
### LazyImage
|
|
|
|
**File:** `src/components/LazyImage.astro`
|
|
|
|
Lightweight image wrapper with native lazy loading and fade-in animation.
|
|
|
|
```astro
|
|
<LazyImage
|
|
src="/images/hero.jpg"
|
|
alt="Team working"
|
|
width={1200}
|
|
height={630}
|
|
loading="eager"
|
|
fetchpriority="high"
|
|
/>
|
|
```
|
|
|
|
| Prop | Default | Notes |
|
|
|------|---------|-------|
|
|
| `loading` | `'lazy'` | Use `'eager'` for above-the-fold images |
|
|
| `fetchpriority` | `'auto'` | Use `'high'` for LCP images |
|
|
| `placeholder` | `var(--color-surface-alt)` | Adapts to theme via CSS custom property |
|
|
|
|
The placeholder color uses `--color-surface-alt` from `global.css`, which is `#f1f5f9` in light mode and `#1e293b` in dark mode.
|
|
|
|
---
|
|
|
|
## Global CSS Component Classes
|
|
|
|
Classes defined in `src/styles/global.css` under `@layer components`. All support dark mode via `dark:` variants.
|
|
|
|
### Buttons
|
|
|
|
```html
|
|
<button class="btn-primary">Primary Action</button>
|
|
<button class="btn-primary-lg">Large CTA</button>
|
|
<button class="btn-secondary">Secondary</button>
|
|
<button class="btn-accent">Accent</button>
|
|
<button class="btn-ghost">Ghost (for dark sections)</button>
|
|
<button class="btn-inverse-lg">Inverse Large (for dark sections)</button>
|
|
```
|
|
|
|
| Class | Theme behavior |
|
|
|-------|---------------|
|
|
| `btn-primary` | Cyan background — same in both modes (brand color) |
|
|
| `btn-secondary` | `border-secondary` in light / `border-secondary-400 text-secondary-300` in dark |
|
|
| `btn-ghost` | `bg-white/5` — designed for dark sections only |
|
|
|
|
### Badges (global classes)
|
|
|
|
```html
|
|
<span class="badge-primary">Label</span>
|
|
<span class="badge-accent">Label</span>
|
|
<span class="badge-secondary">Label</span>
|
|
<span class="badge-primary-dark">Label</span>
|
|
```
|
|
|
|
### Cards (global classes)
|
|
|
|
```html
|
|
<div class="card">...</div>
|
|
<div class="card-hover">...</div>
|
|
<div class="card-service">...</div>
|
|
<div class="card-surface">...</div>
|
|
```
|
|
|
|
### Forms
|
|
|
|
```html
|
|
<label class="form-label">Email</label>
|
|
<input class="form-input" type="email" />
|
|
<p class="form-error">Required field</p>
|
|
```
|
|
|
|
| Class | Dark mode |
|
|
|-------|-----------|
|
|
| `form-input` | `bg-secondary-800 border-secondary-600 text-secondary-100` |
|
|
| `form-label` | `text-secondary-300` |
|
|
| `form-error` | `text-red-400` |
|
|
|
|
### Section Typography
|
|
|
|
```html
|
|
<h2 class="section-title">Heading</h2>
|
|
<p class="section-description">Supporting text</p>
|
|
|
|
<!-- On dark gradient sections -->
|
|
<h2 class="section-title-inverse">Heading</h2>
|
|
<p class="section-description-inverse">Supporting text</p>
|
|
```
|
|
|
|
---
|
|
|
|
## Design Tokens Quick Reference
|
|
|
|
Defined in `src/styles/design-tokens.css` and aliased in `global.css`.
|
|
|
|
| Token | Light value | Dark value |
|
|
|-------|-------------|------------|
|
|
| `--color-surface` | `#f8fafc` | `#0f172a` |
|
|
| `--color-surface-alt` | `#f1f5f9` | `#1e293b` |
|
|
| `--color-border` | `#e2e8f0` | `#334155` |
|
|
| `--color-text-primary` | `#1e293b` | `#f1f5f9` |
|
|
| `--color-text-secondary` | `#475569` | `#cbd5e1` |
|
|
| `--color-text-muted` | `#94a3b8` | `#94a3b8` |
|
|
| `--color-primary` | `#0891b2` (cyan-600) | `#22d3ee` (cyan-400) |
|
|
| `--color-accent` | `#f59e0b` (amber-500) | `#fbbf24` (amber-400) |
|
|
|
|
---
|
|
|
|
## Adding a New Component
|
|
|
|
1. Use `dark:` prefixed Tailwind classes for all color utilities
|
|
2. For surface backgrounds: `bg-white dark:bg-secondary-800` or `bg-secondary-50 dark:bg-secondary-800`
|
|
3. For borders: `border-secondary-100 dark:border-secondary-700`
|
|
4. For text: `text-secondary-900 dark:text-secondary-50` (headings), `text-secondary-600 dark:text-secondary-400` (body)
|
|
5. Do NOT use `html.dark` CSS overrides — prefer Tailwind `dark:` variants for colocation
|
|
6. Use CSS custom properties (`var(--color-surface)`) only when Tailwind classes won't work (e.g. dynamic inline styles)
|