1047 lines
24 KiB
Markdown
1047 lines
24 KiB
Markdown
# Pulse Design System
|
|
|
|
**Version:** 1.0
|
|
**Last Updated:** 2026-02-06
|
|
**Maintained by:** Ciphera Design Team
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This document defines the visual language and design patterns for Pulse Analytics. All components and pages should follow these standards to ensure consistency, accessibility, and brand alignment.
|
|
|
|
---
|
|
|
|
## 🎨 Colors
|
|
|
|
### Brand Colors
|
|
|
|
```css
|
|
/* Primary Brand Color */
|
|
--brand-orange: #FD5E0F;
|
|
--brand-orange-hover: #E54E00; /* Darker for hover states */
|
|
|
|
/* Injected by @ciphera-net/ui preset — use in SVG, Recharts, rgba() */
|
|
--color-brand-orange: #FD5E0F;
|
|
--color-brand-orange-rgb: 253, 94, 15; /* Used by .glow-orange utility; also for custom rgba(var(--color-brand-orange-rgb), 0.5) */
|
|
|
|
/* Usage */
|
|
- Primary CTAs, links, focus rings
|
|
- Accent elements, badges
|
|
- Never use for large backgrounds (too vibrant)
|
|
- var(--color-brand-orange) for SVG/Recharts where Tailwind classes don't apply
|
|
```
|
|
|
|
### Neutral Scale
|
|
|
|
Using Tailwind's neutral palette (50-950):
|
|
|
|
| Token | Light Mode | Dark Mode | Usage |
|
|
|-------|------------|-----------|--------|
|
|
| `neutral-50` | #FAFAFA | - | Subtle backgrounds |
|
|
| `neutral-100` | #F5F5F5 | - | Card backgrounds, disabled states |
|
|
| `neutral-200` | #E5E5E5 | - | Borders, dividers |
|
|
| `neutral-300` | #D4D4D4 | - | Borders (hover) |
|
|
| `neutral-400` | #A3A3A3 | #A3A3A3 | Secondary text (dark mode) |
|
|
| `neutral-500` | #737373 | - | Tertiary text, placeholders |
|
|
| `neutral-600` | #525252 | - | Body text (light mode) |
|
|
| `neutral-700` | #404040 | - | - |
|
|
| `neutral-800` | #262626 | #262626 | Borders, backgrounds (dark mode) |
|
|
| `neutral-900` | #171717 | #171717 | Headings, primary text |
|
|
| `neutral-950` | #0A0A0A | - | - |
|
|
|
|
### Semantic Colors
|
|
|
|
```css
|
|
--color-success: #10B981; /* Green for success states */
|
|
--color-warning: #F59E0B; /* Amber for warnings */
|
|
--color-error: #EF4444; /* Red for errors, destructive actions */
|
|
```
|
|
|
|
### Color Usage Rules
|
|
|
|
**Text Colors:**
|
|
- **Headings:** `text-neutral-900 dark:text-white`
|
|
- **Body text:** `text-neutral-600 dark:text-neutral-400`
|
|
- **Secondary text:** `text-neutral-500 dark:text-neutral-400`
|
|
- **Tertiary/disabled:** `text-neutral-400 dark:text-neutral-500`
|
|
- **Links:** `text-brand-orange hover:text-brand-orange-hover`
|
|
|
|
**Backgrounds:**
|
|
- **Page:** `bg-white dark:bg-neutral-900`
|
|
- **Cards:** `bg-white dark:bg-neutral-900`
|
|
- **Subtle sections:** `bg-neutral-50 dark:bg-neutral-800/50`
|
|
- **Overlays:** `bg-white/80 dark:bg-neutral-900/80` (with backdrop-blur)
|
|
|
|
**Borders:**
|
|
- **Standard:** `border-neutral-200 dark:border-neutral-800`
|
|
- **Subtle:** `border-neutral-200/50 dark:border-neutral-800/50`
|
|
- **Focus rings:** `ring-brand-orange`
|
|
- **Danger actions:** `border-red-200 dark:border-red-900`
|
|
|
|
---
|
|
|
|
## 📝 Typography
|
|
|
|
### Font Family
|
|
|
|
```tsx
|
|
font-family: 'Plus Jakarta Sans', system-ui, sans-serif
|
|
```
|
|
|
|
Loaded via Next.js `next/font/google` in root layout.
|
|
|
|
### Heading Scale
|
|
|
|
| Element | Size (Mobile → Desktop) | Weight | Usage |
|
|
|---------|-------------------------|--------|-------|
|
|
| **H1 (Marketing)** | `text-4xl → text-5xl` | `font-bold` | Landing pages, about, FAQ, installation |
|
|
| **H1 (Hero)** | `text-5xl → text-7xl` | `font-bold` | Home page hero only |
|
|
| **H1 (App)** | `text-2xl` | `font-bold` | Dashboard, settings, app pages |
|
|
| **H2 (Section)** | `text-2xl` | `font-bold` | Major page sections |
|
|
| **H2 (Panel)** | `text-xl` | `font-semibold` | Panel headers (realtime, modals) |
|
|
| **H3 (Section)** | `text-lg` | `font-semibold` | Dashboard widgets, subsections |
|
|
| **H3 (Card)** | `text-xl` | `font-bold` | Feature cards, integration cards |
|
|
| **H4 (Label)** | `text-base` | `font-semibold` | Footer sections, small headers |
|
|
|
|
### Body Text
|
|
|
|
| Size | Class | Usage |
|
|
|------|-------|-------|
|
|
| **Large** | `text-xl` | Hero descriptions, intro paragraphs |
|
|
| **Base** | `text-base` (default) | Body text, card descriptions |
|
|
| **Small** | `text-sm` | Help text, labels, metadata |
|
|
| **Micro** | `text-xs` | Badges, timestamps, fine print |
|
|
|
|
### Font Weights
|
|
|
|
- `font-bold` (700): H1, H2, card titles, stat values
|
|
- `font-semibold` (600): H2 panels, H3 sections, buttons
|
|
- `font-medium` (500): Links, labels, secondary headings
|
|
- `font-normal` (400): Body text
|
|
|
|
### Line Height
|
|
|
|
- **Headings:** Default tight (`leading-tight` or none)
|
|
- **Body:** `leading-relaxed` for readability
|
|
|
|
### Text Color Patterns
|
|
|
|
```tsx
|
|
// Headings
|
|
className="text-neutral-900 dark:text-white"
|
|
|
|
// Body text (most readable)
|
|
className="text-neutral-600 dark:text-neutral-400"
|
|
|
|
// Secondary/helper text
|
|
className="text-neutral-500 dark:text-neutral-400"
|
|
|
|
// Disabled/placeholder
|
|
className="text-neutral-400 dark:text-neutral-500"
|
|
```
|
|
|
|
---
|
|
|
|
## 📏 Spacing System
|
|
|
|
### Padding Scale
|
|
|
|
| Value | Size | Usage |
|
|
|-------|------|-------|
|
|
| `p-3` | 12px | Icon containers, compact badges |
|
|
| `p-4` | 16px | Card headers, small cards, stat displays |
|
|
| `p-6` | 24px | Standard cards, forms, modals |
|
|
| `p-8` | 32px | Feature cards, spacious sections |
|
|
| `p-10` | 40px | Page containers |
|
|
| `p-12` | 48px | Empty states, hero sections |
|
|
|
|
### Vertical Spacing (Sections)
|
|
|
|
| Value | Size | Usage |
|
|
|-------|------|-------|
|
|
| `py-8` | 32px | App page containers |
|
|
| `py-10` | 40px | Standard pages |
|
|
| `py-16` | 64px | Content sections |
|
|
| `py-20` | 80px | Marketing hero sections |
|
|
|
|
### Gaps (Flexbox/Grid)
|
|
|
|
| Value | Size | Usage |
|
|
|-------|------|-------|
|
|
| `gap-2` | 8px | Icon+text pairs, inline elements |
|
|
| `gap-3` | 12px | Button groups, small layouts |
|
|
| `gap-4` | 16px | Form fields, button bars |
|
|
| `gap-6` | 24px | Card grids, main layouts |
|
|
| `gap-8` | 32px | Large section spacing |
|
|
|
|
### Margins
|
|
|
|
| Value | Usage |
|
|
|-------|-------|
|
|
| `mb-2` to `mb-4` | Between related elements |
|
|
| `mb-6` to `mb-8` | Between sections |
|
|
| `mb-16` to `mb-32` | Major page divisions |
|
|
|
|
---
|
|
|
|
## 🔲 Border Radius
|
|
|
|
| Element | Radius | Class | Example |
|
|
|---------|--------|-------|---------|
|
|
| **Cards** | 16px | `rounded-2xl` | Dashboard cards, feature cards |
|
|
| **Buttons** | 12px | `rounded-xl` | Primary, secondary buttons |
|
|
| **Inputs** | 8px | `rounded-lg` | Text inputs, selects, textareas |
|
|
| **Tabs** | 8px | `rounded-lg` | Tab buttons |
|
|
| **Badges** | Full | `rounded-full` | Status badges, tags |
|
|
| **Small UI** | 8px | `rounded-lg` | Small buttons, icons |
|
|
|
|
**Rule:** Larger elements = larger radius. Cards are most prominent, so `rounded-2xl`.
|
|
|
|
---
|
|
|
|
## 🎭 Shadows
|
|
|
|
### Shadow Scale
|
|
|
|
```tsx
|
|
// Subtle (cards at rest)
|
|
shadow-sm
|
|
|
|
// Medium (hover states)
|
|
shadow-md, shadow-lg
|
|
|
|
// Prominent (elevated elements, popovers)
|
|
shadow-xl, shadow-2xl
|
|
|
|
// Glow effects (CTAs)
|
|
shadow-lg shadow-brand-orange/20
|
|
```
|
|
|
|
### Usage Examples
|
|
|
|
```tsx
|
|
// Card at rest
|
|
className="rounded-2xl border border-neutral-200 dark:border-neutral-800 shadow-sm"
|
|
|
|
// Card hover
|
|
className="hover:shadow-xl hover:-translate-y-1 transition-all"
|
|
|
|
// Primary CTA
|
|
className="shadow-lg shadow-brand-orange/20"
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Focus States
|
|
|
|
### Standard Focus Pattern
|
|
|
|
For most interactive elements:
|
|
```tsx
|
|
focus:outline-none focus:ring-2 focus:ring-brand-orange focus:ring-offset-2
|
|
```
|
|
|
|
### Compact Pattern (Links, Small Elements)
|
|
|
|
```tsx
|
|
focus:outline-none focus:ring-2 focus:ring-brand-orange focus:rounded
|
|
```
|
|
|
|
### Danger Actions (Delete, Reset)
|
|
|
|
```tsx
|
|
focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2
|
|
```
|
|
|
|
### Inset Focus (Selected Elements)
|
|
|
|
```tsx
|
|
focus:outline-none focus:ring-2 focus:ring-brand-orange focus:ring-inset
|
|
```
|
|
|
|
**Rule:** ALL interactive elements must have a visible focus state for keyboard accessibility (WCAG 2.1 Level AA).
|
|
|
|
---
|
|
|
|
## 🧩 Custom CSS Classes
|
|
|
|
**Location:** `styles/globals.css`
|
|
|
|
### `.card-glass`
|
|
|
|
Glass card effect with backdrop blur (premium feel):
|
|
```css
|
|
.card-glass {
|
|
@apply bg-white/80 dark:bg-neutral-900/80;
|
|
@apply backdrop-blur-xl;
|
|
@apply border border-neutral-200/50 dark:border-neutral-800/50;
|
|
@apply rounded-2xl;
|
|
@apply transition-all duration-300 ease-out;
|
|
}
|
|
```
|
|
|
|
**Usage:** Feature cards on home page, pricing cards
|
|
|
|
---
|
|
|
|
### `.gradient-text`
|
|
|
|
Orange gradient for emphasized text:
|
|
```css
|
|
.gradient-text {
|
|
@apply bg-gradient-to-r from-brand-orange to-brand-orange-hover bg-clip-text text-transparent;
|
|
}
|
|
```
|
|
|
|
**Usage:** Hero headlines ("privacy-conscious" on home page)
|
|
|
|
---
|
|
|
|
### `.badge-primary`
|
|
|
|
Primary badge style (orange theme):
|
|
```css
|
|
.badge-primary {
|
|
@apply inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider;
|
|
@apply bg-brand-orange/10 text-brand-orange border border-brand-orange/20;
|
|
}
|
|
```
|
|
|
|
**Usage:** "Privacy-First Analytics" badge, "FAQ" badge, page indicators
|
|
|
|
---
|
|
|
|
### `.bg-grid-pattern`
|
|
|
|
Subtle dot grid background:
|
|
```css
|
|
.bg-grid-pattern {
|
|
background-image: radial-gradient(circle at 1px 1px, currentColor 1px, transparent 0);
|
|
background-size: 32px 32px;
|
|
}
|
|
```
|
|
|
|
**Usage:** Marketing page backgrounds (with radial mask for fade effect)
|
|
|
|
---
|
|
|
|
## 🧱 Component Patterns
|
|
|
|
### Button Variants
|
|
|
|
**Primary (Brand Action):**
|
|
```tsx
|
|
<Button variant="primary">Get Started</Button>
|
|
// Orange background, white text, shadow on hover
|
|
```
|
|
|
|
**Secondary (Neutral Action):**
|
|
```tsx
|
|
<Button variant="secondary">Cancel</Button>
|
|
// White background, border, neutral text
|
|
```
|
|
|
|
**Danger (Destructive Action):**
|
|
```tsx
|
|
<Button variant="danger">Delete</Button>
|
|
// Red background, white text (for critical actions)
|
|
```
|
|
|
|
---
|
|
|
|
### Empty States
|
|
|
|
**Pattern:**
|
|
```tsx
|
|
<div className="flex flex-col items-center justify-center text-center gap-3 p-8">
|
|
<div className="rounded-full bg-neutral-100 dark:bg-neutral-800 p-3">
|
|
<IconComponent className="w-6 h-6 text-neutral-500 dark:text-neutral-400" />
|
|
</div>
|
|
<p className="text-sm font-medium text-neutral-900 dark:text-white">
|
|
No data yet
|
|
</p>
|
|
<p className="text-xs text-neutral-500 dark:text-neutral-400">
|
|
Data will appear here once visitors arrive
|
|
</p>
|
|
</div>
|
|
```
|
|
|
|
**Usage:** Dashboard widgets with no data, empty site lists, etc.
|
|
|
|
---
|
|
|
|
### Loading States
|
|
|
|
**Full Page Loading:**
|
|
```tsx
|
|
<LoadingOverlay
|
|
logoSrc="/pulse_icon_no_margins.png"
|
|
title="Pulse"
|
|
/>
|
|
```
|
|
|
|
**Inline Loading:**
|
|
```tsx
|
|
<Button isLoading={true}>
|
|
Saving...
|
|
</Button>
|
|
```
|
|
|
|
**Skeleton Loading:**
|
|
```tsx
|
|
<div className="animate-pulse rounded-2xl bg-neutral-100 dark:bg-neutral-800 h-48" />
|
|
```
|
|
|
|
---
|
|
|
|
### Success/Error States
|
|
|
|
**Success Toast:**
|
|
```tsx
|
|
toast.success('Site created successfully')
|
|
// Green toast with checkmark
|
|
```
|
|
|
|
**Error Toast:**
|
|
```tsx
|
|
toast.error('Failed to load uptime monitors')
|
|
// Red toast with X icon — always mention what failed
|
|
```
|
|
|
|
**Error Display:**
|
|
```tsx
|
|
<div className="text-red-500 text-sm text-center">
|
|
{error}
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 🎬 Animations
|
|
|
|
### Framer Motion Patterns
|
|
|
|
#### Page Entrance
|
|
```tsx
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
{content}
|
|
</motion.div>
|
|
```
|
|
|
|
#### Stagger Animation
|
|
```tsx
|
|
{items.map((item, i) => (
|
|
<motion.div
|
|
key={i}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.5, delay: i * 0.1 }}
|
|
>
|
|
{item}
|
|
</motion.div>
|
|
))}
|
|
```
|
|
|
|
#### Hover Scale
|
|
```tsx
|
|
<motion.div
|
|
whileHover={{ scale: 1.05 }}
|
|
transition={{ duration: 0.2 }}
|
|
>
|
|
{content}
|
|
</motion.div>
|
|
```
|
|
|
|
#### List Item Entry/Exit
|
|
```tsx
|
|
<AnimatePresence mode="popLayout">
|
|
{items.map(item => (
|
|
<motion.div
|
|
key={item.id}
|
|
initial={{ opacity: 0, x: -10 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: -10 }}
|
|
transition={{ duration: 0.2 }}
|
|
>
|
|
{item}
|
|
</motion.div>
|
|
))}
|
|
</AnimatePresence>
|
|
```
|
|
|
|
---
|
|
|
|
### CSS Transitions
|
|
|
|
**Duration Standards:**
|
|
- **Fast (200ms):** Input focus, button clicks, immediate feedback
|
|
- **Medium (300ms):** Hover effects, color changes, icon transforms
|
|
- **Slow (500ms):** Page entrances, large movements
|
|
|
|
**Easing:**
|
|
- Default: `ease-out` (feels snappy)
|
|
- Alternative: `ease-in-out` (smooth both ways)
|
|
|
|
```tsx
|
|
// Standard hover
|
|
className="transition-colors duration-200 hover:text-brand-orange"
|
|
|
|
// Card hover
|
|
className="transition-all duration-300 hover:-translate-y-1 hover:shadow-xl"
|
|
```
|
|
|
|
---
|
|
|
|
## 📱 Responsive Breakpoints
|
|
|
|
Using Tailwind defaults:
|
|
|
|
| Breakpoint | Size | Usage |
|
|
|------------|------|-------|
|
|
| `sm:` | 640px | Small tablets, large phones |
|
|
| `md:` | 768px | Tablets, small laptops |
|
|
| `lg:` | 1024px | Laptops, desktops |
|
|
| `xl:` | 1280px | Large desktops |
|
|
|
|
### Common Patterns
|
|
|
|
**Grid Layouts:**
|
|
```tsx
|
|
// 1 col mobile, 2 col tablet, 3 col desktop
|
|
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
|
|
```
|
|
|
|
**Typography:**
|
|
```tsx
|
|
// Smaller on mobile, larger on desktop
|
|
className="text-4xl md:text-5xl"
|
|
```
|
|
|
|
**Padding:**
|
|
```tsx
|
|
// Less padding on mobile
|
|
className="px-4 sm:px-6"
|
|
```
|
|
|
|
---
|
|
|
|
## 🔘 Buttons
|
|
|
|
### Variant Usage
|
|
|
|
**Primary (Orange):**
|
|
- Main CTAs ("Get Started", "Create Site", "Save Changes")
|
|
- Positive actions
|
|
- One per screen section max
|
|
|
|
**Secondary (Neutral):**
|
|
- Cancel, Back, alternative actions
|
|
- Can have multiple per section
|
|
|
|
**Ghost (Minimal):**
|
|
- Tertiary actions, "View All" links
|
|
- Inline actions that shouldn't be prominent
|
|
|
|
**Danger (Red):**
|
|
- Delete, Reset Data, irreversible actions
|
|
- Always confirm before action
|
|
|
|
### Size Variants
|
|
|
|
```tsx
|
|
// Default
|
|
<Button>Save</Button>
|
|
|
|
// Large (CTAs)
|
|
<Button className="px-8 py-4 text-lg">Get Started</Button>
|
|
|
|
// Small (compact UI)
|
|
<Button className="text-sm px-4 py-2">Add</Button>
|
|
```
|
|
|
|
---
|
|
|
|
## 📦 Cards
|
|
|
|
### Standard Card
|
|
|
|
```tsx
|
|
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl p-6">
|
|
{content}
|
|
</div>
|
|
```
|
|
|
|
### Glass Card (Premium Feel)
|
|
|
|
```tsx
|
|
<div className="card-glass p-8 hover:-translate-y-1 hover:shadow-xl transition-all duration-300">
|
|
{content}
|
|
</div>
|
|
```
|
|
|
|
**When to use Glass:** Marketing pages, feature showcases, hero sections
|
|
|
|
### Hover Effects
|
|
|
|
```tsx
|
|
// Subtle lift
|
|
className="hover:-translate-y-1 hover:shadow-md transition-all"
|
|
|
|
// Scale
|
|
className="hover:scale-105 transition-transform"
|
|
```
|
|
|
|
---
|
|
|
|
## 🏷️ Badges
|
|
|
|
### Primary Badge
|
|
```tsx
|
|
<span className="badge-primary">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-brand-orange animate-pulse" />
|
|
Privacy-First Analytics
|
|
</span>
|
|
```
|
|
|
|
### Status Badges
|
|
|
|
**Active/Live:**
|
|
```tsx
|
|
<div className="flex items-center gap-2 rounded-full bg-green-50 px-2 py-1 text-xs font-medium text-green-700 dark:bg-green-900/20 dark:text-green-400">
|
|
<span className="relative flex h-2 w-2">
|
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
|
</span>
|
|
Active
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 Special Effects
|
|
|
|
### Background Glow (Atmosphere)
|
|
|
|
```tsx
|
|
<div className="absolute inset-0 -z-10 pointer-events-none">
|
|
<div className="absolute top-0 left-1/4 w-[500px] h-[500px] bg-brand-orange/10 rounded-full blur-[128px] opacity-60" />
|
|
<div className="absolute bottom-0 right-1/4 w-[500px] h-[500px] bg-neutral-500/10 rounded-full blur-[128px] opacity-40" />
|
|
</div>
|
|
```
|
|
|
|
### Grid Pattern Background
|
|
|
|
```tsx
|
|
<div
|
|
className="absolute inset-0 bg-grid-pattern opacity-[0.02] dark:opacity-[0.05]"
|
|
style={{ maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 70%)' }}
|
|
/>
|
|
```
|
|
|
|
### SVG Underline (Accent)
|
|
|
|
```tsx
|
|
<svg className="absolute -bottom-2 left-0 w-full h-3 text-brand-orange/30" viewBox="0 0 200 12" preserveAspectRatio="none">
|
|
<path d="M0 9C50 3 150 3 200 9" fill="none" stroke="currentColor" strokeWidth="4" strokeLinecap="round" />
|
|
</svg>
|
|
```
|
|
|
|
---
|
|
|
|
## ♿ Accessibility
|
|
|
|
### Focus Indicators
|
|
|
|
**ALL interactive elements** must have visible focus states. See "Focus States" section above.
|
|
|
|
### Semantic HTML
|
|
|
|
- Use proper heading hierarchy (H1 → H2 → H3, no skipping)
|
|
- Use `<button>` for actions, `<Link>` for navigation
|
|
- Use `<nav>` for navigation sections
|
|
- Use `<main>` for primary content
|
|
|
|
### Alt Text
|
|
|
|
- All images must have descriptive `alt` attributes
|
|
- Decorative images use `alt=""` or `aria-hidden="true"`
|
|
|
|
### Keyboard Navigation
|
|
|
|
- Tab order should follow visual order
|
|
- All actions accessible via keyboard
|
|
- No keyboard traps
|
|
- Focus visible at all times
|
|
|
|
### ARIA Attributes
|
|
|
|
**Current usage (limited but correct):**
|
|
- `aria-label` on icon-only buttons (social links)
|
|
- `aria-hidden="true"` on decorative elements (arrows, ornaments)
|
|
|
|
**Could enhance:**
|
|
- `role="tablist"` on tab navigation
|
|
- `aria-live="polite"` on realtime counters
|
|
- `role="dialog"` on modals
|
|
|
|
---
|
|
|
|
## 🌓 Dark Mode
|
|
|
|
### Implementation
|
|
|
|
Using Tailwind's `dark:` prefix with class-based switching.
|
|
|
|
### Color Adjustments
|
|
|
|
**Backgrounds:**
|
|
- Light: `bg-white`, `bg-neutral-50`
|
|
- Dark: `dark:bg-neutral-900`, `dark:bg-neutral-800`
|
|
|
|
**Text:**
|
|
- Light: `text-neutral-900`, `text-neutral-600`
|
|
- Dark: `dark:text-white`, `dark:text-neutral-400`
|
|
|
|
**Borders:**
|
|
- Light: `border-neutral-200`
|
|
- Dark: `dark:border-neutral-800`
|
|
|
|
### Testing
|
|
|
|
Always test both light and dark modes:
|
|
- Color contrast (4.5:1 minimum in both modes)
|
|
- Focus ring visibility (should stand out in both modes)
|
|
- Glass effects (should work in both modes)
|
|
|
|
---
|
|
|
|
## 📊 Dashboard Widgets
|
|
|
|
### Standard Widget Layout
|
|
|
|
```tsx
|
|
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl p-6 h-full flex flex-col">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className="text-lg font-semibold text-neutral-900 dark:text-white">
|
|
Widget Title
|
|
</h3>
|
|
<button className="text-xs font-medium text-neutral-500 hover:text-neutral-900 transition-colors focus:...">
|
|
View All
|
|
</button>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="flex-1 min-h-[200px]">
|
|
{hasData ? (
|
|
<DataDisplay />
|
|
) : (
|
|
<EmptyState />
|
|
)}
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Tab Toggles (Inside Widgets)
|
|
|
|
```tsx
|
|
<div className="flex p-1 bg-neutral-100 dark:bg-neutral-800 rounded-lg">
|
|
{tabs.map(tab => (
|
|
<button
|
|
key={tab}
|
|
onClick={() => setActiveTab(tab)}
|
|
className={`px-3 py-1 text-xs font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-brand-orange ${
|
|
activeTab === tab
|
|
? 'bg-white dark:bg-neutral-700 text-neutral-900 dark:text-white shadow-sm'
|
|
: 'text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white'
|
|
}`}
|
|
>
|
|
{tab}
|
|
</button>
|
|
))}
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Forms
|
|
|
|
### Input Styling
|
|
|
|
```tsx
|
|
<Input
|
|
id="field-name"
|
|
placeholder="Enter value"
|
|
className="rounded-lg"
|
|
/>
|
|
// Uses @ciphera-net/ui Input component
|
|
// Automatically includes focus states, dark mode
|
|
```
|
|
|
|
### Label Pattern
|
|
|
|
```tsx
|
|
<label htmlFor="field-name" className="block text-sm font-medium mb-2 text-neutral-700 dark:text-neutral-300">
|
|
Field Name
|
|
</label>
|
|
```
|
|
|
|
### Help Text
|
|
|
|
```tsx
|
|
<p className="mt-2 text-sm text-neutral-600 dark:text-neutral-400">
|
|
Additional information about this field.
|
|
</p>
|
|
```
|
|
|
|
### Error Display
|
|
|
|
```tsx
|
|
{error && (
|
|
<div className="text-red-500 text-sm text-center">
|
|
{error}
|
|
</div>
|
|
)}
|
|
```
|
|
|
|
---
|
|
|
|
## 🎭 Code Blocks (Integration Pages)
|
|
|
|
### VS Code-Style Syntax Highlighting
|
|
|
|
```tsx
|
|
<div className="bg-neutral-900 rounded-xl overflow-hidden shadow-2xl border border-neutral-800">
|
|
{/* Header bar */}
|
|
<div className="flex items-center px-4 py-3 bg-neutral-800 border-b border-neutral-800">
|
|
<div className="flex gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-red-500/20" />
|
|
<div className="w-3 h-3 rounded-full bg-yellow-500/20" />
|
|
<div className="w-3 h-3 rounded-full bg-green-500/20" />
|
|
</div>
|
|
<span className="ml-4 text-xs text-neutral-500 font-mono">filename.tsx</span>
|
|
</div>
|
|
|
|
{/* Code content */}
|
|
<div className="p-6 overflow-x-auto">
|
|
<code className="font-mono text-sm text-neutral-300">
|
|
{/* Syntax-highlighted code */}
|
|
</code>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
**Colors for syntax:**
|
|
- Blue `#61AFEF`: Keywords, tags
|
|
- Orange `#D19A66`: Strings
|
|
- Purple `#C678DD`: Functions
|
|
- Green `#98C379`: Comments
|
|
- White `#ABB2BF`: Text
|
|
|
|
---
|
|
|
|
## 🌍 Internationalization (Future)
|
|
|
|
**Currently:** English only
|
|
**When implementing i18n:**
|
|
- Use `next-intl` or similar
|
|
- Extract all strings to translation files
|
|
- Maintain "Swiss infrastructure" messaging
|
|
- Support: EN, DE, FR, IT (Swiss languages)
|
|
|
|
---
|
|
|
|
## 📐 Layout Patterns
|
|
|
|
### Page Container
|
|
|
|
```tsx
|
|
// App pages
|
|
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 py-8">
|
|
{content}
|
|
</div>
|
|
|
|
// Marketing pages
|
|
<div className="w-full max-w-4xl mx-auto px-4 pt-20 pb-10">
|
|
{content}
|
|
</div>
|
|
```
|
|
|
|
### Two-Column Settings Layout
|
|
|
|
```tsx
|
|
<div className="flex flex-col md:flex-row gap-8">
|
|
{/* Sidebar */}
|
|
<nav className="w-full md:w-64 flex-shrink-0">
|
|
{tabs}
|
|
</nav>
|
|
|
|
{/* Content */}
|
|
<div className="flex-1">
|
|
{content}
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 🔌 Integration with @ciphera-net/ui
|
|
|
|
### Current Usage
|
|
|
|
```tsx
|
|
import {
|
|
Button,
|
|
Input,
|
|
Select,
|
|
Modal,
|
|
LoadingOverlay,
|
|
DatePicker,
|
|
Captcha,
|
|
// Icons
|
|
CheckCircleIcon,
|
|
SettingsIcon,
|
|
// etc.
|
|
} from '@ciphera-net/ui'
|
|
```
|
|
|
|
**Version:** `^0.0.45`
|
|
|
|
### Preset
|
|
|
|
Pulse uses the Ciphera UI Tailwind preset:
|
|
```ts
|
|
// tailwind.config.ts
|
|
presets: [
|
|
require('@ciphera-net/ui/dist/tailwind-preset').default,
|
|
]
|
|
```
|
|
|
|
**Provides:**
|
|
- Brand colors (brand-orange)
|
|
- Background gradients (bg-ciphera-gradient)
|
|
- Neutral scale extension
|
|
|
|
---
|
|
|
|
## 🎯 Design Principles
|
|
|
|
### 1. Privacy-First Visual Language
|
|
- Clean, minimal design
|
|
- No tracking pixels, no external fonts from CDNs
|
|
- Swiss aesthetic (precision, trust, neutrality)
|
|
|
|
### 2. Accessibility First
|
|
- WCAG 2.1 Level AA compliance
|
|
- Keyboard navigation throughout
|
|
- Clear focus indicators
|
|
- Readable text contrast
|
|
|
|
### 3. Performance
|
|
- Lightweight animations (opacity, translate only)
|
|
- Optimized images
|
|
- Code splitting by route
|
|
- Fast transitions (200-500ms max)
|
|
|
|
### 4. Brand Consistency
|
|
- Orange used sparingly (accents only)
|
|
- Neutral-heavy palette
|
|
- Glass effects for premium feel
|
|
- Consistent with other Ciphera products
|
|
|
|
---
|
|
|
|
## 🛠️ Component Library
|
|
|
|
### Available from @ciphera-net/ui
|
|
|
|
**Buttons:** Primary, Secondary, Danger variants
|
|
**Forms:** Input, Select, Checkbox, DatePicker
|
|
**Feedback:** Toast (Sonner), LoadingOverlay
|
|
**Security:** Captcha component
|
|
**Icons:** Full icon set from Lucide
|
|
**Layout:** Modal component
|
|
|
|
### Custom to Pulse
|
|
|
|
**Dashboard:** Chart, TopPages, TopReferrers, Locations, TechSpecs, Campaigns, Goals, Performance
|
|
**Settings:** OrganizationSettings, ProfileSettings
|
|
**Sites:** SiteList, VerificationModal
|
|
|
|
---
|
|
|
|
## 📏 Checklist for New Components
|
|
|
|
When creating new components, ensure:
|
|
|
|
- [ ] Uses standard color tokens (no hardcoded hex values)
|
|
- [ ] Follows typography scale (no custom text sizes)
|
|
- [ ] Has dark mode support (`dark:` variants)
|
|
- [ ] Includes focus states on interactive elements
|
|
- [ ] Uses spacing system (no arbitrary padding values)
|
|
- [ ] Follows border radius standards
|
|
- [ ] Has empty state if displays lists
|
|
- [ ] Has loading state if async
|
|
- [ ] Has error state if can fail
|
|
- [ ] Responsive on mobile (<768px)
|
|
- [ ] Accessible (semantic HTML, ARIA where needed)
|
|
- [ ] Consistent with existing components
|
|
|
|
---
|
|
|
|
## 🔍 Testing Checklist
|
|
|
|
Before deploying changes:
|
|
|
|
### Visual
|
|
- [ ] Light mode looks correct
|
|
- [ ] Dark mode looks correct
|
|
- [ ] Focus rings visible on all interactive elements
|
|
- [ ] Typography hierarchy is clear
|
|
- [ ] Spacing feels consistent
|
|
|
|
### Responsive
|
|
- [ ] Works on mobile (375px)
|
|
- [ ] Works on tablet (768px)
|
|
- [ ] Works on desktop (1280px+)
|
|
|
|
### Accessibility
|
|
- [ ] Can navigate with keyboard only
|
|
- [ ] Focus visible at all times
|
|
- [ ] Screen reader friendly (test with VoiceOver/NVDA)
|
|
- [ ] Color contrast passes WCAG AA
|
|
|
|
### Cross-Browser
|
|
- [ ] Chrome/Edge (Chromium)
|
|
- [ ] Firefox
|
|
- [ ] Safari (macOS/iOS)
|
|
|
|
---
|
|
|
|
## 📚 References
|
|
|
|
- **Tailwind CSS:** https://tailwindcss.com/docs
|
|
- **Framer Motion:** https://www.framer.com/motion/
|
|
- **WCAG Guidelines:** https://www.w3.org/WAI/WCAG21/quickref/
|
|
- **Ciphera Brand:** (internal brand guidelines)
|
|
|
|
---
|
|
|
|
## 🔄 Changelog
|
|
|
|
### v1.0 (2026-02-06)
|
|
- Initial design system documentation
|
|
- Documented Tier 1 + Tier 2 standardization results
|
|
- Defined all component patterns
|
|
- Established accessibility standards
|
|
|
|
---
|
|
|
|
**This is a living document.** Update as the design system evolves.
|