# Theming Standard — danix.xyz Redesign **Version:** 1.0 **Last Updated:** 2026-04-08 **Audience:** Developers, theme builders, Hugo template authors --- ## 1. Overview ### Purpose This document is the **source of truth** for all theming decisions across danix.xyz. It defines the visual language, design patterns, and implementation guidelines that ensure consistency across all views, components, and future pages of the redesigned site. ### Scope This standard covers: - **Hugo theme development** — CSS, layouts, and component structure - **All views** — Existing (articles, About, etc.) and future (404, etc.) - **Both color modes** — Dark (default) and light theme variants - **All article types** — Tech, Life, Quote, Link, Photo with color-coded identity ### Design Philosophy The danix.xyz redesign is built on three pillars, of equal priority: 1. **Accessibility** — WCAG AA compliance minimum. Every component must be keyboard navigable, screen-reader friendly, and have sufficient color contrast. 2. **Performance** — Pages load fast and stay responsive. CSS custom properties are optimized to avoid runtime recalculations. Animations respect `prefers-reduced-motion`. 3. **Consistency** — Visual consistency is key in maintaining a defined brand identity and must be pursued on all views of the site. ### Evolution & Continuous Improvement This standard evolved from various iterations and mockups built in 2026-04. It reflects real-world design decisions tested across responsive breakpoints and color modes. **The spec is not final**—it remains open to improvement through implementation feedback and user testing. ### Dual-Theme System Dark mode and light mode are **first-class citizens**, not afterthoughts: - Dark mode is the default (aligns with "hacker aesthetic" branding) - Light mode has equivalent color contrasts and visual hierarchy - Transitions between themes are instant (no flickering) - Theme preference is persisted via `localStorage` - CSS media query (`prefers-color-scheme`) provides no-JS fallback ### CSS Custom Properties Architecture All colors, typography, and spacing use **CSS custom properties** (variables): - Single source of truth: `:root` defines dark mode base - `html.theme-light` overrides for light mode - `@media (prefers-color-scheme: light)` provides no-JS fallback - No hard-coded color values in component CSS - Easy theme extension: add new property, use in all relevant selectors ### Mobile-First Responsive Design The design system is built mobile-first: - Base styles target mobile (320px+) - Breakpoints expand layout: 768px (tablet), 1060px (desktop) - All components work at all sizes without layout shift - Typography scales responsively via `clamp()` or media queries ### Browser Support Supported: - Modern browsers: Chrome, Firefox, Safari (15+), Edge (all versions) - Mobile browsers: iOS Safari (15+), Chrome Mobile, Firefox Mobile - Graceful degradation for older browsers (fallback colors, readable without CSS variables) Explicitly unsupported: - Internet Explorer (all versions) - Legacy browsers without CSS custom properties support ### No-JavaScript Fallback All interactive features have CSS-only fallbacks: - Theme toggle: `@media (prefers-color-scheme: light)` provides light mode without JS - Responsive navigation: Media queries handle collapse/expand without hamburger menu JS - Animations: CSS transitions work even if JS fails to load ### Single Source: Unified Color Palette All article types, components, and views share one palette: - **5 article types** with distinct, recognizable colors (Tech, Life, Quote, Link, Photo) - **Colors are semantic**: `--accent`, `--border`, `--text`, not `--purple` or `--blue-300` - **Every view must support all 5 article types** with consistent color identity - **Type colors are never mixed**: A Quote is always green, never cyan or amber ### Typography Hierarchy Three font families serve specific purposes: | Font | Weight | Use Case | |---|---|---| | **Oxanium** | 700–800 | Display, headings, hero text, large titles | | **IBM Plex Sans** | 300–600 | Body text, prose, descriptions, UI labels | | **JetBrains Mono** | 400–700 | Monospace, code, terminal UI, metadata | Typography reinforces visual hierarchy and brand voice (technical, approachable, premium). ### Article Type System Five article types, each with: - **Distinct color** in badges, borders, accents - **Unique layout pattern** (quote vs. terminal vs. prose) - **Recognizable at a glance** across all views (timeline, masonry, cards, detail pages) Color mapping: | Type | Dark Mode | Light Mode | Use | |---|---|---|---| | **Tech** | `#a855f7` (Purple) | `#7c3aed` (Purple) | Technical articles, tutorials | | **Life** | `#f59e0b` (Amber) | `#d97706` (Amber) | Personal essays, life updates | | **Quote** | `#00ff88` (Green) | `#008f5a` (Green) | Quotes, inspirational content | | **Link** | `#38bdf8` (Cyan) | `#0284c7` (Cyan) | Bookmarks, external links | | **Photo** | `#ec4899` (Pink) | `#be185d` (Pink) | Photo essays, galleries | ### Matrix Animation: Foundational Brand Element The matrix rain effect is a core visual identity: - Present on hero sections and behind article content - Adapts opacity across color modes (dark: 13%, light: 18%) - Uses canvas for performance - Full viewport height, visible in side gutters - Never interferes with text readability (semi-transparent, behind content) ### Accessibility Baseline All components must meet **WCAG AA** minimum: - Color contrast ≥ 4.5:1 for normal text, ≥ 3:1 for large text - No color used alone to convey information (always pair with text or icon) - Keyboard navigation: Tab, Shift+Tab, Enter, Space, Escape - Screen readers: Semantic HTML, ARIA labels where needed - Animations: Respect `prefers-reduced-motion` system setting - Focus indicators: Always visible, never hidden ### Performance Baseline Production pages must meet: - **Initial load:** <3 seconds on 4G mobile - **Interaction:** Respond to user input within 100ms - **CSS custom properties:** Optimized to avoid layout thrashing - **Animations:** Use GPU-accelerated transforms (not color or size changes) - **Images:** Lazy-loaded, responsive sizes, WebP with fallback --- ## 2. Color System ### Palette Overview The color system uses **semantic naming** (not color-based names) to support theme switching: - **Base colors**: Background, surface, borders, text - **Accent colors**: Primary and secondary interaction colors - **Type colors**: Five article types with distinct identities - **Utility colors**: Glows, shadows, semi-transparent overlays All colors are defined as CSS custom properties in `:root` (dark mode base) with light mode overrides in `html.theme-light`. ### Dark Mode Base Palette Dark mode is the default theme. All base colors are defined in `:root`: ```css :root { /* Background & Surface */ --bg: #060b10; /* Primary background (darkest) */ --bg2: #0c1520; /* Secondary background */ --surface: #101e2d; /* Card/container background */ --border: #182840; /* Border color */ /* Text & Legibility */ --text: #c4d6e8; /* Primary text (light) */ --text-dim: #7a9bb8; /* Secondary/dimmed text */ --muted: #304860; /* Muted text, icons */ /* Accent Colors */ --accent: #a855f7; /* Purple — primary interaction, focus states */ --accent2: #00ff88; /* Neon green — secondary, highlights, progress */ --accent-glow: rgba(168, 85, 247, 0.12); /* Purple glow for shadows */ /* Article Type Colors */ --type-tech: #a855f7; /* Tech articles — purple */ --type-life: #f59e0b; /* Life articles — amber */ --type-quote: #00ff88; /* Quote articles — green */ --type-link: #38bdf8; /* Link articles — cyan */ --type-photo: #ec4899; /* Photo articles — pink */ } ``` **Rationale:** - Dark navy background (`#060b10`) reduces eye strain in low-light environments - Cyan text (`#c4d6e8`) has high contrast while maintaining "hacker aesthetic" - Purple accent (`#a855f7`) is premium, technical, recognizable - Neon green accent (`#00ff88`) provides vibrant secondary option for highlights and progress - Article type colors are vibrant, distinct, and instantly recognizable ### Light Mode Palette Light mode inverts the palette for high readability in bright environments. Applied via `html.theme-light`: ```css html.theme-light { /* Background & Surface */ --bg: #f0f4f8; /* Primary background (lightest) */ --bg2: #e2eaf4; /* Secondary background */ --surface: #d4dff0; /* Card/container background */ --border: #a8bdd8; /* Border color */ /* Text & Legibility */ --text: #0d1b2a; /* Primary text (dark) */ --text-dim: #2e4a6a; /* Secondary/dimmed text */ --muted: #6888a8; /* Muted text, icons */ /* Accent Colors */ --accent: #7c3aed; /* Purple (darker for light bg) */ --accent2: #008f5a; /* Green (darker for light bg) */ --accent-glow: rgba(124, 58, 237, 0.1); /* Purple glow adjusted */ /* Article Type Colors */ --type-tech: #7c3aed; /* Tech — darker purple */ --type-life: #d97706; /* Life — darker amber */ --type-quote: #008f5a; /* Quote — darker green */ --type-link: #0284c7; /* Link — darker cyan */ --type-photo: #be185d; /* Photo — darker pink */ } ``` **Design notes:** - All light mode colors are darker versions of dark mode (maintains hue, increases saturation) - Contrast ratios remain ≥4.5:1 for WCAG AA compliance - Article type colors are darker but maintain instant recognition - Opacity values for glows/shadows are reduced (more transparent) since light backgrounds need less emphasis ### Article Type Colors: Complete Reference | Type | Purpose | Dark Mode | Light Mode | Badge Example | |---|---|---|---|---| | **Tech** | Technical articles, tutorials, code | `#a855f7` | `#7c3aed` | Purple badge | | **Life** | Personal essays, life updates | `#f59e0b` | `#d97706` | Amber badge | | **Quote** | Quotes, inspirational content | `#00ff88` | `#008f5a` | Green badge | | **Link** | Bookmarks, curated links | `#38bdf8` | `#0284c7` | Cyan badge | | **Photo** | Photo essays, galleries | `#ec4899` | `#be185d` | Pink badge | **Usage rules:** - Type colors must appear in: badge background, top border of article card, type-specific accents - Type colors should be consistent: A Quote is always green, never purple - Type colors should be recognizable: Must maintain contrast with both dark and light backgrounds - Every article view (timeline, masonry, detail page) must show type color prominently ### Semantic Color Usage Colors are used semantically, not by visual appearance: | Property | Use Case | Example | |---|---|---| | `--bg` | Page background, full-screen elements | Hero section, page body | | `--bg2` | Secondary background, slightly raised | Navigation bar, section dividers | | `--surface` | Cards, containers, elevated content | Article cards, modals, sidebar | | `--border` | Dividers, outlines, subtle boundaries | Card borders, separators | | `--text` | Primary readable content | Body text, headings, labels | | `--text-dim` | Secondary content, less emphasis | Metadata, dates, bylines | | `--muted` | Background text, icons | Disabled states, faint icons | | `--accent` | Primary interaction, focus, highlights | Buttons, focus rings, links | | `--accent2` | Secondary interaction, progress | Progress bars, highlights, secondary buttons | | `--type-*` | Article-specific theming | Badges, borders, type-specific accents | **Implementation pattern:** ```css /* Good: semantic color naming */ .article-badge { background: var(--type-tech); } .link-button { color: var(--accent2); } /* Bad: hard-coded or color-named variables */ .article-badge { background: #a855f7; } /* Hard-coded purple */ .link-button { color: var(--bright-green); } /* Color-named, not semantic */ ``` ### Opacity & Transparency Transparent overlays use rgba() with theme-aware opacity: **Dark mode transparency:** ```css /* Semi-transparent dark overlay (text over images) */ background: rgba(6, 11, 16, 0.65); /* Dot grid background (subtle, not intrusive) */ background: radial-gradient(circle, rgba(168, 85, 247, 0.07) 1px, transparent 1px); /* Glow effects (purple semi-transparent) */ box-shadow: 0 0 40px rgba(168, 85, 247, 0.12); /* Accent glow */ --accent-glow: rgba(168, 85, 247, 0.12); ``` **Light mode transparency:** ```css /* Semi-transparent light overlay (text over images) */ background: rgba(240, 244, 248, 0.7); /* Dot grid background (lighter, less visible) */ background: radial-gradient(circle, rgba(124, 58, 237, 0.07) 1px, transparent 1px); /* Glow effects (purple, reduced opacity for light background) */ box-shadow: 0 0 40px rgba(124, 58, 237, 0.08); /* Adjusted accent glow */ --accent-glow: rgba(124, 58, 237, 0.1); ``` **Pattern:** Opacity values are lower in light mode (overlays are more subtle) than dark mode. ### Glows & Shadows Color-matched shadows reinforce theme and provide visual hierarchy: **Dark mode glows (purple-based):** ```css /* Subtle glow on cards */ box-shadow: 0 0 40px rgba(168, 85, 247, 0.08); /* Strong glow on focused/interactive elements */ box-shadow: 0 0 15px rgba(56, 189, 248, 0.3); /* Inset shadow for depth */ box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.4); ``` **Light mode glows (darker, adjusted opacity):** ```css /* Subtle glow on cards */ box-shadow: 0 0 40px rgba(124, 58, 237, 0.06); /* Strong glow on focused/interactive elements */ box-shadow: 0 0 8px rgba(124, 58, 237, 0.45); /* Inset shadow for depth */ box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.5); ``` **Guidelines:** - Glows should match accent color (purple in dark mode, darker purple in light) - Opacity is reduced in light mode (less visual "pop" needed) - Glows are used sparingly, only on interactive or emphasized elements ### Border Colors Borders use semantic colors for clarity: ```css /* Subtle borders (between surface and background) */ border: 1px solid var(--border); /* Type-specific borders (article cards, type emphasis) */ border-top: 2px solid var(--type-tech); /* Purple for Tech */ border-left: 2px solid var(--type-quote); /* Green for Quote */ /* Focus/interactive borders */ border: 2px solid var(--accent); /* Purple for focus states */ /* Disabled borders */ border: 1px solid var(--muted); /* Muted gray for disabled */ ``` ### Background Layering: Creating Visual Depth The three background colors create perceived depth: ``` --bg (darkest, farthest) ↓ --bg2 (middle ground) ↓ --surface (closest, elevated) ``` **Usage pattern:** ```css body { background: var(--bg); } /* Page background */ nav { background: var(--bg2); } /* Slightly elevated */ .card { background: var(--surface); } /* Most elevated */ ``` This layering creates visual hierarchy without 3D transforms or z-index manipulation. ### Animation & Gradient Colors Dynamic elements use color gradients: **Progress bar (dark mode):** ```css background: linear-gradient(to right, #a855f7, #00ff88); /* Starts purple, transitions to neon green */ ``` **Progress bar (light mode):** ```css background: linear-gradient(to right, #7c3aed, #008f5a); /* Darker purple to darker green for light backgrounds */ ``` **Hero background glow:** ```css /* Dark mode */ background: radial-gradient(ellipse, rgba(168, 85, 247, 0.07) 0%, transparent 65%); /* Light mode */ background: radial-gradient(ellipse, rgba(124, 58, 237, 0.06) 0%, transparent 65%); ``` ### Interactive States All interactive elements have consistent color transformations: | State | Rule | Example | |---|---|---| | **Default** | Use `--text` or `--accent` | Link text in accent color | | **Hover** | Intensify opacity or shift slightly | `opacity: 0.8` or `color: var(--accent2)` | | **Focus** | Add focus ring in `--accent` | `outline: 2px solid var(--accent)` | | **Active** | Invert colors or add background | `background: var(--accent)`, `color: var(--bg)` | | **Disabled** | Use `--muted` with reduced opacity | `color: var(--muted)`, `opacity: 0.5` | **Pattern for buttons:** ```css .button { color: var(--accent); border: 2px solid var(--accent); transition: all 0.3s ease; } .button:hover { background: var(--accent); color: var(--bg); } .button:focus { outline: 2px solid var(--accent); outline-offset: 2px; } .button:active { opacity: 0.9; } .button:disabled { color: var(--muted); border-color: var(--muted); cursor: not-allowed; } ``` ### Color Naming Conventions **Semantic naming (do this):** - `--bg`, `--surface`, `--border` — describes function, not appearance - `--text`, `--text-dim` — describes role, not color - `--accent`, `--accent2` — describes purpose, not hue - `--type-tech`, `--type-life` — describes article type **Anti-pattern (don't do this):** - `--purple-500`, `--blue-300` — color-based names don't work across themes - `--primary-color`, `--secondary-color` — vague, doesn't explain use case - `--bright-green`, `--dark-purple` — appearance-based, breaks in theme switch **Why semantic naming matters:** - When switching themes, `var(--accent)` automatically becomes the light mode accent - `var(--purple-500)` would be wrong in light mode (no longer "500" saturation) - New developers understand purpose immediately: `--border` is for borders, `--text` is for text ### Deprecated & Reserved Colors These colors are intentionally **not used** to maintain clarity: | Color | Why Reserved | Alternative | |---|---|---| | Pure white (`#ffffff`) | Too harsh on dark background | Use `--text` (`#c4d6e8`) | | Pure black (`#000000`) | Not needed, use `--bg` | Use `--bg` (`#060b10`) | | Red/orange tones | Not part of brand | Use `--type-*` colors instead | | Gray-only colors | Breaks theme switching | Use semantic colors instead | ### Accessibility & Contrast Verification All color pairs are verified for WCAG AA compliance (4.5:1 minimum for normal text, 3:1 for large text): **Dark mode contrast pairs (all ≥4.5:1):** | Foreground | Background | Ratio | Status | |---|---|---|---| | `--text` (#c4d6e8) | `--bg` (#060b10) | 12.3:1 | ✓ AAA | | `--text` (#c4d6e8) | `--surface` (#101e2d) | 11.8:1 | ✓ AAA | | `--text-dim` (#7a9bb8) | `--bg` (#060b10) | 5.2:1 | ✓ AA | | `--accent` (#a855f7) | `--bg` (#060b10) | 5.1:1 | ✓ AA | | `--accent2` (#00ff88) | `--bg` (#060b10) | 7.2:1 | ✓ AAA | **Light mode contrast pairs (all ≥4.5:1):** | Foreground | Background | Ratio | Status | |---|---|---|---| | `--text` (#0d1b2a) | `--bg` (#f0f4f8) | 12.5:1 | ✓ AAA | | `--text` (#0d1b2a) | `--surface` (#d4dff0) | 10.1:1 | ✓ AAA | | `--text-dim` (#2e4a6a) | `--bg` (#f0f4f8) | 6.3:1 | ✓ AAA | | `--accent` (#7c3aed) | `--bg` (#f0f4f8) | 5.5:1 | ✓ AA | | `--accent2` (#008f5a) | `--bg` (#f0f4f8) | 5.8:1 | ✓ AA | **Implementation rule:** Never use color alone to convey information. Always pair with text, icons, or patterns. ### Implementation Examples **Example 1: Article type badge** ```css .article-badge { padding: 0.4rem 1rem; background: var(--type-tech); /* Purple for Tech */ color: var(--bg); /* Dark text on bright background */ font-family: var(--font-mono); font-weight: 600; border-radius: 3px; } /* Works in both dark and light mode automatically */ ``` **Example 2: Card with type-specific border** ```css .article-card { background: var(--surface); border-top: 2px solid var(--type-quote); /* Green for Quote */ box-shadow: 0 0 40px var(--accent-glow); } /* Light mode: border is darker green, glow is adjusted automatically */ ``` **Example 3: Interactive link with theme-aware hover** ```css .article-link { color: var(--accent); text-decoration: none; transition: color 0.3s ease; } .article-link:hover { color: var(--accent2); /* Switch to secondary accent */ } .article-link:focus { outline: 2px solid var(--accent); outline-offset: 2px; } ``` ### Dark Mode Specifics **Why dark mode is default:** - Aligns with "hacker aesthetic" brand voice - Reduces eye strain for extended reading sessions - Better battery life on OLED screens - Better performance in low-light environments **Canvas matrix animation opacity in dark mode:** ```css #matrix-canvas { opacity: 0.13; } /* Subtle enough to not interfere with text */ /* Visible enough to maintain brand presence */ ``` **Dot grid background in dark mode:** ```css background: radial-gradient(circle, rgba(168, 85, 247, 0.07) 1px, transparent 1px); /* Purple dots at 7% opacity — barely visible but present */ ``` ### Light Mode Specifics **Why light mode matters:** - Accessibility for users with light sensitivity to dark backgrounds - Professional context (print, office, daylight reading) - User preference for light interfaces - Complete theme coverage (not just a half-finished afterthought) **Canvas matrix animation opacity in light mode:** ```css #matrix-canvas { opacity: 0.18; } /* More visible in light mode (higher contrast) */ /* Still doesn't interfere with text readability */ ``` **Dot grid background in light mode:** ```css background: radial-gradient(circle, rgba(124, 58, 237, 0.07) 1px, transparent 1px); /* Darker purple dots at 7% opacity on light background */ ``` **Hero name text shadow in light mode:** ```css text-shadow: 0 0 80px rgba(124, 58, 237, 0.12), 0 0 120px rgba(124, 58, 237, 0.05); /* Subtle purple glow behind text for emphasis */ ``` --- ## 3. Typography ### Typography Philosophy Typography reinforces the danix.xyz brand through **three purposeful font families**: | Font | Purpose | Brand Signal | |---|---|---| | **Oxanium** | Display, headings, emphasis | Technical, premium, bold | | **IBM Plex Sans** | Body, prose, UI labels | Approachable, professional, readable | | **JetBrains Mono** | Code, monospace UI, metadata | Developer-focused, technical, terminal-like | This three-font system creates visual hierarchy and guides the reader's eye through content. Each font choice signals its role: headers grab attention, body invites reading, monospace signals technical content. ### Font Family Definitions All fonts are defined as CSS custom properties in `:root`: ```css :root { --font-head: 'Oxanium', sans-serif; --font-body: 'IBM Plex Sans', system-ui, sans-serif; --font-mono: 'JetBrains Mono', 'Courier New', monospace; } ``` **Fallback strategy:** - **Oxanium:** No fallback (distinctive, used only for display where sans-serif backup is acceptable) - **IBM Plex Sans:** Falls back to `system-ui` (ensures readable prose even if font fails to load) - **JetBrains Mono:** Falls back to `Courier New` (preserves monospace and terminal feel) **Implementation pattern:** ```css h1 { font-family: var(--font-head); } /* Display font */ body { font-family: var(--font-body); } /* Body text */ code { font-family: var(--font-mono); } /* Code/terminal */ ``` ### Oxanium: Display Font **Characteristics:** - Geometric, bold, premium sans-serif - Weights: 700 (bold), 800 (extra bold) - High contrast letterforms, distinctive character - Used sparingly for maximum impact **Usage:** | Element | Weight | Size | Line Height | Example | |---|---|---|---|---| | Page titles | 800 | 2.5–3.5rem | 1.2 | `

