# 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 | `
` |
| 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
```
**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
"
This is a remarkable quote that challenges assumptions.
"
— Author Name, Source
$
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 Title
```
**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
1 / 10
```
**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
```
**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
Share on X/Twitter
Share on LinkedIn
```
**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
```
**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 |
| `
Your commentary about the quote...