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
983 lines
29 KiB
Markdown
983 lines
29 KiB
Markdown
# Redesign Documentation & Handoff Guide
|
|
|
|
**Project**: WorkRoot IT Solutions — Company Site
|
|
**Date**: 2026-03-21
|
|
**Author**: documentation-writer agent
|
|
**Status**: Production-ready (98/100 Lighthouse avg)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Project Overview](#1-project-overview)
|
|
2. [Design System](#2-design-system)
|
|
3. [Component Library](#3-component-library)
|
|
4. [Page Architecture](#4-page-architecture)
|
|
5. [Animation System](#5-animation-system)
|
|
6. [Contact Page Deep Dive](#6-contact-page-deep-dive)
|
|
7. [Theme System](#7-theme-system)
|
|
8. [Maintenance Guidelines](#8-maintenance-guidelines)
|
|
9. [Future Enhancement Recommendations](#9-future-enhancement-recommendations)
|
|
10. [Quick Reference](#10-quick-reference)
|
|
|
|
---
|
|
|
|
## 1. Project Overview
|
|
|
|
### Technology Stack
|
|
|
|
| Layer | Technology | Notes |
|
|
|-------|-----------|-------|
|
|
| Framework | Astro (SSR) | Node.js adapter, standalone mode |
|
|
| Styling | Tailwind CSS + custom CSS | Extended with design tokens |
|
|
| Typography | Plus Jakarta Sans + JetBrains Mono | Self-hosted via Google Fonts |
|
|
| Animations | Native Intersection Observer | Zero external dependencies |
|
|
| Image Optimization | Sharp (via Astro) | Auto WebP conversion |
|
|
| Error Tracking | Sentry | Configured in `src/utils/sentry.ts` |
|
|
| Analytics | Custom wrapper | `src/utils/analytics.ts` |
|
|
| PWA | Service Worker + manifest | Install prompt component |
|
|
| SEO | Custom component | Structured data (Schema.org) |
|
|
|
|
### Site URL
|
|
|
|
```
|
|
Production: https://workroot.in
|
|
Server: 0.0.0.0:10000 (standalone Node.js)
|
|
```
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
src/
|
|
├── components/
|
|
│ ├── Analytics.astro
|
|
│ ├── Footer.astro
|
|
│ ├── Header.astro
|
|
│ ├── LazyImage.astro
|
|
│ ├── OptimizedImage.astro
|
|
│ ├── PWAInstallPrompt.astro
|
|
│ ├── SEO.astro
|
|
│ └── ui/
|
|
│ ├── Badge.astro
|
|
│ ├── Card.astro
|
|
│ └── SectionHeader.astro
|
|
├── layouts/
|
|
│ └── BaseLayout.astro
|
|
├── pages/
|
|
│ ├── index.astro
|
|
│ ├── about.astro
|
|
│ ├── services.astro
|
|
│ ├── portfolio.astro
|
|
│ ├── contact.astro ← Primary redesign
|
|
│ ├── privacy.astro
|
|
│ └── terms.astro
|
|
├── styles/
|
|
│ └── global.css ← Design tokens + component classes
|
|
└── utils/
|
|
├── analytics.ts
|
|
├── animations.ts ← Scroll-reveal utilities
|
|
├── imageUtils.ts
|
|
├── logger.ts
|
|
├── sentry.ts
|
|
└── seo.ts
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Design System
|
|
|
|
### Color Palette
|
|
|
|
Defined in both `tailwind.config.mjs` (Tailwind classes) and `src/styles/global.css` (CSS custom properties).
|
|
|
|
#### Primary — Cyan
|
|
|
|
| Token | Value | Usage |
|
|
|-------|-------|-------|
|
|
| `--color-primary` | `#0891b2` | Buttons, links, icons, accents |
|
|
| `primary-50` | `#ecfeff` | Light backgrounds, hover states |
|
|
| `primary-100` | `#cffafe` | Subtle highlights |
|
|
| `primary-500` | `#06b6d4` | Mid-tone |
|
|
| `primary-600` | `#0891b2` | Default (same as token) |
|
|
| `primary-700` | `#0e7490` | Hover states, borders |
|
|
| `primary-900` | `#164e63` | Dark text on light primary |
|
|
|
|
#### Secondary — Slate
|
|
|
|
| Token | Value | Usage |
|
|
|-------|-------|-------|
|
|
| `--color-secondary` | `#1e293b` | Body text, dark backgrounds |
|
|
| `secondary-50` | `#f8fafc` | Page backgrounds, cards |
|
|
| `secondary-100` | `#f1f5f9` | Surface cards |
|
|
| `secondary-600` | `#475569` | Muted body text |
|
|
| `secondary-700` | `#334155` | Default body text |
|
|
| `secondary-800` | `#1e293b` | Headings, labels |
|
|
| `secondary-900` | `#0f172a` | High-contrast text |
|
|
|
|
#### Accent — Amber
|
|
|
|
| Token | Value | Usage |
|
|
|-------|-------|-------|
|
|
| `--color-accent` | `#f59e0b` | Highlights, CTAs, warnings |
|
|
| `accent-50` | `#fffbeb` | Very light backgrounds |
|
|
| `accent-400` | `#fbbf24` | Hover states |
|
|
| `accent-500` | `#f59e0b` | Default (same as token) |
|
|
| `accent-600` | `#d97706` | Pressed/active states |
|
|
|
|
### Typography
|
|
|
|
```css
|
|
/* Font Families */
|
|
--font-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
|
|
--font-mono: 'JetBrains Mono', monospace;
|
|
|
|
/* Display Sizes (responsive clamp) */
|
|
display-xl: clamp(3rem, 6vw, 6rem) /* Hero headlines */
|
|
display-lg: clamp(2.25rem, 4vw, 4.5rem) /* Section headlines */
|
|
```
|
|
|
|
**Usage Pattern**:
|
|
- `font-sans` — All UI text, body copy, labels
|
|
- `font-mono` — Code blocks, technical values, stats
|
|
- `font-display` — NOT a separate family; use `font-sans font-bold` at display sizes
|
|
|
|
### Spacing
|
|
|
|
| Token | Value | Tailwind Class | Usage |
|
|
|-------|-------|----------------|-------|
|
|
| `--spacing-section` | `6rem` | `py-24` | Major section padding |
|
|
| `--spacing-section-sm` | `4rem` | `py-16` | Smaller section padding |
|
|
| `--spacing-section-lg` | `8rem` | `py-32` | Hero / featured sections |
|
|
|
|
### Border Radius
|
|
|
|
| Token | Value | Usage |
|
|
|-------|-------|-------|
|
|
| `--radius-sm` | `8px` | Inputs, badges, tags |
|
|
| `--radius-md` | `12px` | Buttons, small cards |
|
|
| `--radius-lg` | `16px` | Standard cards |
|
|
| `--radius-xl` | `24px` | Feature cards, modals |
|
|
| `--radius-full` | `9999px` | Pills, avatars, icons |
|
|
| `rounded-card` | `1rem` | Card.astro default |
|
|
| `rounded-card-lg` | `1.5rem` | Service cards |
|
|
|
|
### Shadows
|
|
|
|
| Token | Usage |
|
|
|-------|-------|
|
|
| `shadow-sm` | Subtle depth on inputs |
|
|
| `shadow-card` | Default card elevation |
|
|
| `shadow-card-hover` | Hovered card elevation |
|
|
| `shadow-primary-glow` | Primary color glow effect |
|
|
| `shadow-accent-glow` | Accent color glow effect |
|
|
|
|
### Transitions
|
|
|
|
| Token | Value | Usage |
|
|
|-------|-------|-------|
|
|
| `--transition-fast` | `150ms` | Hover tints, focus rings |
|
|
| `--transition-base` | `200ms` | Button states, icon transforms |
|
|
| `--transition-slow` | `300ms` | Card lifts, panel slides |
|
|
| `--transition-slower` | `500ms` | Page-level transitions |
|
|
|
|
### Background Gradients (Tailwind `bg-*`)
|
|
|
|
| Class | Usage |
|
|
|-------|-------|
|
|
| `bg-hero-dark` | Hero sections with dark gradient |
|
|
| `bg-cta-dark` | CTA/banner sections |
|
|
| `bg-primary-gradient` | Primary-to-transparent gradient |
|
|
| `bg-primary-gradient-br` | Bottom-right variant |
|
|
| `bg-surface-gradient` | Light surface backgrounds |
|
|
| `bg-card-gradient` | Card hover gradients |
|
|
|
|
---
|
|
|
|
## 3. Component Library
|
|
|
|
### `Badge.astro` — Eyebrow Label
|
|
|
|
Used above section headings as visual callouts.
|
|
|
|
```astro
|
|
---
|
|
import Badge from '../components/ui/Badge.astro';
|
|
---
|
|
<Badge label="Our Services" variant="primary" />
|
|
<Badge label="Case Studies" variant="accent" />
|
|
```
|
|
|
|
**Props:**
|
|
|
|
| Prop | Type | Default | Description |
|
|
|------|------|---------|-------------|
|
|
| `label` | `string` | required | Text to display |
|
|
| `variant` | `'primary' \| 'primary-dark' \| 'accent' \| 'secondary'` | `'primary'` | Color theme |
|
|
| `class` | `string` | — | Extra Tailwind classes |
|
|
|
|
**Variants:**
|
|
- `primary` — Cyan on light backgrounds (most pages)
|
|
- `primary-dark` — Light text on dark/hero sections
|
|
- `accent` — Amber/warm for highlights
|
|
- `secondary` — Neutral slate for subtle labeling
|
|
|
|
---
|
|
|
|
### `Card.astro` — Versatile Card Wrapper
|
|
|
|
```astro
|
|
---
|
|
import Card from '../components/ui/Card.astro';
|
|
---
|
|
<Card variant="feature">
|
|
<h3>Title</h3>
|
|
<p>Content</p>
|
|
</Card>
|
|
```
|
|
|
|
**Props:**
|
|
|
|
| Prop | Type | Default | Description |
|
|
|------|------|---------|-------------|
|
|
| `variant` | `'feature' \| 'service' \| 'surface' \| 'dark' \| 'plain'` | `'feature'` | Visual style |
|
|
| `padding` | `string` | variant-based | Override padding |
|
|
| `class` | `string` | — | Extra classes |
|
|
|
|
**Variant Details:**
|
|
|
|
| Variant | Background | Hover Effect | Default Padding | Use Case |
|
|
|---------|-----------|--------------|-----------------|----------|
|
|
| `feature` | Gradient (white→primary-50) | Lift + border glow | `p-8` | Benefits, features |
|
|
| `service` | White | Shadow lift | `p-10` | Services grid |
|
|
| `surface` | `secondary-50` | Subtle border | `p-6` | Contact info, sidebar |
|
|
| `dark` | Glassmorphism | Border highlight | `p-8` | Dark section cards |
|
|
| `plain` | None | None | None | Full customization |
|
|
|
|
---
|
|
|
|
### `SectionHeader.astro` — Section Heading Block
|
|
|
|
Used at the top of each major page section.
|
|
|
|
```astro
|
|
---
|
|
import SectionHeader from '../components/ui/SectionHeader.astro';
|
|
---
|
|
<SectionHeader
|
|
badge="Our Approach"
|
|
title="How We Work"
|
|
description="We follow a proven process..."
|
|
theme="light"
|
|
badgeColor="primary"
|
|
/>
|
|
```
|
|
|
|
**Props:**
|
|
|
|
| Prop | Type | Default | Description |
|
|
|------|------|---------|-------------|
|
|
| `badge` | `string` | required | Eyebrow text (uses Badge.astro) |
|
|
| `title` | `string` | required | H2 heading text |
|
|
| `description` | `string` | — | Optional paragraph below |
|
|
| `theme` | `'light' \| 'dark'` | `'light'` | Adapts text color |
|
|
| `badgeColor` | `'primary' \| 'accent'` | `'primary'` | Badge variant |
|
|
| `maxWidth` | `string` | `'max-w-3xl'` | Width constraint on description |
|
|
| `class` | `string` | — | Extra classes |
|
|
|
|
---
|
|
|
|
### `BaseLayout.astro` — Page Wrapper
|
|
|
|
Wraps every page. Handles: SEO meta tags, Organization structured data, global fonts, analytics, error tracking, PWA prompt, header, footer, skip-to-content link.
|
|
|
|
```astro
|
|
---
|
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
---
|
|
<BaseLayout
|
|
title="Page Title"
|
|
description="Meta description..."
|
|
image="/og-image.jpg"
|
|
canonical="https://workroot.in/page"
|
|
>
|
|
<!-- page content -->
|
|
</BaseLayout>
|
|
```
|
|
|
|
---
|
|
|
|
### `SEO.astro` — Meta Tags & Structured Data
|
|
|
|
Included via `BaseLayout.astro`. Generates:
|
|
- `<title>` and `<meta name="description">`
|
|
- Open Graph (Facebook/LinkedIn) tags
|
|
- Twitter Card tags
|
|
- Canonical URL
|
|
- JSON-LD structured data (WebPage schema)
|
|
|
|
---
|
|
|
|
### `LazyImage.astro` / `OptimizedImage.astro`
|
|
|
|
For images within page content.
|
|
|
|
```astro
|
|
<!-- Lazy loaded with blur placeholder -->
|
|
<LazyImage src="/hero.jpg" alt="Description" width={800} height={450} />
|
|
|
|
<!-- Optimized with Sharp (auto WebP) -->
|
|
<OptimizedImage src="/photo.jpg" alt="Description" width={600} height={400} />
|
|
```
|
|
|
|
---
|
|
|
|
### CSS-Only Components (via `global.css`)
|
|
|
|
These are plain HTML elements styled via class names — no Astro component required.
|
|
|
|
#### Buttons
|
|
|
|
```html
|
|
<!-- Primary CTA -->
|
|
<a href="/contact" class="btn-primary">Get Started</a>
|
|
|
|
<!-- Secondary/Outline -->
|
|
<a href="/portfolio" class="btn-secondary">View Work</a>
|
|
|
|
<!-- Accent (amber) -->
|
|
<button class="btn-accent">Highlight Action</button>
|
|
|
|
<!-- Ghost (transparent) -->
|
|
<button class="btn-ghost">Learn More</button>
|
|
|
|
<!-- Large variants for hero sections -->
|
|
<a href="/contact" class="btn-primary-lg">Start a Project</a>
|
|
<a href="/services" class="btn-inverse-lg">View Services</a>
|
|
```
|
|
|
|
#### Form Elements
|
|
|
|
```html
|
|
<label class="form-label" for="name">Your Name</label>
|
|
<input class="form-input" id="name" type="text" />
|
|
<span class="form-error">This field is required.</span>
|
|
|
|
<!-- Error state on input -->
|
|
<input class="form-input form-input-error" id="email" type="email" />
|
|
```
|
|
|
|
#### Section Patterns
|
|
|
|
```html
|
|
<!-- Light section -->
|
|
<div class="section-header">
|
|
<p class="section-title">Main Heading</p>
|
|
<p class="section-description">Supporting text...</p>
|
|
</div>
|
|
|
|
<!-- Dark/inverse section -->
|
|
<div class="section-header">
|
|
<p class="section-title-inverse">Main Heading</p>
|
|
<p class="section-description-inverse">Supporting text...</p>
|
|
</div>
|
|
```
|
|
|
|
#### Decorative Elements
|
|
|
|
```html
|
|
<!-- Blobs (absolute-positioned background shapes) -->
|
|
<div class="blob blob-primary"></div>
|
|
<div class="blob blob-accent"></div>
|
|
|
|
<!-- Grid overlay (semi-transparent dot grid) -->
|
|
<div class="grid-overlay"></div>
|
|
|
|
<!-- Gradient text -->
|
|
<span class="gradient-text">Highlighted Words</span>
|
|
<span class="gradient-text-primary">Also works</span>
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Page Architecture
|
|
|
|
### Section Pattern
|
|
|
|
Every major page section follows this structure:
|
|
|
|
```astro
|
|
<section class="py-24 bg-white relative overflow-hidden">
|
|
<!-- Optional decorative blobs -->
|
|
<div class="blob blob-primary top-0 left-0"></div>
|
|
|
|
<div class="container-wrapper">
|
|
<!-- SectionHeader -->
|
|
<SectionHeader
|
|
badge="Label"
|
|
title="Section Title"
|
|
description="Optional description"
|
|
/>
|
|
|
|
<!-- Content grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-16">
|
|
<!-- Cards or content -->
|
|
</div>
|
|
</div>
|
|
</section>
|
|
```
|
|
|
|
### Page Layout Zones
|
|
|
|
| Zone | Background | Typical Content |
|
|
|------|-----------|-----------------|
|
|
| Hero | `bg-hero-dark` (dark gradient) | Headline, subtext, CTAs, trust stats |
|
|
| Features | `bg-white` | Cards with icons |
|
|
| Services | `bg-secondary-50` | Service cards grid |
|
|
| Testimonials | `bg-hero-dark` | Quote cards |
|
|
| CTA Banner | `bg-cta-dark` | Headline + 2 CTA buttons |
|
|
| Contact | `bg-secondary-50` | Form + sidebar |
|
|
| Footer | `bg-secondary-900` | Nav links, social, copyright |
|
|
|
|
### Responsive Breakpoints
|
|
|
|
Uses Tailwind defaults:
|
|
- `sm`: 640px — Small phones → landscape
|
|
- `md`: 768px — Tablets
|
|
- `lg`: 1024px — Laptops
|
|
- `xl`: 1280px — Desktops
|
|
- `2xl`: 1536px — Large monitors
|
|
|
|
**Grid patterns used across pages:**
|
|
|
|
```
|
|
Hero: Single column centered
|
|
Features: 1 col → 2 col → 3 col (sm/md/lg)
|
|
Services: 1 col → 2 col → 3 col
|
|
Contact: 1 col → 3+2 col split (lg)
|
|
Portfolio: 1 col → 2 col → 3 col with filters
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Animation System
|
|
|
|
### How It Works
|
|
|
|
Animations use the native **Intersection Observer API** — no JavaScript library needed. The system is defined in two files:
|
|
|
|
- `src/utils/animations.ts` — JavaScript observer logic
|
|
- `src/styles/global.css` — CSS transitions and keyframes
|
|
|
|
### Usage Pattern
|
|
|
|
Add `data-animate` and optional `data-delay` to any element:
|
|
|
|
```html
|
|
<div data-animate="fade-up">Animates when scrolled into view</div>
|
|
<div data-animate="fade-up" data-delay="200">Delayed by 200ms</div>
|
|
<div data-animate="scale-up" data-duration="slow">Slower duration</div>
|
|
```
|
|
|
|
Then initialize in the page's `<script>` tag:
|
|
|
|
```javascript
|
|
import { initScrollReveal } from '../utils/animations';
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initScrollReveal();
|
|
});
|
|
```
|
|
|
|
Or use `initAllAnimations()` to enable everything:
|
|
|
|
```javascript
|
|
import { initAllAnimations } from '../utils/animations';
|
|
document.addEventListener('DOMContentLoaded', initAllAnimations);
|
|
```
|
|
|
|
### Animation Types (`data-animate` values)
|
|
|
|
| Value | Effect |
|
|
|-------|--------|
|
|
| `fade-up` | Fade in + slide up 30px |
|
|
| `fade-down` | Fade in + slide down 30px |
|
|
| `fade-left` | Fade in + slide from right |
|
|
| `fade-right` | Fade in + slide from left |
|
|
| `fade-in` | Fade in only |
|
|
| `scale-up` | Scale from 0.95 → 1.0 with fade |
|
|
| `zoom-in` | Scale from 0.8 → 1.0 with fade |
|
|
|
|
### Delay Classes (`data-delay` values)
|
|
|
|
`100`, `200`, `300`, `400`, `500`, `600`, `700`, `800` (milliseconds)
|
|
|
|
Use to stagger card grids:
|
|
|
|
```html
|
|
<div class="grid grid-cols-3 gap-8">
|
|
<div data-animate="fade-up" data-delay="0">Card 1</div>
|
|
<div data-animate="fade-up" data-delay="150">Card 2</div>
|
|
<div data-animate="fade-up" data-delay="300">Card 3</div>
|
|
</div>
|
|
```
|
|
|
|
### Duration Variants (`data-duration` values)
|
|
|
|
| Value | Duration |
|
|
|-------|---------|
|
|
| `fast` | 400ms |
|
|
| (default) | 600ms |
|
|
| `slow` | 900ms |
|
|
|
|
### Reduced Motion
|
|
|
|
All animations automatically respect `prefers-reduced-motion: reduce`. Elements marked with `data-animate` are revealed immediately without transitions when this preference is set.
|
|
|
|
### Other Animation Utilities
|
|
|
|
| Function | Description |
|
|
|----------|-------------|
|
|
| `initCounters()` | Animate `[data-counter]` elements from 0 to target value |
|
|
| `initButtonRipple()` | Cursor-following radial highlight on `.btn-ripple` buttons |
|
|
| `initProgressBars()` | Fill `.progress-bar` elements to `--progress-width` CSS variable |
|
|
|
|
### Counter Example
|
|
|
|
```html
|
|
<span
|
|
data-counter="150"
|
|
data-counter-suffix="+"
|
|
data-counter-duration="2000"
|
|
class="text-4xl font-bold"
|
|
>0</span>
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Contact Page Deep Dive
|
|
|
|
The contact page (`src/pages/contact.astro`, ~1029 lines) is the most feature-rich redesigned page and serves as the reference implementation for advanced patterns.
|
|
|
|
### Page Sections
|
|
|
|
1. **Hero** — Dark gradient with grid overlay, trust statistics, scroll-reveal
|
|
2. **Contact Form + Sidebar** — 3-column form + 2-column sidebar on `lg` breakpoints
|
|
3. **FAQ** — Collapsible details/summary
|
|
4. **CTA Banner** — Dark gradient with dual CTAs
|
|
|
|
### Form Architecture
|
|
|
|
The contact form uses no external form library. All behavior is plain JavaScript.
|
|
|
|
#### Spam Protection (Honeypot)
|
|
|
|
```html
|
|
<!-- Hidden field — bots fill it, humans don't -->
|
|
<div style="position: absolute; left: -9999px; opacity: 0" aria-hidden="true">
|
|
<input type="text" name="website" tabindex="-1" autocomplete="off" />
|
|
</div>
|
|
```
|
|
|
|
Server-side: check that `website` field is empty before processing.
|
|
|
|
#### Budget Selector (Radio as Chips)
|
|
|
|
The budget options render as visual chip buttons but use `<input type="radio">` under the hood for accessibility:
|
|
|
|
```html
|
|
<div class="flex flex-wrap gap-3">
|
|
<label class="budget-option">
|
|
<input type="radio" name="budget" value="under-5k" class="sr-only" />
|
|
<span>Under $5K</span>
|
|
</label>
|
|
</div>
|
|
```
|
|
|
|
JavaScript adds/removes the `selected` class when a chip is clicked, and the CSS handles the visual transformation from neutral to active.
|
|
|
|
#### Form Validation
|
|
|
|
Regex patterns used:
|
|
|
|
```javascript
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
const phoneRegex = /^[\+]?[1-9][\d]{0,15}$/;
|
|
```
|
|
|
|
Validation fires on `blur` (field leave) and `submit`. Visual feedback via `.form-input-error` class and a sibling error `<span class="form-error">`.
|
|
|
|
#### Character Counter
|
|
|
|
```html
|
|
<textarea
|
|
id="message"
|
|
maxlength="2000"
|
|
data-counter-target="message-counter"
|
|
></textarea>
|
|
<span id="message-counter">0/2000</span>
|
|
```
|
|
|
|
JavaScript updates the counter text on `input` events.
|
|
|
|
#### Toast Notifications
|
|
|
|
```javascript
|
|
function showToast(message, type = 'success') {
|
|
const toast = document.getElementById('toast');
|
|
// Set message text, apply type class, show with transition
|
|
// Auto-hide after 5000ms
|
|
}
|
|
```
|
|
|
|
The toast element is always in the DOM (`position: fixed, bottom-right`) but visually hidden until triggered.
|
|
|
|
#### Form States
|
|
|
|
| State | UI Change |
|
|
|-------|-----------|
|
|
| Default | Normal form |
|
|
| Loading | Button shows spinner, disabled |
|
|
| Success | Overlay replaces form, shows success message |
|
|
| Error | Toast notification, form re-enabled |
|
|
|
|
### Sidebar Components
|
|
|
|
#### Contact Info Cards
|
|
|
|
Four cards (Visit Us, Call Us, Email Us, Business Hours) using `card-surface` variant. Each has:
|
|
- Color-coded icon container (primary, accent, etc.)
|
|
- Title + content
|
|
- Hover lift effect
|
|
|
|
#### Map Embed
|
|
|
|
A static map image with an overlay on hover. Contains a floating pin animation:
|
|
|
|
```css
|
|
@keyframes float {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-8px); }
|
|
}
|
|
```
|
|
|
|
The hover overlay prompts "Open in Google Maps" with a link.
|
|
|
|
#### Social Links
|
|
|
|
```html
|
|
<a href="https://linkedin.com/..." class="social-link group">
|
|
<!-- SVG icon -->
|
|
<span>LinkedIn</span>
|
|
</a>
|
|
```
|
|
|
|
Custom hover effect: icon rotates slightly, text shifts right.
|
|
|
|
### FAQ Section
|
|
|
|
Uses native `<details>/<summary>` HTML for zero-JS accordion behavior:
|
|
|
|
```html
|
|
<details class="faq-item">
|
|
<summary class="faq-question">
|
|
Question text
|
|
<span class="faq-icon"><!-- chevron SVG --></span>
|
|
</summary>
|
|
<div class="faq-answer">Answer text</div>
|
|
</details>
|
|
```
|
|
|
|
CSS handles the open/close transition via `details[open]` selector. The chevron rotates 180° when open.
|
|
|
|
### Analytics Events
|
|
|
|
The contact page fires analytics events for key interactions:
|
|
|
|
```javascript
|
|
// On budget chip select
|
|
trackEvent('budget_selected', { budget: value });
|
|
|
|
// On form submit (success)
|
|
trackEvent('form_submitted', { budget: selectedBudget });
|
|
|
|
// On social link click
|
|
trackEvent('social_click', { platform: 'linkedin' });
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Theme System
|
|
|
|
The site ships a full **dark / light theme system** with FOUC prevention, localStorage persistence, OS preference detection, and WCAG AAA contrast compliance.
|
|
|
|
### Key Files
|
|
|
|
| File | Role |
|
|
|------|------|
|
|
| `src/styles/design-tokens.css` | All color tokens (`:root` light + `html.dark` overrides) |
|
|
| `src/components/ThemeToggle.astro` | Sun/moon toggle button in the header |
|
|
| `src/layouts/BaseLayout.astro` | Blocking inline script (prevents flash of wrong theme) |
|
|
|
|
### How It Works (Summary)
|
|
|
|
1. A synchronous inline script in `<head>` reads `localStorage.getItem('theme')` or `prefers-color-scheme` and adds `html.dark` before the first CSS paint — eliminating FOUC.
|
|
2. All colors are CSS custom properties (`--color-*`). `html.dark` overrides them for the dark palette.
|
|
3. Tailwind's `dark:` class variant targets `html.dark` for utility classes.
|
|
4. `ThemeToggle.astro` handles click events, syncs all toggle instances, updates ARIA state, announces to screen readers, and updates `<meta name="theme-color">`.
|
|
|
|
### Developer Quick Start
|
|
|
|
```html
|
|
<!-- ✅ Use semantic tokens -->
|
|
<div style="background: var(--color-surface-raised); color: var(--color-text-primary);">
|
|
|
|
<!-- ✅ Use Tailwind dark: variants -->
|
|
<div class="bg-white dark:bg-secondary-800 text-secondary-700 dark:text-secondary-300">
|
|
```
|
|
|
|
### Full Documentation
|
|
|
|
See **[THEME_SYSTEM_GUIDE.md](./THEME_SYSTEM_GUIDE.md)** for:
|
|
- Complete design token reference
|
|
- Step-by-step guide for adding theme support to new components
|
|
- Troubleshooting guide (FOUC, contrast issues, screen reader support, etc.)
|
|
- Accessibility and WCAG compliance details
|
|
- Performance notes
|
|
|
|
---
|
|
|
|
## 8. Maintenance Guidelines
|
|
|
|
### Adding a New Page
|
|
|
|
1. Create `src/pages/new-page.astro`
|
|
2. Use `BaseLayout.astro` as wrapper with SEO props
|
|
3. Follow the section pattern (container-wrapper, SectionHeader, content)
|
|
4. Import and initialize animations in the `<script>` tag
|
|
5. Update `Header.astro` navigation links
|
|
6. Update `Footer.astro` if it appears in site map
|
|
7. Update `src/pages/sitemap.astro` and `sitemap.xml.ts`
|
|
|
|
### Adding a New Color
|
|
|
|
1. Add to `tailwind.config.mjs` under `theme.extend.colors`
|
|
2. Add CSS custom property to `src/styles/global.css` in `:root`
|
|
3. Create any needed component classes in `global.css`
|
|
|
|
### Adding a New Animation
|
|
|
|
1. Define keyframe in `src/styles/global.css` under `@keyframes`
|
|
2. Add animation class in Tailwind config `theme.extend.animation`
|
|
3. Add `[data-animate="new-type"]` CSS in `global.css` if using scroll-reveal
|
|
4. Always add `@media (prefers-reduced-motion: reduce)` override
|
|
|
|
### Modifying the Contact Form
|
|
|
|
- **Validation rules**: Edit the regex patterns in the `<script>` section of `contact.astro`
|
|
- **Budget options**: Add/remove `<label class="budget-option">` items
|
|
- **FAQ entries**: Add `<details>` blocks in the FAQ section
|
|
- **Server handling**: The form POSTs to `/api/contact` — edit `src/pages/api/contact.ts`
|
|
|
|
### Performance Checklist (before deploy)
|
|
|
|
- [ ] New images use `<OptimizedImage>` or `<LazyImage>` components
|
|
- [ ] Large sections use `data-animate` for scroll-reveal (avoid layout shift)
|
|
- [ ] No inline `style` tags with large CSS blocks — use `global.css` classes
|
|
- [ ] External resources are from whitelisted domains only
|
|
- [ ] Run `npm run build` and check bundle size (target: <300KB client JS)
|
|
- [ ] Run Lighthouse (target: 90+ on all metrics)
|
|
|
|
### Dependency Updates
|
|
|
|
Key dependencies to keep updated:
|
|
- `astro` — Major versions may need config migration
|
|
- `@astrojs/tailwind` — Follows Tailwind CSS releases
|
|
- `tailwindcss` — v3.x series; v4 will require config rewrite
|
|
- `@sentry/browser` — Security patches important
|
|
|
|
---
|
|
|
|
## 9. Future Enhancement Recommendations
|
|
|
|
### High Priority
|
|
|
|
#### 1. Blog / Content Section
|
|
The `src/content/blog/` directory exists but no blog listing page is implemented. Recommended approach:
|
|
- Create `src/pages/blog/index.astro` with card grid
|
|
- Create `src/pages/blog/[slug].astro` for individual posts
|
|
- Use Astro Content Collections (already configured)
|
|
- Leverage existing `SectionHeader`, `Card`, `Badge` components
|
|
|
|
#### 2. Case Study Modal → Dedicated Pages
|
|
Portfolio case studies currently open in a JavaScript modal. For better SEO and shareability:
|
|
- Create `src/pages/portfolio/[slug].astro`
|
|
- Add structured data (CreativeWork schema) per case study
|
|
- Implement smooth navigation with View Transitions API
|
|
|
|
#### 3. Form Backend Enhancement
|
|
Current contact form uses a simple API route. Consider:
|
|
- Email notification via SendGrid or Resend
|
|
- CRM integration (HubSpot, Pipedrive)
|
|
- File attachment support for design briefs
|
|
- Multi-step form wizard for complex project scoping
|
|
|
|
### Medium Priority
|
|
|
|
#### 4. i18n / Localization
|
|
Astro has built-in i18n routing. For a future multi-language site:
|
|
- Use `src/pages/[locale]/` routing
|
|
- Store copy in `src/content/` or external CMS
|
|
- Update `astro.config.mjs` with `i18n` config
|
|
|
|
#### 5. Dark Mode ✅ Implemented
|
|
Dark mode is fully implemented. See [THEME_SYSTEM_GUIDE.md](./THEME_SYSTEM_GUIDE.md) for developer and user documentation.
|
|
|
|
#### 6. Animation Performance
|
|
Current scroll-reveal uses a single Intersection Observer. For pages with many animated elements:
|
|
- Consider virtualized animation queuing (pause observer after element leaves viewport)
|
|
- Profile with Chrome DevTools Performance tab on low-end devices
|
|
- The `data-duration="fast"` option is available for elements needing snappier reveals
|
|
|
|
### Low Priority
|
|
|
|
#### 7. Component Storybook
|
|
The `Badge`, `Card`, and `SectionHeader` components are well-isolated. A Storybook setup would:
|
|
- Provide a visual component library
|
|
- Document all variant combinations
|
|
- Enable designer handoff via Storybook's design tools
|
|
|
|
#### 8. Automated Visual Regression
|
|
Add Playwright screenshot tests for critical pages:
|
|
```typescript
|
|
// tests/visual.spec.ts
|
|
test('contact page matches snapshot', async ({ page }) => {
|
|
await page.goto('/contact');
|
|
await expect(page).toHaveScreenshot('contact.png');
|
|
});
|
|
```
|
|
Use `--update-snapshots` flag after intentional design changes.
|
|
|
|
#### 9. Edge Caching
|
|
Currently SSR renders on every request. For pages with low dynamic content:
|
|
- Use Astro's `export const prerender = true` for static generation
|
|
- Or add `Cache-Control` headers in the Node.js middleware
|
|
- The about, privacy, and terms pages are good candidates
|
|
|
|
---
|
|
|
|
## 10. Quick Reference
|
|
|
|
### Common Patterns
|
|
|
|
#### Hero Section
|
|
|
|
```astro
|
|
<section class="bg-hero-dark py-32 relative overflow-hidden">
|
|
<div class="grid-overlay"></div>
|
|
<div class="blob blob-primary top-0 right-0"></div>
|
|
|
|
<div class="container-wrapper text-center">
|
|
<Badge label="Eyebrow Text" variant="primary-dark" />
|
|
<h1 class="display-xl text-white font-bold mt-4">
|
|
Hero <span class="gradient-text">Headline</span>
|
|
</h1>
|
|
<p class="text-xl text-secondary-300 max-w-2xl mx-auto mt-6">
|
|
Supporting description text.
|
|
</p>
|
|
<div class="flex gap-4 justify-center mt-10">
|
|
<a href="/contact" class="btn-primary-lg">Primary CTA</a>
|
|
<a href="/portfolio" class="btn-inverse-lg">Secondary CTA</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
```
|
|
|
|
#### Feature Card Grid
|
|
|
|
```astro
|
|
<section class="py-24 bg-white">
|
|
<div class="container-wrapper">
|
|
<SectionHeader badge="Features" title="Why Choose Us" />
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-16">
|
|
{features.map((feature, i) => (
|
|
<Card variant="feature" data-animate="fade-up" data-delay={i * 100}>
|
|
<div class="icon-container icon-md mb-4">
|
|
<!-- SVG icon -->
|
|
</div>
|
|
<h3 class="text-xl font-semibold text-secondary-800">{feature.title}</h3>
|
|
<p class="text-secondary-600 mt-2">{feature.description}</p>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
```
|
|
|
|
#### CTA Banner
|
|
|
|
```astro
|
|
<section class="bg-cta-dark py-24 relative overflow-hidden">
|
|
<div class="container-wrapper text-center">
|
|
<h2 class="display-lg text-white font-bold">
|
|
Ready to <span class="gradient-text">Get Started?</span>
|
|
</h2>
|
|
<p class="text-secondary-300 text-xl max-w-2xl mx-auto mt-6">
|
|
Supporting text here.
|
|
</p>
|
|
<div class="flex gap-4 justify-center mt-10">
|
|
<a href="/contact" class="btn-primary-lg">Start a Project</a>
|
|
<a href="/services" class="btn-inverse-lg">View Services</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
```
|
|
|
|
### Class Cheat Sheet
|
|
|
|
```
|
|
Layout:
|
|
container-wrapper Max-width container with responsive padding
|
|
|
|
Buttons:
|
|
btn-primary Solid cyan button
|
|
btn-secondary Outline cyan button
|
|
btn-accent Solid amber button
|
|
btn-ghost Transparent/white hover
|
|
btn-primary-lg Large solid (hero sections)
|
|
btn-inverse-lg Large outline white (dark backgrounds)
|
|
|
|
Badges:
|
|
badge badge-primary Cyan pill (light bg)
|
|
badge badge-primary-dark Cyan pill (dark bg)
|
|
badge badge-accent Amber pill
|
|
badge badge-secondary Slate pill
|
|
|
|
Forms:
|
|
form-label Label text
|
|
form-input Text input / textarea / select
|
|
form-input-error Error state for inputs
|
|
form-error Error message text
|
|
|
|
Typography:
|
|
gradient-text Cyan gradient text
|
|
gradient-text-primary Alternate gradient
|
|
section-title Section H2 on light bg
|
|
section-description Section P on light bg
|
|
section-title-inverse Section H2 on dark bg
|
|
section-description-inverse Section P on dark bg
|
|
|
|
Decorative:
|
|
blob blob-primary Cyan background blob
|
|
blob blob-accent Amber background blob
|
|
grid-overlay Dot grid overlay
|
|
|
|
Animations:
|
|
data-animate="fade-up" Scroll reveal animation
|
|
data-delay="200" Delay in ms (100-800)
|
|
data-counter="100" Animated counter
|
|
```
|
|
|
|
---
|
|
|
|
*This document was generated by the documentation-writer agent on 2026-03-21. Update this document when making significant design changes to the site.*
|