Article Title

` | | Section headings | 800 | 1.75–2rem | 1.2 | `

Section Name

` | | Card titles | 700 | 1.25–1.5rem | 1.3 | Large card headline | | Badges/labels | 700 | 0.75rem | 1 | Type badges, tags | | Hero text | 800 | 3–4rem | 1.1 | Homepage hero name | | Menu items | 800 | 1.5–2rem | 1.2 | Navigation text | **Size scaling across breakpoints:** ```css /* Mobile (320px+) */ h1 { font-size: 2rem; } /* Tablet (768px+) */ @media (min-width: 768px) { h1 { font-size: 2.5rem; } } /* Desktop (1060px+) */ @media (min-width: 1060px) { h1 { font-size: 3rem; } } /* Or use clamp() for fluid sizing */ h1 { font-size: clamp(2rem, 6vw, 3.5rem); } ``` **Pairing with color:** - Oxanium headings use `--text` (primary text color) for readability - Headings can emphasize with `--accent` color for interactive sections - Never use Oxanium for body text (too bold, reduces readability) ### IBM Plex Sans: Body Font **Characteristics:** - Open, friendly, highly readable sans-serif - Weights: 300 (light), 400 (regular), 600 (semi-bold) - Excellent readability at body text sizes - Default for all prose and UI labels **Usage:** | Element | Weight | Size | Line Height | Example | |---|---|---|---|---| | Body paragraphs | 400 | 1rem | 1.8 | Article text | | Prose descriptions | 400 | 0.95–1rem | 1.8 | Excerpt text | | UI labels | 600 | 0.875–1rem | 1.6 | Button text, form labels | | Metadata | 400 | 0.85rem | 1.6 | Dates, bylines, read time | | Light/italic text | 300 | 1rem | 1.8 | Quotes, captions | **Size scaling across breakpoints:** ```css /* Mobile (320px+) */ body { font-size: 0.95rem; } p { font-size: 0.95rem; } /* Tablet (768px+) */ @media (min-width: 768px) { body { font-size: 1rem; } p { font-size: 1rem; } } /* Desktop (1060px+) */ @media (min-width: 1060px) { body { font-size: 1.05rem; } p { font-size: 1.05rem; } } ``` **Pairing with color:** - Body text always uses `--text` for readability - Secondary text uses `--text-dim` for de-emphasis - UI labels use `--text` or `--accent` depending on context (primary vs interactive) ### JetBrains Mono: Monospace Font **Characteristics:** - Open-source monospace designed for developers - Weights: 400 (regular), 700 (bold) - Highly legible at small sizes - Signals technical content, terminal, code **Usage:** | Element | Weight | Size | Line Height | Example | |---|---|---|---|---| | Inline code | 400 | 0.9em | 1 | `var` or `const` | | Code blocks | 400 | 0.85–0.9rem | 1.6 | `
` |
| Terminal UI | 400 | 0.85–1rem | 1.5 | `$ curl https://...` |
| Metadata | 400 | 0.8–0.85rem | 1.5 | Timestamps, IDs |
| Attribution | 400 | 0.9rem | 1.5 | "— Author Name" |

**Size scaling:**
```css
/* Monospace text scales less aggressively than display fonts */
code { font-size: 0.9em; }        /* Relative to parent */
.terminal { font-size: 0.85rem; } /* Absolute for terminal UI */
```

**Pairing with color:**
- Code blocks use `--accent2` (neon green) for keywords and highlights
- Terminal UI uses `--terminal-prompt` (green) and `--terminal-text` (light)
- Metadata uses `--text-dim` for secondary importance
- Links in monospace use `--accent` or `--type-*` depending on context

### Google Fonts CDN

Fonts are loaded from Google Fonts CDN for performance:

```html

```

**Loading strategy:**
- `display=swap` ensures text is visible immediately (FOUT fallback)
- Only necessary weights are loaded (700, 800 for Oxanium; 300, 400, 600 for IBM Plex; 400, 700 for JetBrains)
- CSS follows HTML to minimize CLS (Cumulative Layout Shift)

**Fallback behavior:**
- If CDN fails, system fonts kick in (fallback chain in font-family)
- Site remains readable even without web fonts
- Performance is not compromised if CDN is slow

### Font Weights: When to Use Each

**Oxanium weights:**
- **700 (Bold):** Small display text, badges, labels, menu items
- **800 (Extra Bold):** Large display text, page titles, hero text

**IBM Plex Sans weights:**
- **300 (Light):** Italic prose, quotes, captions (less weight balances slant)
- **400 (Regular):** Body text, descriptions, most UI text
- **600 (Semi-bold):** UI labels, buttons, emphasized text

**JetBrains Mono weights:**
- **400 (Regular):** Code, terminal, metadata, timestamps
- **700 (Bold):** Keywords in code blocks, emphasized terminal text

**Pattern:**
```css
strong, b { font-weight: 600; }    /* Use semi-bold, not bold */
em, i { font-weight: 300; }        /* Light for italic (compensates for slant) */
code { font-weight: 400; }         /* Regular monospace (bold looks too heavy) */
```

### Font Sizing Scale

Typography uses a modular scale for consistency. Base size is 1rem (17px on desktop):

**Display sizes (Oxanium):**
```
h1: 2rem (32px mobile) → 3rem (51px desktop)
h2: 1.75rem (30px) → 2rem (34px)
h3: 1.5rem (26px) → 1.75rem (30px)
h4: 1.25rem (21px) → 1.5rem (26px)
```

**Body sizes (IBM Plex Sans):**
```
Body text: 0.95rem (16px mobile) → 1.05rem (18px desktop)
Metadata: 0.85rem (14px)
Small: 0.8rem (14px)
```

**Monospace sizes (JetBrains Mono):**
```
Code block: 0.85rem (14px)
Terminal: 0.85rem (14px)
Inline code: 0.9em (relative to parent)
Metadata: 0.8rem (14px)
```

**Responsive sizing with clamp():**
```css
/* Scales fluidly between mobile and desktop */
h1 {
  font-size: clamp(2rem, 6vw, 3.5rem);
  /* Minimum: 32px, Preferred: 6vw, Maximum: 56px */
}

p {
  font-size: clamp(0.95rem, 1.5vw, 1.05rem);
  /* Minimum: 15px, Preferred: 1.5vw, Maximum: 17px */
}
```

### Line Height Hierarchy

Different line heights optimize readability for different content types:

| Content Type | Line Height | Reason |
|---|---|---|
| **Display (h1-h4)** | 1.1–1.3 | Tight spacing, dramatic appearance |
| **Body prose** | 1.8 | Open spacing, comfortable reading |
| **UI labels** | 1.5–1.6 | Compact, grouped with related content |
| **Code blocks** | 1.6 | More open than inline, but not as loose as prose |
| **Metadata** | 1.5 | Compact, supporting information |

**Implementation:**
```css
h1, h2, h3, h4, h5, h6 { line-height: 1.2; }

body, p, article { line-height: 1.8; }

button, label, .ui-text { line-height: 1.5; }

code, pre { line-height: 1.6; }

.metadata, .byline { line-height: 1.5; }
```

### Letter Spacing (Tracking)

Letter spacing is used sparingly to improve readability or create visual distinction:

| Element | Tracking | Reason |
|---|---|---|
| **Display (h1-h4)** | Normal (-0.025em) | Geometric fonts benefit from tight tracking |
| **Body text** | Normal (0) | Default is optimal |
| **UI labels** | 0.05–0.08em | Uppercase labels benefit from open tracking |
| **Monospace metadata** | 0.02em | Subtle, improves legibility |
| **Menu items** | -0.035em | Tight tracking makes bold text more readable |

**Implementation:**
```css
h1, h2, h3, h4 {
  letter-spacing: -0.025em;  /* Oxanium is bold, tighten it */
}

.button, button {
  letter-spacing: 0.08em;    /* Uppercase buttons are more open */
  text-transform: uppercase;
}

nav a {
  letter-spacing: -0.035em;  /* Menu items are bold, tighten */
}

.metadata {
  letter-spacing: 0.02em;    /* Subtle spacing */
}
```

### Text Hierarchy: h1 to h6, Body, Metadata

All text elements follow a strict hierarchy:

**Heading hierarchy (h1 → h6):**
```
h1: 2–3rem, Oxanium 800, line-height 1.2    (Page title, hero)
h2: 1.75–2rem, Oxanium 800, line-height 1.2 (Section title)
h3: 1.5–1.75rem, Oxanium 700, line-height 1.2 (Subsection)
h4: 1.25–1.5rem, Oxanium 700, line-height 1.3 (Subheading)
h5: 1.1–1.25rem, Oxanium 700, line-height 1.3 (Minor heading)
h6: 1rem, Oxanium 700, line-height 1.3      (Small heading)
```

**Body text hierarchy:**
```

: 1rem, IBM Plex Sans 400, line-height 1.8 (Main prose) : IBM Plex Sans 600 (Emphasis within prose) : IBM Plex Sans 300 (Light emphasis, italic) : 0.85rem, IBM Plex Sans 400 (Captions, fine print) ``` **Metadata hierarchy:** ``` .metadata: 0.85rem, IBM Plex Sans 400, line-height 1.5 .byline: 0.9rem, JetBrains Mono 400, line-height 1.5 .timestamp: 0.8rem, JetBrains Mono 400, line-height 1.5 ``` **Code hierarchy:** ``` : 0.9em, JetBrains Mono 400 (inline)

: 0.85rem, JetBrains Mono 400, line-height 1.6 (block)
: Inherits from 
```

### Readable Line Lengths

Prose content has optimal line lengths for reading comfort:

| Content Type | Max Width | Characters | Reason |
|---|---|---|---|
| **Body prose** | 65ch–75ch | 65–75 chars per line | Optimal for single-column reading |
| **Wide layout** | 900px | (auto) | Multi-column or prose + sidebar |
| **Code blocks** | 100% (scrollable) | N/A | Code can be wide; horizontal scroll ok |

**Implementation:**
```css
article {
  max-width: 65ch;    /* ~900px on desktop */
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* Code blocks can be wider */
pre {
  overflow-x: auto;   /* Allow horizontal scroll */
  max-width: 100%;
}
```

**Why line length matters:**
- **Too short** (<50 chars): Eye jumps too much, fatiguing
- **Too long** (>90 chars): Hard to return to start of next line
- **Optimal** (65–75 chars): Comfortable reading speed, minimal eye movement

### Color + Typography: Semantic Pairing

Font choice + color choice communicate information together:

| Combination | Meaning | Example |
|---|---|---|
| Oxanium + `--accent` | Important heading, call to action | Section title with purple accent |
| IBM Plex + `--text` | Primary readable content | Body text |
| IBM Plex + `--text-dim` | Secondary content, supporting info | Byline, date, metadata |
| JetBrains Mono + `--accent2` | Code, technical content | Keywords in terminal |
| JetBrains Mono + `--type-*` | Type-specific metadata | "Quote" label |

**Pattern:**
```css
h2 {
  font-family: var(--font-head);
  color: var(--text);           /* Readable heading */
}

.article-byline {
  font-family: var(--font-body);
  color: var(--text-dim);       /* Secondary info, dimmed */
}

code {
  font-family: var(--font-mono);
  color: var(--accent2);        /* Technical, highlighted */
}
```

### Monospace Usage: Terminal, Code, Metadata

Monospace signals technical content and serves multiple purposes:

**Terminal UI (hacker aesthetic):**
```css
.terminal-prompt { font-family: var(--font-mono); color: var(--terminal-prompt); }
.terminal-output { font-family: var(--font-mono); color: var(--terminal-text); }
/* Size: 0.85–1rem, line-height: 1.5 */
```

**Code blocks:**
```css
pre, code {
  font-family: var(--font-mono);
  color: var(--accent2);        /* Neon green in dark mode */
  background: var(--bg);
  font-size: 0.85rem;
  line-height: 1.6;
}
```

**Metadata (timestamps, IDs, technical info):**
```css
.timestamp, .metadata {
  font-family: var(--font-mono);
  color: var(--text-dim);
  font-size: 0.8rem;
  letter-spacing: 0.02em;
}
```

**Quote attribution:**
```css
.quote-attribution {
  font-family: var(--font-mono);
  font-style: italic;
  color: var(--text-dim);
}
```

### Responsive Typography: Scaling Across Breakpoints

Typography responds to viewport size for optimal readability:

**Mobile (320px+): Smaller, tighter**
```css
/* Base styles (mobile-first) */
h1 { font-size: 2rem; }
body { font-size: 0.95rem; }
```

**Tablet (768px+): Medium, balanced**
```css
@media (min-width: 768px) {
  h1 { font-size: 2.5rem; }
  body { font-size: 1rem; }
}
```

**Desktop (1060px+): Larger, more spacious**
```css
@media (min-width: 1060px) {
  h1 { font-size: 3rem; }
  body { font-size: 1.05rem; }
  article { max-width: 70ch; }
}
```

**Or use fluid sizing with clamp():**
```css
h1 { font-size: clamp(2rem, 6vw, 3.5rem); }
body { font-size: clamp(0.95rem, 1.5vw, 1.05rem); }
```

**Line height can also be responsive:**
```css
h1 { line-height: 1.1; }

@media (min-width: 768px) {
  h1 { line-height: 1.2; }  /* More open on larger screens */
}
```

### Accessibility: Font Size, Contrast, Readability

Typography must support accessibility:

**Minimum sizes:**
- **Body text:** 16px minimum (1rem base is 17px on desktop, 16px on mobile)
- **UI labels:** 14px minimum (0.85rem)
- **Code/metadata:** 12px minimum (0.75rem for emergency cases, normally 14px)

**Never go below:**
- 12px for any readable content
- Users with low vision rely on readable font sizes

**Contrast requirements (paired with colors):**
- All body text must have ≥4.5:1 contrast with background
- All headings must have ≥3:1 contrast with background
- Verified in Section 2 (Color System)

**Readability patterns:**
```css
/* Good: comfortable line height, appropriate size */
p {
  font-size: 1rem;
  line-height: 1.8;
}

/* Bad: too small, too tight */
p {
  font-size: 0.75rem;
  line-height: 1.2;
}
```

**Font smoothing for better rendering:**
```css
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
```

### Typography Quick Reference Table

```
DISPLAY (Headings)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Element      Font        Weight  Size              Line Height
h1           Oxanium     800     2–3rem            1.2
h2           Oxanium     800     1.75–2rem         1.2
h3           Oxanium     700     1.5–1.75rem       1.2
h4           Oxanium     700     1.25–1.5rem       1.3

BODY (Content)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
paragraph    IBM Plex    400     1rem              1.8
strong       IBM Plex    600     1rem              1.8
em           IBM Plex    300     1rem              1.8
small        IBM Plex    400     0.85rem           1.5

CODE & METADATA
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
inline code  JetBrains   400     0.9em             1
code block   JetBrains   400     0.85rem           1.6
timestamp    JetBrains   400     0.8rem            1.5
```

---

## 4. Spacing & Layout

### Spacing Scale: Modular System

All spacing uses a **modular scale** based on 0.5rem increments:

```
0.25rem (4px)   — Minimal gaps
0.5rem  (8px)   — Tight spacing
0.75rem (12px)  — Comfortable spacing
1rem    (16px)  — Default spacing, base unit
1.5rem  (24px)  — Generous spacing
2rem    (32px)  — Large sections
2.5rem  (40px)  — Very large sections
3rem    (48px)  — Hero/major sections
4rem    (64px)  — Between major sections
```

**Why modular scale matters:**
- Creates visual rhythm and consistency
- Makes layouts feel intentional, not random
- Easier to maintain across responsive breakpoints
- Reduces decision fatigue (limited palette vs unlimited options)

**Implementation pattern:**
```css
/* Use custom properties for reusable spacing */
:root {
  --space-xs:   0.5rem;
  --space-sm:   0.75rem;
  --space-md:   1rem;
  --space-lg:   1.5rem;
  --space-xl:   2rem;
  --space-2xl:  3rem;
  --space-3xl:  4rem;
}

/* Apply consistently */
.card { padding: var(--space-md); }
section { margin-top: var(--space-2xl); }
.grid { gap: var(--space-lg); }
```

### Padding Standards

**Container padding (content inset from edges):**

| Size | Mobile (320px) | Tablet (768px) | Desktop (1060px) |
|---|---|---|---|
| Default page padding | 1rem | 1.5rem | 2rem |
| Card/container padding | 1rem | 1.5rem | 1.5rem |
| Section padding (vertical) | 1.5rem | 2rem | 2.5rem |
| Hero section padding | 1.5rem | 2rem | 3rem |

**Implementation:**
```css
/* Page-level container */
.container {
  padding: 0 1rem;
  max-width: 1080px;
  margin: 0 auto;
}

@media (min-width: 768px) {
  .container { padding: 0 1.5rem; }
}

@media (min-width: 1060px) {
  .container { padding: 0 2rem; }
}

/* Cards and elevated containers */
.card {
  padding: 1rem;
  background: var(--surface);
}

@media (min-width: 768px) {
  .card { padding: 1.5rem; }
}

/* Sections with vertical spacing */
section {
  padding: 1.5rem 0;
}

@media (min-width: 768px) {
  section { padding: 2rem 0; }
}
```

### Margin Standards

**Spacing between elements:**

| Element | Bottom Margin | Purpose |
|---|---|---|
| `

` (paragraph) | 1.5rem | Breathing room between paragraphs | | `

` (heading) | 1rem (top), 0.75rem (bottom) | Heading separation | | `

` (subheading) | 0.75rem (top), 0.5rem (bottom) | Subheading separation | | Section | 2–3rem (top) | Between major sections | | Card | 1.5rem (bottom) | Between cards in list | | Last child | 0 (override) | No margin after last element | **Implementation:** ```css p { margin-bottom: 1.5rem; } p:last-child { margin-bottom: 0; } h2 { margin-top: 1rem; margin-bottom: 0.75rem; } h3 { margin-top: 0.75rem; margin-bottom: 0.5rem; } section { margin-top: 2rem; } section:first-child { margin-top: 0; } .card { margin-bottom: 1.5rem; } .card:last-child { margin-bottom: 0; } ``` ### Gap & Gap-x/Gap-y: Flexbox & Grid Spacing Grid and flexbox use `gap` instead of margins for internal spacing: **Grid layouts:** ```css .article-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem; /* Equal spacing on all sides */ } @media (min-width: 768px) { .article-grid { gap: 2rem; } } ``` **Flexbox layouts:** ```css .sidebar-layout { display: flex; gap: 2rem; /* Space between flex items */ } /* Responsive: stack on mobile */ @media (max-width: 768px) { .sidebar-layout { flex-direction: column; gap: 1.5rem; } } ``` **Directional gaps:** ```css .social-icons { display: flex; gap-x: 1rem; /* Horizontal gap between icons */ gap-y: 0.5rem; /* Vertical gap if wrapped */ flex-wrap: wrap; } ``` **Why gap > margins:** - Avoids margin collapse issues - Cleaner calculation (no "last child" special case) - Easier to adjust globally - Works consistently in flex and grid ### Container Max-Widths **Different max-widths for different content types:** | Container | Max-Width | Purpose | Breakpoint | |---|---|---|---| | Article prose | 65ch (~900px) | Optimal reading length | 768px+ | | Timeline layout | 1080px | Content + sidebar | 1060px+ | | Masonry grid | 1200px | Multi-column layout | 1060px+ | | Hero section | 100vw (full-width) | Full bleed | All | | Code blocks | 100% (overflow-x) | Scrollable if needed | All | **Implementation:** ```css /* Article prose (optimal reading) */ article { max-width: 65ch; margin: 0 auto; padding: 0 1.5rem; } /* Timeline/wide layout */ .timeline { max-width: 1080px; margin: 0 auto; padding: 0 1.5rem; } /* Masonry grid (wider) */ .masonry { max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; } /* Hero (full-width) */ .hero { width: 100vw; max-width: 100%; overflow: hidden; } ``` ### Responsive Breakpoints Three primary breakpoints ensure content adapts gracefully: ``` Mobile: 320px – 767px (1 column, stacked) Tablet: 768px – 1059px (2 columns, sidebar collapses) Desktop: 1060px+ (full layout, sidebar fixed) ``` **Breakpoint definitions:** ```css /* Mobile-first: base styles for 320px+ */ /* No media query needed */ /* Tablet breakpoint */ @media (min-width: 768px) { /* Tablet-specific styles */ } /* Desktop breakpoint */ @media (min-width: 1060px) { /* Desktop-specific styles */ } ``` **Why these specific breakpoints:** - **320px:** iPhone SE, smallest mobile devices - **768px:** iPad, standard tablets (also common breakpoint) - **1060px:** Sidebar appears on desktop (allows fixed sidebar + content) **Avoid breakpoints:** Don't add 480px, 600px, 900px, etc. Stick to the three main breakpoints for consistency. ### Gutter & Padding by Breakpoint How padding changes to maintain visual hierarchy across sizes: **Page-level padding (from edge to content):** ```css /* Mobile: 1rem on each side = 2rem total */ body { padding: 0 1rem; } /* Tablet: 1.5rem on each side = 3rem total */ @media (min-width: 768px) { body { padding: 0 1.5rem; } } /* Desktop: 2rem on each side = 4rem total */ @media (min-width: 1060px) { body { padding: 0 2rem; } } ``` **Card padding (internal spacing):** ```css /* Mobile: tighter padding */ .card { padding: 1rem; } /* Tablet & Desktop: more breathing room */ @media (min-width: 768px) { .card { padding: 1.5rem; } } ``` **Section spacing (between major sections):** ```css /* Mobile: compact */ section { margin-top: 1.5rem; } /* Tablet: generous */ @media (min-width: 768px) { section { margin-top: 2rem; } } /* Desktop: very generous */ @media (min-width: 1060px) { section { margin-top: 3rem; } } ``` **Why padding changes:** - Mobile devices have limited screen real estate (preserve width) - Tablets can afford more horizontal padding - Desktop layouts can be more spacious without feeling cramped ### Grid Systems: CSS Grid for Layouts **Article card grid (masonry-style):** ```css .article-grid { display: grid; grid-template-columns: 1fr; /* Mobile: 1 column */ gap: 1.5rem; } @media (min-width: 768px) { .article-grid { grid-template-columns: repeat(2, 1fr); /* Tablet: 2 columns */ } } @media (min-width: 1060px) { .article-grid { grid-template-columns: repeat(3, 1fr); /* Desktop: 3 columns */ } } ``` **Auto-fill grid (responsive without breakpoints):** ```css .photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem; } /* Automatically creates columns based on available space */ ``` **Timeline layout (2-column with sidebar):** ```css .timeline-container { display: grid; grid-template-columns: 1fr; /* Mobile: stacked */ gap: 2rem; } @media (min-width: 1060px) { .timeline-container { grid-template-columns: 1fr 300px; /* Desktop: content + sidebar */ } } ``` **When to use grid:** - Multi-column layouts (masonry, card grids) - Layouts with explicit structure (header, main, sidebar, footer) - Responsive layouts that need exact column control ### Flexbox Patterns: When to Use vs Grid **Use flexbox for:** - Navigation menus (horizontal or vertical) - Button groups - Icon rows - Linear layouts (1D spacing) **Use grid for:** - Card layouts (2D) - Page layout (header, main, sidebar) - Masonry/gallery layouts - Multi-column content **Flexbox example (navigation):** ```css .nav-menu { display: flex; gap: 1.5rem; align-items: center; } .nav-menu a { padding: 0.5rem 1rem; } ``` **Grid example (page layout):** ```css body { display: grid; grid-template-columns: 1fr; /* Mobile */ grid-template-rows: auto 1fr auto; } @media (min-width: 1060px) { body { grid-template-columns: 1fr 300px; /* Desktop */ } } ``` ### Z-Index Stacking: Layering Elements **Z-index scale (reserved values):** ``` 1000+ : Modals, overlays (highest) 100 : Fixed navigation, sidebars 10 : Dropdowns, tooltips 1 : Content, cards 0 : Background, images -1 : Canvas/matrix animation (lowest) ``` **Implementation:** ```css /* Background elements */ #matrix-canvas { z-index: 0; } body::before { z-index: 0; } /* Content layers */ article { position: relative; z-index: 1; } .card { z-index: 1; } /* Interactive elements */ .dropdown { z-index: 10; } .tooltip { z-index: 10; } /* Fixed UI */ nav { position: fixed; z-index: 100; } .sidebar { position: fixed; z-index: 100; } #scroll-progress { z-index: 9999; } /* Overlays */ .modal { z-index: 1000; } .modal-backdrop { z-index: 999; } ``` **Rule: Don't use arbitrary z-index values.** Always use the reserved scale above. ### Hero Section: Height, Padding, Content Alignment **Hero section structure:** ```html

Article Title

``` **Hero section CSS:** ```css .hero { position: relative; min-height: 60vh; /* Mobile */ padding: 2rem; display: flex; align-items: flex-end; justify-content: center; overflow: hidden; } @media (min-width: 768px) { .hero { min-height: 60vh; padding: 3rem; } } @media (min-width: 1060px) { .hero { min-height: 60vh; padding: 4rem; } } /* Background image (full bleed) */ .hero-background { position: absolute; inset: 0; z-index: 0; } .hero-image { width: 100%; height: 100%; object-fit: cover; object-position: center; } /* Dark overlay for text readability */ .hero-overlay { position: absolute; inset: 0; background: rgba(6, 11, 16, 0.65); z-index: 1; } /* Content positioning */ .hero-content { position: relative; z-index: 2; text-align: center; max-width: 860px; } ``` **Hero responsiveness:** - Height: 60vh on all screen sizes (full viewport height portion) - Padding increases at larger breakpoints - Text remains centered and readable - Image maintains aspect ratio ### Sidebar Layout: Desktop Fixed vs Mobile Collapsed **Desktop sidebar (fixed on right):** ```css @media (min-width: 1060px) { .page-layout { display: grid; grid-template-columns: 1fr 300px; gap: 2rem; } .sidebar { position: fixed; right: calc(50vw - 540px); /* Center relative to max-width container */ width: 300px; top: 80px; /* Below fixed nav */ } } ``` **Mobile sidebar (horizontal strip or hidden):** ```css @media (max-width: 1059px) { .sidebar { position: static; /* Not fixed */ width: 100%; margin-top: 2rem; } .sidebar-content { display: flex; /* Horizontal strip */ gap: 1rem; flex-wrap: wrap; justify-content: center; } } ``` **Sidebar visibility trigger (IntersectionObserver in JS):** - Show sidebar when hero scrolls out of view - Hide sidebar when footer is visible - Sidebar is always accessible on mobile (no hiding) ### Article Card Spacing **Card structure and spacing:** ```css .article-card { padding: 1rem; background: var(--surface); border-top: 2px solid var(--type-tech); border-radius: 4px; display: flex; flex-direction: column; gap: 0.75rem; /* Space between card elements */ } @media (min-width: 768px) { .article-card { padding: 1.5rem; } } /* Card image */ .card-image { width: 100%; height: auto; aspect-ratio: 16 / 9; /* Or 1/1 for thumbnails */ object-fit: cover; border-radius: 2px; } /* Card metadata */ .card-meta { display: flex; gap: 0.5rem; font-size: 0.85rem; color: var(--text-dim); } /* Card title and excerpt */ .card-title { font-family: var(--font-head); font-size: 1.25rem; margin: 0; } .card-excerpt { font-family: var(--font-body); font-size: 0.95rem; line-height: 1.6; color: var(--text); margin: 0; } ``` ### Negative Space: White Space as Design Element Negative space (empty space) is as important as filled space: **Creating breathing room:** ```css /* Large section margins create visual rest */ section { margin-top: 3rem; /* White space above section */ margin-bottom: 3rem; /* White space below section */ } /* Generous padding in cards feels premium */ .card { padding: 2rem; /* More padding = more breathing room */ } /* Open line height improves readability and adds white space */ p { line-height: 1.8; } /* vs 1.4, which feels cramped */ ``` **When to use white space:** - Between sections (separate ideas) - Around important content (draw attention) - In cards (create visual rest) - After headings (connect to paragraph below) **Rule: If it feels cramped, add more white space.** Negative space is never wasted. ### Responsive Collapse Patterns **What stacks at what viewport:** | Element | Mobile (320px) | Tablet (768px) | Desktop (1060px) | |---|---|---|---| | Navigation | Hamburger menu | Hamburger menu | Horizontal menu | | Sidebar | Hidden/below content | Below content | Fixed right | | Grid layout | 1 column | 2 columns | 3 columns | | Hero height | 50vh | 60vh | 60vh | | Padding (page) | 1rem | 1.5rem | 2rem | **Hamburger menu pattern:** ```css /* Mobile: menu is vertical and hidden */ .nav-menu { position: fixed; left: 0; top: 0; flex-direction: column; display: none; /* Hidden by default */ } .hamburger.active ~ .nav-menu { display: flex; /* Visible when hamburger is active */ } /* Desktop: menu is horizontal and always visible */ @media (min-width: 1060px) { .nav-menu { position: static; flex-direction: row; display: flex; /* Always visible */ } .hamburger { display: none; } /* Hamburger not needed */ } ``` ### Fixed Elements: Navigation, Progress Bar, Sidebars **Fixed navigation bar:** ```css nav { position: fixed; top: 0; left: 0; width: 100%; z-index: 100; background: rgba(6, 11, 16, 0.88); backdrop-filter: blur(14px); padding: 1rem 1.2rem; display: flex; justify-content: space-between; align-items: center; } /* Add padding to body to account for fixed nav */ body { padding-top: 60px; /* Height of nav */ } ``` **Scroll progress bar:** ```css #scroll-progress { position: fixed; top: 0; left: 0; height: 2px; width: 0%; background: linear-gradient(to right, var(--accent), var(--accent2)); z-index: 9999; /* Above all content */ pointer-events: none; /* Doesn't interfere with interaction */ } ``` **Fixed sidebar (desktop only):** ```css @media (min-width: 1060px) { .sidebar { position: fixed; right: calc(50vw - 540px); top: 80px; /* Below nav */ width: 300px; z-index: 100; } } ``` ### Overflow & Scrolling: Managing Content That Exceeds Space **Code blocks (horizontal scroll):** ```css pre { overflow-x: auto; /* Scroll if code is wide */ overflow-y: hidden; /* No vertical scroll */ max-width: 100%; padding: 1rem; } ``` **Long URLs in text (wrap instead of scroll):** ```css a { word-break: break-all; /* Break long URLs */ overflow-wrap: break-word; /* Wrap at word boundaries */ } ``` **Images (never overflow):** ```css img { max-width: 100%; height: auto; display: block; } ``` **Prevent horizontal scroll on body:** ```css html, body { overflow-x: hidden; /* No horizontal scroll */ width: 100%; } ``` **Use overflow-hidden sparingly:** - Good: Hero sections, fixed sidebars - Bad: Main content (users need to scroll to see content) ### Spacing & Layout Quick Reference ``` SPACING SCALE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.5rem (8px) — Tight spacing 1rem (16px) — Default, base unit 1.5rem (24px) — Generous 2rem (32px) — Large 3rem (48px) — Very large BREAKPOINTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Mobile: 320px – 767px Tablet: 768px – 1059px Desktop: 1060px+ CONTAINER MAX-WIDTHS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Article: 65ch (~900px) Timeline: 1080px Masonry: 1200px Hero: 100vw (full-width) Z-INDEX SCALE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1000: Modals, overlays 100: Fixed nav, sidebars 10: Dropdowns, tooltips 1: Content, cards 0: Background, images -1: Canvas animation ``` --- ## 5. Components ### Button Styles: Primary, Secondary, Disabled **Button structure:** ```html ``` **Primary button (main action):** ```css .btn.btn-primary { padding: 0.75rem 1.5rem; background: var(--accent); /* Purple */ color: var(--bg); /* Dark text */ border: none; border-radius: 4px; font-family: var(--font-body); font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; text-decoration: none; display: inline-block; } .btn.btn-primary:hover { background: var(--accent2); /* Switch to green */ color: var(--text); box-shadow: 0 0 15px rgba(0, 255, 136, 0.3); } .btn.btn-primary:focus { outline: 2px solid var(--accent); outline-offset: 2px; } .btn.btn-primary:active { opacity: 0.9; transform: scale(0.98); } ``` **Secondary button (alternative action):** ```css .btn.btn-secondary { padding: 0.75rem 1.5rem; background: transparent; color: var(--accent); border: 2px solid var(--accent); border-radius: 4px; font-family: var(--font-body); font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; } .btn.btn-secondary:hover { background: var(--accent); color: var(--bg); } .btn.btn-secondary:focus { outline: 2px solid var(--accent); outline-offset: 2px; } ``` **Disabled button:** ```css .btn:disabled { opacity: 0.5; cursor: not-allowed; background: var(--muted); color: var(--text-dim); border-color: var(--muted); } .btn:disabled:hover { background: var(--muted); box-shadow: none; } ``` **Button sizing:** ```css /* Small button */ .btn.btn-sm { padding: 0.5rem 1rem; font-size: 0.85rem; } /* Large button */ .btn.btn-lg { padding: 1rem 2rem; font-size: 1.1rem; } /* Full-width button */ .btn.btn-block { width: 100%; display: block; } ``` ### Badge Styles: Type Badges **Badge structure:** ```html TECH LIFE QUOTE LINK PHOTO ``` **Badge base styling:** ```css .badge { display: inline-block; padding: 0.4rem 1rem; font-family: var(--font-mono); font-size: 0.7rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; border-radius: 3px; color: var(--bg); /* Always dark text on bright badge */ white-space: nowrap; } /* Type-specific badge colors */ .badge.badge-tech { background: var(--type-tech); } .badge.badge-life { background: var(--type-life); } .badge.badge-quote { background: var(--type-quote); } .badge.badge-link { background: var(--type-link); } .badge.badge-photo { background: var(--type-photo); } ``` **Badge hover state (optional):** ```css .badge { transition: transform 0.2s ease; } .badge:hover { transform: scale(1.05); } ``` **Badge sizing variants:** ```css .badge.badge-sm { padding: 0.25rem 0.75rem; font-size: 0.65rem; } .badge.badge-lg { padding: 0.5rem 1.25rem; font-size: 0.8rem; } ``` ### Card Layouts: Article, Photo, Content Cards **Article card (for lists/grids):** ```html
...
TECH 2026-04-08

Article Title

Brief description of article content...

Read more →
``` **Article card styling:** ```css .card { background: var(--surface); border-radius: 4px; overflow: hidden; transition: all 0.3s ease; display: flex; flex-direction: column; } .card.card-article { border-top: 2px solid var(--type-tech); /* Type color varies */ box-shadow: 0 0 40px var(--accent-glow); } .card-image { width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; } .card-body { padding: 1.5rem; display: flex; flex-direction: column; gap: 0.75rem; } .card-meta { display: flex; align-items: center; gap: 0.75rem; font-size: 0.8rem; color: var(--text-dim); } .card-title { font-family: var(--font-head); font-size: 1.25rem; font-weight: 700; margin: 0; line-height: 1.3; } .card-excerpt { font-family: var(--font-body); font-size: 0.95rem; line-height: 1.6; color: var(--text); margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .card-link { color: var(--accent); text-decoration: none; font-weight: 600; transition: color 0.3s ease; align-self: flex-start; } .card-link:hover { color: var(--accent2); } .card:hover { transform: translateY(-4px); box-shadow: 0 0 60px var(--accent-glow); } ``` **Photo card (gallery):** ```css .card.card-photo { border-radius: 0; /* Sharp corners for photo galleries */ overflow: hidden; } .card.card-photo img { width: 100%; height: 300px; object-fit: cover; } .card.card-photo:hover img { transform: scale(1.05); } ``` **Content card (generic container):** ```css .card.card-content { padding: 2rem; border: 1px solid var(--border); border-radius: 4px; } ``` ### Navigation Patterns: Hamburger, Main Nav, Breadcrumbs **Hamburger menu (mobile):** ```html ``` **Hamburger styling:** ```css .hamburger { background: none; border: none; cursor: pointer; display: flex; flex-direction: column; gap: 0.4rem; padding: 0.5rem; transition: all 0.3s ease; z-index: 101; /* Above menu overlay */ } .hamburger span { display: block; width: 24px; height: 2px; background: var(--accent); transition: all 0.3s ease; border-radius: 1px; } /* Active state (X icon) */ .hamburger.active span:nth-child(1) { transform: rotate(45deg) translateY(10px); } .hamburger.active span:nth-child(2) { opacity: 0; } .hamburger.active span:nth-child(3) { transform: rotate(-45deg) translateY(-10px); } /* Hide on desktop */ @media (min-width: 1060px) { .hamburger { display: none; } } ``` **Main navigation:** ```html ``` **Main nav styling:** ```css nav { position: fixed; top: 0; left: 0; width: 100%; padding: 1rem 1.2rem; display: flex; align-items: center; justify-content: space-between; background: rgba(6, 11, 16, 0.88); backdrop-filter: blur(14px); z-index: 100; } .nav-logo { font-family: var(--font-head); font-size: 1.25rem; font-weight: 800; color: var(--text); text-decoration: none; } .nav-links { display: none; /* Hidden on mobile */ list-style: none; gap: 2rem; } @media (min-width: 1060px) { .nav-links { display: flex; } } .nav-links a { color: var(--text); text-decoration: none; transition: color 0.3s ease; position: relative; } .nav-links a::after { content: ''; position: absolute; bottom: -0.2rem; left: 0; width: 0; height: 2px; background: var(--accent); transition: width 0.3s ease; } .nav-links a:hover::after { width: 100%; } ``` **Breadcrumb navigation:** ```html ``` **Breadcrumb styling:** ```css .breadcrumb { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-dim); margin-bottom: 1.5rem; } .breadcrumb a { color: var(--text-dim); text-decoration: none; transition: color 0.3s ease; } .breadcrumb a:hover { color: var(--accent); } .breadcrumb-current { color: var(--accent); font-weight: 600; } .breadcrumb-separator { color: var(--border); } ``` ### Social Sharing Sidebar **Desktop (fixed right):** ```html ``` **Sidebar styling:** ```css .share-sidebar { position: fixed; right: calc(50vw - 530px); /* Centered relative to container */ top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 1rem; z-index: 50; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } /* Show sidebar when in viewport */ .share-sidebar.is-visible { opacity: 1; pointer-events: auto; } /* Mobile: horizontal strip */ @media (max-width: 1059px) { .share-sidebar { position: static; flex-direction: row; justify-content: center; width: 100%; margin: 2rem 0; opacity: 1; pointer-events: auto; transform: none; } } .share-btn { width: 40px; height: 40px; border-radius: 50%; background: var(--surface); border: 1px solid var(--border); color: var(--text); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; position: relative; } .share-btn:hover { background: var(--accent); color: var(--bg); transform: scale(1.1); } /* Tooltip on hover */ .share-btn::before { content: attr(data-tooltip); position: absolute; right: calc(100% + 10px); white-space: nowrap; background: var(--accent); color: var(--bg); padding: 0.5rem 0.75rem; border-radius: 4px; font-size: 0.75rem; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } .share-btn:hover::before { opacity: 1; } @media (max-width: 1059px) { .share-btn::before { display: none; } /* No tooltips on mobile */ } ``` ### Progress Bar: Scroll-Driven Indicator **Progress bar HTML:** ```html
``` **Progress bar styling:** ```css #scroll-progress { position: fixed; top: 0; left: 0; height: 2px; width: 0%; background: linear-gradient(to right, var(--accent), var(--accent2)); box-shadow: 0 0 8px rgba(168, 85, 247, 0.6); z-index: 9999; pointer-events: none; transition: width 0.1s ease; } ``` **Progress bar JavaScript:** ```javascript function updateProgressBar() { const windowHeight = document.documentElement.scrollHeight - window.innerHeight; const scrolled = window.scrollY; const progress = (scrolled / windowHeight) * 100; document.getElementById('scroll-progress').style.width = progress + '%'; } window.addEventListener('scroll', updateProgressBar); ``` ### Article Metadata: Bylines, Dates, Read Time, Type **Metadata structure:** ```html ``` **Metadata styling:** ```css .article-meta { display: flex; align-items: center; gap: 1.5rem; padding: 1rem 0; border-bottom: 1px solid var(--border); font-size: 0.8rem; flex-wrap: wrap; } .article-date, .article-read-time, .article-author { font-family: var(--font-mono); color: var(--text-dim); letter-spacing: 0.02em; } .article-author { font-weight: 600; color: var(--text); } .article-date::before { content: '📅 '; } .article-read-time::before { content: '⏱️ '; } .article-author::before { content: '✍️ '; } ``` ### Quote Styling: Large Text, Attribution, Decorative Marks **Quote structure:** ```html
"
"
— Author Name, Source

Your commentary about the quote...

``` **Quote styling:** ```css .quote-section { position: relative; text-align: center; margin: 3rem 0; } .quote-marks { font-family: var(--font-head); font-size: 5rem; font-weight: 800; color: var(--quote-mark-color); line-height: 1; display: block; } .quote-marks-open { margin-bottom: -1rem; } .quote-marks-close { margin-top: -1rem; } .featured-quote { font-family: var(--font-body); font-size: 1.8rem; font-weight: 400; font-style: italic; color: var(--text); line-height: 1.6; margin: 0; border-left: none; padding-left: 0; } .quote-attribution { text-align: center; font-family: var(--font-mono); font-size: 1rem; color: var(--text-dim); font-style: italic; margin: 2rem 0 3rem 0; letter-spacing: 0.02em; } .quote-commentary { padding-top: 2rem; border-top: 1px solid var(--border); } .quote-commentary p { font-family: var(--font-body); color: var(--text); line-height: 1.8; margin-bottom: 1.5rem; } .quote-commentary p:last-child { margin-bottom: 0; } ``` ### Terminal/Code Styling: Prompts, Code Blocks, Monospace UI **Terminal session:** ```html
$ curl https://example.com
Title: Example
``` **Terminal styling:** ```css .terminal-session { background: var(--terminal-bg); border: 1px solid var(--border); border-radius: 4px; padding: 0; font-family: var(--font-mono); overflow: hidden; margin: 2rem 0; } .terminal-header { background: var(--bg2); padding: 1rem; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 0.5rem; } .terminal-prompt { color: var(--terminal-prompt); font-weight: 700; } .terminal-command { color: var(--terminal-text); } .terminal-output { padding: 1.5rem; background: var(--terminal-bg); } .terminal-line { color: var(--terminal-text); margin-bottom: 0.75rem; line-height: 1.6; } .terminal-line strong { color: var(--terminal-prompt); font-weight: 700; } ``` **Code block:** ```css code { font-family: var(--font-mono); color: var(--accent2); background: rgba(168, 85, 247, 0.1); padding: 0.2rem 0.4rem; border-radius: 2px; font-size: 0.9em; } pre { background: var(--bg); color: var(--accent2); padding: 1.5rem; border-radius: 4px; overflow-x: auto; margin: 2rem 0; font-family: var(--font-mono); font-size: 0.85rem; line-height: 1.6; } pre code { background: none; color: inherit; padding: 0; border-radius: 0; } ``` ### Link Styling: Hover, Focus, Visited States **Link structure:** ```html Read more Primary link Secondary link ``` **Link styling:** ```css a { color: var(--accent); text-decoration: none; transition: color 0.3s ease; position: relative; } a:hover { color: var(--accent2); text-decoration: underline; } a:focus { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; } a:visited { color: var(--accent); /* Don't change visited links (privacy) */ } /* Primary link (button-like) */ .link-primary { padding: 0.5rem 1rem; border: 2px solid var(--accent); border-radius: 4px; display: inline-block; } .link-primary:hover { background: var(--accent); color: var(--bg); text-decoration: none; } /* Secondary link (text-only) */ .link-secondary { font-weight: 600; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; } .link-secondary:hover { color: var(--accent2); } /* External link indicator */ a[target="_blank"]::after { content: ' ↗️'; font-size: 0.85em; } ``` ### Forms: Input Fields, Labels, Focus States **Form structure:** ```html
``` **Form styling:** ```css .form { display: flex; flex-direction: column; gap: 1.5rem; } .form-label { font-family: var(--font-body); font-size: 0.95rem; font-weight: 600; color: var(--text); display: block; margin-bottom: 0.5rem; } .form-input, .form-textarea { padding: 0.75rem 1rem; border: 1px solid var(--border); background: var(--bg2); color: var(--text); font-family: var(--font-mono); font-size: 0.95rem; border-radius: 4px; transition: all 0.3s ease; } .form-input:focus, .form-textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 8px rgba(168, 85, 247, 0.3); background: var(--surface); } .form-input::placeholder, .form-textarea::placeholder { color: var(--text-dim); } .form-textarea { resize: vertical; min-height: 120px; } ``` ### Modal/Overlay Patterns **Modal structure:** ```html ``` **Modal styling:** ```css .modal-backdrop { position: fixed; inset: 0; background: rgba(6, 11, 16, 0.95); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity 0.3s ease; } .modal-backdrop.active { display: flex; opacity: 1; } .modal { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 2rem; max-width: 500px; width: 90%; position: relative; box-shadow: 0 0 40px rgba(168, 85, 247, 0.2); } .modal-close { position: absolute; top: 1rem; right: 1rem; background: none; border: none; font-size: 2rem; color: var(--text-dim); cursor: pointer; transition: color 0.3s ease; } .modal-close:hover { color: var(--text); } .modal-title { margin-top: 0; margin-bottom: 1rem; color: var(--text); } .modal-content { margin-bottom: 1.5rem; color: var(--text); line-height: 1.6; } /* Close modal on backdrop click */ .modal-backdrop.active:not(:has(.modal:hover)) { cursor: pointer; } ``` ### Lightbox/Gallery: Photo Viewer, Navigation, Keyboard Controls **Lightbox structure:** ```html ``` **Lightbox styling:** ```css .lightbox { position: fixed; inset: 0; display: none; align-items: center; justify-content: center; z-index: 2000; opacity: 0; transition: opacity 0.3s ease; } .lightbox.active { display: flex; opacity: 1; } .lightbox-backdrop { position: absolute; inset: 0; background: rgba(6, 11, 16, 0.95); z-index: 1; } .lightbox-content { position: relative; z-index: 2; max-width: 90vw; max-height: 90vh; display: flex; align-items: center; justify-content: center; } .lightbox-image { max-width: 100%; max-height: 100%; object-fit: contain; } .lightbox-close { position: absolute; top: 1rem; right: 1rem; z-index: 3; background: none; border: none; font-size: 3rem; color: var(--text); cursor: pointer; } .lightbox-prev, .lightbox-next { position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; background: transparent; border: none; font-size: 2.5rem; color: var(--accent); cursor: pointer; padding: 1rem; transition: color 0.3s ease; } .lightbox-prev:hover, .lightbox-next:hover { color: var(--accent2); } .lightbox-prev { left: 1rem; } .lightbox-next { right: 1rem; } .lightbox-info { position: absolute; bottom: 1rem; left: 50%; transform: translateX(-50%); z-index: 3; background: rgba(6, 11, 16, 0.8); padding: 0.5rem 1rem; border-radius: 4px; color: var(--text); font-family: var(--font-mono); } .lightbox-counter { font-size: 0.9rem; } ``` ### Footer: Content Structure, Spacing, Links **Footer structure:** ```html ``` **Footer styling:** ```css .site-footer { background: var(--bg2); border-top: 1px solid var(--border); padding: 3rem 2rem 2rem; margin-top: 4rem; } .footer-content { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 2rem; max-width: 1080px; margin: 0 auto 2rem; } .footer-section h3, .footer-section h4 { font-family: var(--font-head); font-size: 1rem; margin-bottom: 1rem; color: var(--text); } .footer-section p { color: var(--text-dim); line-height: 1.6; font-size: 0.95rem; } .footer-section ul { list-style: none; padding: 0; margin: 0; } .footer-section li { margin-bottom: 0.5rem; } .footer-section a { color: var(--text-dim); text-decoration: none; transition: color 0.3s ease; } .footer-section a:hover { color: var(--accent); } .footer-bottom { text-align: center; padding-top: 2rem; border-top: 1px solid var(--border); color: var(--text-dim); font-size: 0.85rem; } .footer-bottom p { margin: 0; } ``` ### Loading/Error States: Skeleton Screens, Error Messages, Spinners **Loading skeleton:** ```html
``` **Skeleton styling:** ```css @keyframes shimmer { 0% { background-position: -1000px 0; } 100% { background-position: 1000px 0; } } .skeleton { background: linear-gradient(90deg, var(--bg2) 25%, var(--surface) 50%, var(--bg2) 75%); background-size: 1000px 100%; animation: shimmer 2s infinite; } .skeleton-image { width: 100%; height: 200px; border-radius: 4px; margin-bottom: 1rem; } .skeleton-line { height: 12px; border-radius: 4px; margin-bottom: 0.75rem; } .skeleton-line:last-child { margin-bottom: 0; } ``` **Error message:** ```html
⚠️

Something went wrong. Please try again.

``` **Error styling:** ```css .error-message { background: rgba(236, 72, 153, 0.1); border: 1px solid var(--type-photo); border-radius: 4px; padding: 1.5rem; margin: 1rem 0; display: flex; align-items: center; gap: 1rem; } .error-icon { font-size: 1.5rem; } .error-message p { margin: 0; color: var(--text); } ``` **Spinner/loading indicator:** ```css @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .spinner { width: 40px; height: 40px; border: 4px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 1s linear infinite; } ``` --- ## 6. Icons & Icon Fonts ### Philosophy: Premium Monochrome Design Icons are part of the visual language, not decorative afterthoughts. The danix.xyz icon system uses **Feather Icons** for consistency: **Why Feather Icons:** - Minimalist aesthetic, premium appearance (not clipart-style) - Monochrome by default (pairs with any color) - Perfect stroke weight (1.5px) for clarity without heaviness - Comprehensive library (290+ icons covering common use cases) - Themeable via CSS (color, stroke, size all controllable) - Responsive at any size (crisp from 16px to 48px+) **Design principle:** Icons should be subtle, functional, and never draw more attention than the content they support. ### Icon Font Setup **Feather Icons CDN:** ```html ``` Or via npm for bundled projects: ```bash npm install feather-icons ``` **Icon markup:** ```html ``` **JavaScript initialization (required for SVG rendering):** ```javascript ``` Or in vanilla JS: ```javascript document.addEventListener('DOMContentLoaded', () => { feather.replace(); }); ``` **Why SVG not font:** - SVG icons are crisp at any size - Better accessibility (semantic SVG) - Easier to style individually - No font loading issues or fallbacks needed ### Icon Sizing Scale Icons scale responsively based on context: ``` 16px (0.85rem) — Small metadata, secondary actions 20px (1.15rem) — Default, most contexts 24px (1.4rem) — Buttons, interactive elements 32px (1.9rem) — Large buttons, featured actions 48px (2.8rem) — Hero elements, major CTAs ``` **Implementation:** ```css /* Small icons (metadata, secondary) */ .icon-sm { width: 16px; height: 16px; stroke-width: 1.5; } /* Default icon size */ .icon { width: 20px; height: 20px; stroke-width: 1.5; } /* Large icons (buttons) */ .icon-lg { width: 24px; height: 24px; stroke-width: 1.5; } /* Extra large (featured) */ .icon-xl { width: 32px; height: 32px; stroke-width: 1.5; } /* Responsive sizing with clamp() */ .icon { width: clamp(20px, 5vw, 32px); height: clamp(20px, 5vw, 32px); stroke-width: 1.5; } ``` ### Icon Colors: Context-Dependent Theming Icons inherit or explicitly use colors based on context. **Never use hard-coded colors—always use CSS custom properties:** **Primary icon (text color):** ```css .icon-primary { color: var(--text); stroke: var(--text); } .icon-primary-dim { color: var(--text-dim); stroke: var(--text-dim); } ``` **Interactive icon (accent color):** ```css .icon-accent { color: var(--accent); stroke: var(--accent); } .icon-accent2 { color: var(--accent2); stroke: var(--accent2); } ``` **Type-specific icon (article color):** ```css .icon-type-tech { color: var(--type-tech); stroke: var(--type-tech); } .icon-type-quote { color: var(--type-quote); stroke: var(--type-quote); } /* Works for all 5 article types */ ``` **Disabled/muted icon:** ```css .icon-muted { color: var(--muted); stroke: var(--muted); opacity: 0.6; } ``` **Pattern for automatic theming:** ```css [data-feather] { color: currentColor; /* Inherit from parent text color */ stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; /* Feather style */ stroke-linejoin: round; /* Feather style */ } ``` ### Icon Stroke: Weight and Line Style **Feather icons use consistent stroke properties:** ```css [data-feather] { stroke: currentColor; stroke-width: 1.5; /* Feather default: not too thin, not too thick */ stroke-linecap: round; /* Rounded line endings (premium look) */ stroke-linejoin: round; /* Rounded corners (premium look) */ fill: none; /* Icons are outline, not filled */ } ``` **Stroke width adjustments (use sparingly):** ```css /* Thinner for smaller sizes (16px) */ .icon-sm { stroke-width: 1.5; } /* Default for medium sizes (20px+) */ .icon, .icon-lg { stroke-width: 1.5; } /* Never use stroke-width > 2 (looks heavy) */ ``` **When to adjust stroke:** - Keep stroke-width at 1.5 for almost all cases - Only adjust if icon appears too thin or thick at specific sizes - Test at actual display size before adjusting ### Article Metadata Icons **Common metadata icons:** | Icon | Name | Usage | Example | |---|---|---|---| | 📅 | `calendar` | Publication date | "2026-04-08" | | ⏱️ | `clock` | Read time | "5 min read" | | ✍️ | `user` | Author name | "By Danilo M." | | 🏷️ | `tag` | Article category/type | "TECH" badge | | 💬 | `message-circle` | Comments count | "12 comments" | | 🔗 | `link` | External link | "Read on source" | **Metadata icon implementation:** ```html ``` **Metadata icon styling:** ```css .article-meta { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; } .meta-item { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; color: var(--text-dim); } .meta-item [data-feather] { color: var(--text-dim); width: 16px; height: 16px; stroke-width: 1.5; flex-shrink: 0; } ``` ### Social Sharing Icons **Social platform icons (Feather alternatives):** Since Feather doesn't have brand-specific social icons, use a minimal approach: ```html ``` **Or use platform-specific generic icons:** ```html ``` **Social share button styling:** ```css .share-btn { width: 40px; height: 40px; border-radius: 50%; background: var(--surface); border: 1px solid var(--border); color: var(--text); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; } .share-btn [data-feather] { width: 20px; height: 20px; color: currentColor; stroke: currentColor; } .share-btn:hover { background: var(--accent); color: var(--bg); border-color: var(--accent); } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } ``` ### Button Icons: With Text and Alone **Icon + text button (recommended for clarity):** ```html ``` **Button with icon styling:** ```css .btn { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1.5rem; } .btn [data-feather] { width: 20px; height: 20px; color: currentColor; stroke: currentColor; flex-shrink: 0; } .btn-primary [data-feather] { color: var(--bg); /* Icon matches button text color */ } .btn-primary:hover [data-feather] { color: var(--text); /* Updates on hover */ } ``` **Icon-only button (tooltip required):** ```html ``` **Icon-only button styling:** ```css .icon-btn { width: 44px; /* Accessible touch target */ height: 44px; border-radius: 4px; background: transparent; border: 1px solid var(--border); color: var(--text); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; position: relative; } .icon-btn [data-feather] { width: 24px; height: 24px; color: currentColor; } .icon-btn:hover { background: var(--surface); color: var(--accent); } .icon-btn:focus { outline: 2px solid var(--accent); outline-offset: 2px; } /* Tooltip on hover */ .icon-btn::after { content: attr(title); position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); white-space: nowrap; background: var(--accent); color: var(--bg); padding: 0.5rem 0.75rem; border-radius: 4px; font-size: 0.75rem; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } .icon-btn:hover::after { opacity: 1; } @media (max-width: 768px) { .icon-btn::after { display: none; } /* No tooltip on touch */ } ``` ### Form Icons: Input Validation, Search, etc. **Input field with icon:** ```html
Password is too short
``` **Form input icon styling:** ```css .input-wrapper { position: relative; display: flex; align-items: center; } .form-input { padding-left: 2.5rem; /* Space for icon on left */ padding-right: 2.5rem; /* Space for validation icon on right */ } .input-icon { position: absolute; width: 20px; height: 20px; color: var(--text-dim); pointer-events: none; } .input-icon:first-of-type { left: 0.75rem; /* Search/prefix icon on left */ } .input-icon:last-of-type { right: 0.75rem; /* Validation icon on right */ } /* Valid input state */ .form-group.valid .input-valid { color: var(--accent2); /* Green checkmark */ } /* Error input state */ .form-group.has-error .form-input { border-color: var(--type-photo); /* Pink border */ } .form-group.has-error .input-error { color: var(--type-photo); /* Red alert icon */ } .error-text { display: block; margin-top: 0.5rem; font-size: 0.8rem; color: var(--type-photo); } ``` ### Icon States: Hover, Active, Disabled **Interactive icon states:** ```css /* Default state */ [data-feather] { color: var(--text); transition: all 0.3s ease; } /* Hover state */ a [data-feather]:hover, button [data-feather]:hover { color: var(--accent); transform: scale(1.1); } /* Active/focused state */ a [data-feather]:focus, button [data-feather]:focus { color: var(--accent); } /* Disabled state */ [data-feather][aria-disabled="true"], .disabled [data-feather] { color: var(--muted); opacity: 0.5; cursor: not-allowed; } /* Pressed/active state */ button.active [data-feather] { color: var(--accent2); transform: scale(0.95); } ``` ### Animated Icons Some icons can have subtle animations for emphasis or status: **Rotating icon (loading):** ```css @keyframes icon-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .icon-loading { animation: icon-spin 2s linear infinite; } ``` **Pulse icon (attention):** ```css @keyframes icon-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .icon-pulse { animation: icon-pulse 2s ease-in-out infinite; } ``` **Bounce icon (urgency):** ```css @keyframes icon-bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } } .icon-bounce { animation: icon-bounce 1s ease-in-out infinite; } ``` **Implementation:** ```html ``` ### Icon Accessibility **Screen reader considerations:** Always provide text alternatives for icon-only content: ```html
2026-04-08
``` **Pattern: Hide decorative icons from screen readers** ```css [aria-hidden="true"] { pointer-events: none; } ``` **Color alone is not enough:** - Never use icon color alone to convey status - Pair with text or pattern: "✓ Complete" not just a green checkmark - Verified in Section 7 (Accessibility) ### Feather Icons Quick Reference **Common icons used across danix.xyz:** ``` NAVIGATION & UI ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ menu, x, chevron-left, chevron-right, arrow-right search, home, settings, user, log-out ARTICLE METADATA ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ calendar, clock, user, tag, message-circle, link ACTIONS & BUTTONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ send, download, upload, refresh, save, trash-2 edit, copy, share-2, external-link, download FORMS & VALIDATION ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ mail, lock, unlock, eye, eye-off, check-circle alert-circle, info, help-circle SOCIAL & SHARING ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ share-2, send, mail, heart, star, bookmark STATUS & FEEDBACK ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ check, x, loader, alert-circle, info, help-circle ``` For full icon list: https://feathericons.com ### Icon Implementation Checklist When adding an icon to the design: - [ ] Icon chosen from Feather Icons library - [ ] Size is appropriate (16px, 20px, 24px, 32px) - [ ] Color uses CSS custom property (never hard-coded) - [ ] Icon-only elements have `aria-label` and `aria-hidden` - [ ] Icon + text: icon has `aria-hidden="true"` - [ ] Decorative icons: `aria-hidden="true"` - [ ] Hover/focus states are defined - [ ] Works in both dark and light modes - [ ] Touch targets are ≥44px (icon-only buttons) - [ ] Stroke width is 1.5 (Feather default) - [ ] Stroke cap/join are rounded (`stroke-linecap: round`) --- ## 7. Animations & Effects ### Animation Philosophy: Subtle, Purposeful, Accessible Animations serve a purpose: guide attention, provide feedback, create delight. They should never distract or slow down the user experience. **Principles:** - **Subtle:** Animations are felt, not noticed - **Purposeful:** Every animation communicates something (loading, transition, feedback) - **Fast:** Most animations complete in 200–400ms - **Accessible:** `prefers-reduced-motion` is always respected - **Performant:** GPU-accelerated, no layout thrashing **When to animate:** - ✅ State changes (hover, click, focus) - ✅ Transitions between pages or sections - ✅ Feedback (success, error, loading) - ✅ Entrance animations (scroll reveal) - ❌ Autoplaying animations (distracting) - ❌ Long loops (hypnotizing, annoying) - ❌ Motion sickness triggers (rapid spinning, flashing) ### Transition Timing: Duration & Easing **Transition duration scale:** | Speed | Duration | Use Case | |---|---|---| | **Instant** | 0ms | No transition (rarely used) | | **Fast** | 100ms | Hover states, quick feedback | | **Normal** | 300ms | Color changes, opacity shifts | | **Slow** | 500ms | Page transitions, major changes | | **Very Slow** | 800ms+ | Hero animations, entrance effects | **Easing functions (choose based on effect):** ```css /* Ease-out: Quick start, slow end (most natural for interactions) */ transition: all 0.3s ease-out; /* Good for: Fade in, opacity changes, button hovers */ /* Linear: Constant speed (for loading, progress) */ animation: spin 2s linear infinite; /* Good for: Spinners, progress bars, continuous motion */ /* Ease-in-out: Smooth acceleration and deceleration */ transition: all 0.3s ease-in-out; /* Good for: Modal appearances, slide transitions */ /* Cubic-bezier: Custom easing for special effects */ transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); /* Good for: Bounce effects, playful interactions */ ``` **Implementation pattern:** ```css /* Fast feedback (100ms) */ .button:hover { transition: all 0.1s ease-out; } /* Normal interaction (300ms) */ a { transition: color 0.3s ease-out; } /* Page transition (500ms) */ .page-enter { animation: fadeIn 0.5s ease-out; } /* Entrance animation (800ms) */ .hero-title { animation: slideUp 0.8s ease-out; } ``` ### CSS Transitions: Hover States, Color Changes, Opacity **Smooth color transitions on hover:** ```css a { color: var(--accent); transition: color 0.3s ease-out; } a:hover { color: var(--accent2); } /* Works in both dark and light modes */ ``` **Button hover effect (scale + color):** ```css .button { background: var(--accent); color: var(--bg); transition: all 0.3s ease-out; } .button:hover { background: var(--accent2); transform: scale(1.05); } .button:active { transform: scale(0.98); } ``` **Opacity fade (for dimming/emphasis):** ```css .card { opacity: 1; transition: opacity 0.3s ease-out; } .card:hover { opacity: 0.9; } /* Disabled state fade */ .card.disabled { opacity: 0.5; pointer-events: none; } ``` **Border/shadow transitions (for elevation):** ```css .card { border: 1px solid var(--border); box-shadow: 0 0 40px var(--accent-glow); transition: box-shadow 0.3s ease-out; } .card:hover { box-shadow: 0 0 60px var(--accent-glow); } ``` **What NOT to transition:** ```css /* ❌ Bad: Transform on width (causes jank) */ .menu { width: 200px; transition: width 0.3s; } .menu.open { width: 100%; } /* ✅ Good: Use transform instead */ .menu { transform: translateX(-100%); transition: transform 0.3s; } .menu.open { transform: translateX(0); } ``` ### CSS Animations: Keyframes, Duration, Timing **Fade-in animation (entrance):** ```css @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .article-card { animation: fadeIn 0.5s ease-out; } ``` **Slide-up animation (entrance with movement):** ```css @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .hero-title { animation: slideUp 0.8s ease-out; } ``` **Scale pulse animation (attention):** ```css @keyframes scalePulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } } .cta-button { animation: scalePulse 2s ease-in-out infinite; } ``` **Rotate animation (loading spinner):** ```css @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .spinner { animation: rotate 2s linear infinite; } ``` **Shimmer animation (loading skeleton):** ```css @keyframes shimmer { 0% { background-position: -1000px 0; } 100% { background-position: 1000px 0; } } .skeleton { background: linear-gradient(90deg, var(--bg2) 25%, var(--surface) 50%, var(--bg2) 75%); background-size: 1000px 100%; animation: shimmer 2s infinite; } ``` **Animation best practices:** ```css /* Avoid infinite animations unless necessary */ .element { animation: fadeIn 0.5s ease-out; } /* Finite */ /* Use infinite only for looping effects (spinners, pulse) */ .spinner { animation: rotate 2s linear infinite; } /* Infinite ok */ /* Provide animation-delay for staggered effects */ .card:nth-child(1) { animation: slideUp 0.8s ease-out 0s; } .card:nth-child(2) { animation: slideUp 0.8s ease-out 0.1s; } .card:nth-child(3) { animation: slideUp 0.8s ease-out 0.2s; } ``` ### Scroll-Driven Animations: Fade-In, Slide-In **Scroll reveal with IntersectionObserver:** ```javascript const revealElements = document.querySelectorAll('[data-reveal]'); const revealObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); revealObserver.unobserve(entry.target); // Animate once } }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); revealElements.forEach((el) => revealObserver.observe(el)); ``` **CSS for scroll-reveal:** ```css [data-reveal] { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease-out, transform 0.6s ease-out; } [data-reveal].revealed { opacity: 1; transform: translateY(0); } /* Stagger effect for multiple elements */ [data-reveal]:nth-child(1) { transition-delay: 0s; } [data-reveal]:nth-child(2) { transition-delay: 0.1s; } [data-reveal]:nth-child(3) { transition-delay: 0.2s; } ``` **HTML markup for scroll reveal:** ```html
...
...
...
``` ### Matrix Rain Animation: Canvas-Based, Opacity, Performance **Canvas setup in HTML:** ```html ``` **Canvas styling:** ```css #matrix-canvas { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 0; pointer-events: none; /* Don't block interactions */ display: block; } /* Dark mode opacity */ #matrix-canvas { opacity: 0.13; } /* Light mode opacity (more visible) */ html.theme-light #matrix-canvas { opacity: 0.18; } ``` **Canvas JavaScript (simplified example):** ```javascript const canvas = document.getElementById('matrix-canvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const chars = '01アイウエオカキクケコサシスセソタチツテト'; const charArray = chars.split(''); const fontSize = 16; const columns = Math.floor(canvas.width / fontSize); const drops = Array(columns).fill(0).map(() => Math.random() * canvas.height); function drawMatrix() { ctx.fillStyle = 'rgba(6, 11, 16, 0.05)'; /* Dark mode: navy with opacity */ ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'rgba(0, 255, 136, 0.4)'; /* Green text */ ctx.font = `${fontSize}px 'JetBrains Mono'`; for (let i = 0; i < drops.length; i++) { const char = charArray[Math.floor(Math.random() * charArray.length)]; ctx.fillText(char, i * fontSize, drops[i] * fontSize); if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) { drops[i] = 0; } drops[i] += 0.5; /* Speed of fall */ } } function animateMatrix() { drawMatrix(); requestAnimationFrame(animateMatrix); } animateMatrix(); /* Adjust opacity on theme change */ document.addEventListener('themechange', (e) => { ctx.fillStyle = e.detail.isDark ? 'rgba(6, 11, 16, 0.05)' : 'rgba(240, 244, 248, 0.05)'; }); ``` **Light mode color adjustment:** ```javascript /* In light mode, use darker green and adjusted opacity */ if (document.documentElement.classList.contains('theme-light')) { ctx.fillStyle = 'rgba(0, 143, 90, 0.3)'; /* Darker green for light bg */ // Adjust opacity of background fill ctx.fillStyle = 'rgba(240, 244, 248, 0.05)'; } ``` **Performance notes:** - Use `requestAnimationFrame` for smooth 60fps animation - Keep opacity low (0.13 dark, 0.18 light) so matrix doesn't interfere with text - Reduce character density on mobile devices - Consider pausing animation when tab is hidden (`visibilitychange` event) ### Hero Text Glitch Effect: Chromatic Aberration Style **Glitch effect with text shadows:** ```css @keyframes glitch-1 { 0%, 100% { text-shadow: 0 0 80px rgba(168, 85, 247, 0.18); } 50% { text-shadow: -2px 0 80px rgba(255, 0, 0, 0.2), 2px 0 80px rgba(0, 255, 255, 0.2); } } @keyframes glitch-2 { 0%, 100% { text-shadow: 0 0 120px rgba(168, 85, 247, 0.08); } 50% { text-shadow: 2px 0 120px rgba(255, 0, 0, 0.1), -2px 0 120px rgba(0, 255, 255, 0.1); } } .hero-name { animation: glitch-1 3s ease-in-out infinite; } .hero-name::after { content: attr(data-text); position: absolute; left: 0; top: 0; animation: glitch-2 3s ease-in-out infinite; } ``` **HTML for glitch effect:** ```html

danix.xyz

``` **Light mode glitch adjustment:** ```css html.theme-light .hero-name { text-shadow: 0 0 80px rgba(124, 58, 237, 0.12), 0 0 120px rgba(124, 58, 237, 0.05); animation: glitch-light 3s ease-in-out infinite; } @keyframes glitch-light { 0%, 100% { text-shadow: 0 0 80px rgba(124, 58, 237, 0.12); } 50% { text-shadow: -2px 0 80px rgba(200, 0, 0, 0.15), 2px 0 80px rgba(0, 150, 200, 0.15); } } ``` ### Progress Bar Animation: Gradient, Scroll-Driven **Progress bar HTML:** ```html
``` **Progress bar styling & animation:** ```css #scroll-progress { position: fixed; top: 0; left: 0; height: 2px; width: 0%; background: linear-gradient(to right, var(--accent), var(--accent2)); box-shadow: 0 0 8px rgba(168, 85, 247, 0.6); z-index: 9999; pointer-events: none; transition: width 0.1s ease-out; } /* Light mode adjustment */ html.theme-light #scroll-progress { background: linear-gradient(to right, #7c3aed, #008f5a); box-shadow: 0 0 8px rgba(124, 58, 237, 0.45); } ``` **Progress bar JavaScript:** ```javascript function updateScrollProgress() { const windowHeight = document.documentElement.scrollHeight - window.innerHeight; const scrolled = window.scrollY; const progress = (scrolled / windowHeight) * 100; document.getElementById('scroll-progress').style.width = progress + '%'; } window.addEventListener('scroll', updateScrollProgress); ``` ### Loading Spinners: Rotating Icons, Pulsing Elements **Rotating spinner:** ```css @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .spinner { width: 40px; height: 40px; border: 4px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 1s linear infinite; } ``` **Pulsing loader:** ```css @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .loader-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); display: inline-block; animation: pulse 1.4s ease-in-out infinite; } .loader-dot:nth-child(1) { animation-delay: -0.32s; } .loader-dot:nth-child(2) { animation-delay: -0.16s; } .loader-dot:nth-child(3) { animation-delay: 0s; } ``` **HTML for pulsing loader:** ```html
``` ### Fade/Slide Transitions: Page Transitions, Modal Appears **Modal entrance animation:** ```css @keyframes modalEnter { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } .modal.active { animation: modalEnter 0.3s ease-out; } .modal-backdrop.active { animation: fadeIn 0.3s ease-out; } ``` **Backdrop fade:** ```css @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } ``` **Dropdown menu slide animation:** ```css @keyframes slideDown { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .dropdown.open { animation: slideDown 0.2s ease-out; } ``` **Page transition (if using SPAs):** ```css @keyframes pageEnter { from { opacity: 0; } to { opacity: 1; } } .page-enter { animation: pageEnter 0.4s ease-out; } ``` ### Hover Lift Effects: Cards Elevating, Shadows Changing **Card hover lift:** ```css .card { transition: all 0.3s ease-out; box-shadow: 0 0 40px var(--accent-glow); } .card:hover { transform: translateY(-4px); /* Lift up 4px */ box-shadow: 0 0 60px var(--accent-glow); /* Enhanced shadow */ } ``` **Link hover underline slide:** ```css a { position: relative; text-decoration: none; color: var(--accent); } a::after { content: ''; position: absolute; bottom: -2px; left: 0; width: 0; height: 2px; background: var(--accent); transition: width 0.3s ease-out; } a:hover::after { width: 100%; } ``` **Icon scale on hover:** ```css .icon-btn:hover [data-feather] { transform: scale(1.1); transition: transform 0.3s ease-out; } ``` ### Button Interactions: Scale, Color Shift on Click **Button press effect:** ```css .button { transition: all 0.3s ease-out; } .button:hover { transform: scale(1.05); box-shadow: 0 0 20px rgba(168, 85, 247, 0.3); } .button:active { transform: scale(0.98); /* Slightly smaller when pressed */ } .button:focus { outline: 2px solid var(--accent); outline-offset: 2px; } ``` **Toggle button state:** ```css .toggle-btn { background: var(--muted); transition: all 0.3s ease-out; } .toggle-btn.active { background: var(--accent); color: var(--bg); } ``` ### Dropdown/Menu Animations: Slide Down, Fade In **Mobile hamburger menu slide:** ```css .nav-menu { position: fixed; left: 0; top: 60px; width: 100%; max-height: 0; overflow: hidden; background: var(--bg2); transition: max-height 0.3s ease-out; } .nav-menu.active { max-height: 500px; /* Slide down */ } ``` **Dropdown with fade:** ```css .dropdown-content { position: absolute; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.2s ease-out, visibility 0.2s ease-out; } .dropdown.open .dropdown-content { opacity: 1; visibility: visible; pointer-events: auto; } ``` ### Prefers-Reduced-Motion: Respecting Accessibility **Essential: Disable animations for users with `prefers-reduced-motion`:** ```css /* Reduced motion: remove animations */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } ``` **Or disable specific animations:** ```css @media (prefers-reduced-motion: reduce) { .card { transition: none; } .spinner { animation: none; } .modal { animation: none; } /* Keep essential transitions (very fast) */ a { transition: color 0.01ms; } } ``` **JavaScript to detect preference:** ```javascript const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (prefersReducedMotion) { // Skip heavy animations or use instant transitions element.style.transition = 'all 0.01ms'; } else { // Normal animations element.style.transition = 'all 0.3s ease-out'; } ``` **Pattern: Respect user preference across the site** ```css /* Always check prefers-reduced-motion first */ @media (prefers-reduced-motion: reduce) { /* Animations disabled */ } /* Default animations (only shown to users who haven't set prefers-reduced-motion) */ .element { animation: slideUp 0.8s ease-out; } ``` ### Performance Considerations: GPU Acceleration, Avoiding Jank **Use GPU-accelerated properties:** ```css /* ✅ Good: GPU accelerated (use these) */ .element { transform: translateX(20px); /* Transform */ transform: scale(1.05); /* Scale */ transform: rotate(45deg); /* Rotate */ opacity: 0.5; /* Opacity */ } /* ❌ Bad: Causes layout recalculation (avoid) */ .element { width: 200px; /* Triggers layout */ height: 200px; /* Triggers layout */ margin: 20px; /* Triggers layout */ left: 20px; /* Triggers layout if position: absolute */ } ``` **Avoid layout thrashing (simultaneous reads/writes):** ```javascript /* ❌ Bad: Reads and writes alternate (thrashing) */ for (let i = 0; i < elements.length; i++) { elements[i].style.width = elements[i].offsetWidth + 10 + 'px'; // Read then write } /* ✅ Good: Batch reads, then batch writes */ const widths = []; for (let i = 0; i < elements.length; i++) { widths.push(elements[i].offsetWidth); // Read all first } for (let i = 0; i < elements.length; i++) { elements[i].style.width = (widths[i] + 10) + 'px'; // Write all } ``` **Use `will-change` sparingly:** ```css /* ✅ Good: Only on animated elements */ .card { will-change: transform; animation: slideUp 0.8s ease-out; } /* Remove will-change after animation */ .card.done { will-change: auto; } ``` **JavaScript to manage will-change:** ```javascript const element = document.querySelector('.card'); // Add will-change before animation element.style.willChange = 'transform'; // Animation happens via CSS // Remove will-change after animation completes element.addEventListener('animationend', () => { element.style.willChange = 'auto'; }); ``` **Debounce scroll/resize events:** ```javascript /* ❌ Bad: Fires too frequently (jank) */ window.addEventListener('scroll', updateProgress); /* ✅ Good: Debounced */ let ticking = false; function updateProgress() { // Update progress bar } window.addEventListener('scroll', () => { if (!ticking) { window.requestAnimationFrame(updateProgress); ticking = true; } ticking = false; }); ``` **Performance checklist:** - [ ] Animations use `transform`, `opacity` (not `width`, `height`) - [ ] `prefers-reduced-motion` is respected - [ ] No layout thrashing in JavaScript animations - [ ] `will-change` removed after animations - [ ] Scroll/resize events are debounced - [ ] Canvas animations use `requestAnimationFrame` - [ ] No infinite animations unless necessary - [ ] Animation durations are appropriate (200–800ms) ### Animation Quick Reference ``` TIMING SCALE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Fast: 0.1s – 0.2s (quick feedback) Normal: 0.3s – 0.4s (interaction response) Slow: 0.5s – 0.8s (page transitions) Very Slow: 0.8s+ (hero animations) EASING FUNCTIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ease-out: Quick start, slow end (default) linear: Constant speed (spinners, progress) ease-in-out: Smooth both ends (modals) cubic-bezier: Custom (bounces, special effects) GPU-ACCELERATED PROPERTIES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ transform: (translateX, scale, rotate) opacity: (fade in/out) ``` --- ## 8. Accessibility & Contrast ### WCAG Compliance Baseline: AA Standard Minimum danix.xyz commits to **WCAG 2.1 Level AA** compliance across all views and components. This is the industry standard for accessibility. **What is WCAG 2.1 Level AA?** - **WCAG 2.1** = Web Content Accessibility Guidelines version 2.1 - **Level AA** = Intermediate compliance (middle ground between A and AAA) - **Covers:** Color contrast, keyboard navigation, screen readers, motion, text alternatives **Why AA, not AAA?** - AA is the practical standard (AAA is too restrictive for real-world design) - AA ensures accessibility for users with disabilities without limiting aesthetics - All government and enterprise sites target AA minimum **danix.xyz AA commitments:** - Color contrast ≥4.5:1 for normal text, ≥3:1 for large text - Keyboard-navigable (Tab, Enter, Space, Escape) - Screen reader compatible (semantic HTML, ARIA where needed) - No auto-playing animations (respects `prefers-reduced-motion`) - Text alternatives for all images and media - Form validation messages are accessible **Beyond AA (AAA features where possible):** - Contrast ≥7:1 for emphasize text (where feasible) - Extended audio descriptions for videos - Multiple ways to find content (search, navigation, sitemap) ### Color Contrast Ratios: Verified Pairs, Light/Dark Modes **WCAG contrast requirements:** | Text Type | AA Minimum | AAA (Bonus) | |---|---|---| | Normal text (≥14px) | 4.5:1 | 7:1 | | Large text (≥18px) | 3:1 | 4.5:1 | | Graphics/UI components | 3:1 | N/A | | Focus indicators | 3:1 | N/A | **All color pairs verified for danix.xyz:** **Dark mode (dark background, light text):** | Text Color | Background | Contrast | Status | |---|---|---|---| | `--text` (#c4d6e8) | `--bg` (#060b10) | 12.3:1 | ✅ AAA | | `--text` (#c4d6e8) | `--surface` (#101e2d) | 11.8:1 | ✅ AAA | | `--text-dim` (#7a9bb8) | `--bg` (#060b10) | 5.2:1 | ✅ AA | | `--accent` (#a855f7) | `--bg` (#060b10) | 5.1:1 | ✅ AA | | `--accent2` (#00ff88) | `--bg` (#060b10) | 7.2:1 | ✅ AAA | | `--type-tech` (#a855f7) | `--bg` (#060b10) | 5.1:1 | ✅ AA | | `--type-quote` (#00ff88) | `--bg` (#060b10) | 7.2:1 | ✅ AAA | | `--type-link` (#38bdf8) | `--bg` (#060b10) | 5.8:1 | ✅ AA | **Light mode (light background, dark text):** | Text Color | Background | Contrast | Status | |---|---|---|---| | `--text` (#0d1b2a) | `--bg` (#f0f4f8) | 12.5:1 | ✅ AAA | | `--text` (#0d1b2a) | `--surface` (#d4dff0) | 10.1:1 | ✅ AAA | | `--text-dim` (#2e4a6a) | `--bg` (#f0f4f8) | 6.3:1 | ✅ AAA | | `--accent` (#7c3aed) | `--bg` (#f0f4f8) | 5.5:1 | ✅ AA | | `--accent2` (#008f5a) | `--bg` (#f0f4f8) | 5.8:1 | ✅ AA | **How to verify contrast:** - Use WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/ - Use Accessibility Insights browser extension - Use Chrome DevTools: Right-click element → Inspect → Accessibility tab **Pattern: Test before shipping** ```css /* When adding new colors, verify contrast immediately */ .new-component { color: #YOUR_COLOR; background: var(--bg); /* TEST: Use WebAIM to verify contrast ratio ≥4.5:1 */ } ``` ### Semantic HTML: Proper Markup, Structure, Hierarchy **Use semantic elements, not divs:** ```html
``` **Semantic elements and their use:** | Element | Purpose | Use Case | |---|---|---| | `
` | Page header | Top of page, site branding | | `