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

Article Title

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

Section Name

` | +| Card titles | 700 | 1.25–1.5rem | 1.3 | Large card headline | +| Badges/labels | 700 | 0.75rem | 1 | Type badges, tags | +| Hero text | 800 | 3–4rem | 1.1 | Homepage hero name | +| Menu items | 800 | 1.5–2rem | 1.2 | Navigation text | + +**Size scaling across breakpoints:** +```css +/* Mobile (320px+) */ +h1 { font-size: 2rem; } + +/* Tablet (768px+) */ +@media (min-width: 768px) { + h1 { font-size: 2.5rem; } +} + +/* Desktop (1060px+) */ +@media (min-width: 1060px) { + h1 { font-size: 3rem; } +} + +/* Or use clamp() for fluid sizing */ +h1 { font-size: clamp(2rem, 6vw, 3.5rem); } +``` + +**Pairing with color:** +- Oxanium headings use `--text` (primary text color) for readability +- Headings can emphasize with `--accent` color for interactive sections +- Never use Oxanium for body text (too bold, reduces readability) + +### IBM Plex Sans: Body Font + +**Characteristics:** +- Open, friendly, highly readable sans-serif +- Weights: 300 (light), 400 (regular), 600 (semi-bold) +- Excellent readability at body text sizes +- Default for all prose and UI labels + +**Usage:** + +| Element | Weight | Size | Line Height | Example | +|---|---|---|---|---| +| Body paragraphs | 400 | 1rem | 1.8 | Article text | +| Prose descriptions | 400 | 0.95–1rem | 1.8 | Excerpt text | +| UI labels | 600 | 0.875–1rem | 1.6 | Button text, form labels | +| Metadata | 400 | 0.85rem | 1.6 | Dates, bylines, read time | +| Light/italic text | 300 | 1rem | 1.8 | Quotes, captions | + +**Size scaling across breakpoints:** +```css +/* Mobile (320px+) */ +body { font-size: 0.95rem; } +p { font-size: 0.95rem; } + +/* Tablet (768px+) */ +@media (min-width: 768px) { + body { font-size: 1rem; } + p { font-size: 1rem; } +} + +/* Desktop (1060px+) */ +@media (min-width: 1060px) { + body { font-size: 1.05rem; } + p { font-size: 1.05rem; } +} +``` + +**Pairing with color:** +- Body text always uses `--text` for readability +- Secondary text uses `--text-dim` for de-emphasis +- UI labels use `--text` or `--accent` depending on context (primary vs interactive) + +### JetBrains Mono: Monospace Font + +**Characteristics:** +- Open-source monospace designed for developers +- Weights: 400 (regular), 700 (bold) +- Highly legible at small sizes +- Signals technical content, terminal, code + +**Usage:** + +| Element | Weight | Size | Line Height | Example | +|---|---|---|---|---| +| Inline code | 400 | 0.9em | 1 | `var` or `const` | +| Code blocks | 400 | 0.85–0.9rem | 1.6 | `
` |
+| Terminal UI | 400 | 0.85–1rem | 1.5 | `$ curl https://...` |
+| Metadata | 400 | 0.8–0.85rem | 1.5 | Timestamps, IDs |
+| Attribution | 400 | 0.9rem | 1.5 | "— Author Name" |
+
+**Size scaling:**
+```css
+/* Monospace text scales less aggressively than display fonts */
+code { font-size: 0.9em; }        /* Relative to parent */
+.terminal { font-size: 0.85rem; } /* Absolute for terminal UI */
+```
+
+**Pairing with color:**
+- Code blocks use `--accent2` (neon green) for keywords and highlights
+- Terminal UI uses `--terminal-prompt` (green) and `--terminal-text` (light)
+- Metadata uses `--text-dim` for secondary importance
+- Links in monospace use `--accent` or `--type-*` depending on context
+
+### Google Fonts CDN
+
+Fonts are loaded from Google Fonts CDN for performance:
+
+```html
+
+```
+
+**Loading strategy:**
+- `display=swap` ensures text is visible immediately (FOUT fallback)
+- Only necessary weights are loaded (700, 800 for Oxanium; 300, 400, 600 for IBM Plex; 400, 700 for JetBrains)
+- CSS follows HTML to minimize CLS (Cumulative Layout Shift)
+
+**Fallback behavior:**
+- If CDN fails, system fonts kick in (fallback chain in font-family)
+- Site remains readable even without web fonts
+- Performance is not compromised if CDN is slow
+
+### Font Weights: When to Use Each
+
+**Oxanium weights:**
+- **700 (Bold):** Small display text, badges, labels, menu items
+- **800 (Extra Bold):** Large display text, page titles, hero text
+
+**IBM Plex Sans weights:**
+- **300 (Light):** Italic prose, quotes, captions (less weight balances slant)
+- **400 (Regular):** Body text, descriptions, most UI text
+- **600 (Semi-bold):** UI labels, buttons, emphasized text
+
+**JetBrains Mono weights:**
+- **400 (Regular):** Code, terminal, metadata, timestamps
+- **700 (Bold):** Keywords in code blocks, emphasized terminal text
+
+**Pattern:**
+```css
+strong, b { font-weight: 600; }    /* Use semi-bold, not bold */
+em, i { font-weight: 300; }        /* Light for italic (compensates for slant) */
+code { font-weight: 400; }         /* Regular monospace (bold looks too heavy) */
+```
+
+### Font Sizing Scale
+
+Typography uses a modular scale for consistency. Base size is 1rem (17px on desktop):
+
+**Display sizes (Oxanium):**
+```
+h1: 2rem (32px mobile) → 3rem (51px desktop)
+h2: 1.75rem (30px) → 2rem (34px)
+h3: 1.5rem (26px) → 1.75rem (30px)
+h4: 1.25rem (21px) → 1.5rem (26px)
+```
+
+**Body sizes (IBM Plex Sans):**
+```
+Body text: 0.95rem (16px mobile) → 1.05rem (18px desktop)
+Metadata: 0.85rem (14px)
+Small: 0.8rem (14px)
+```
+
+**Monospace sizes (JetBrains Mono):**
+```
+Code block: 0.85rem (14px)
+Terminal: 0.85rem (14px)
+Inline code: 0.9em (relative to parent)
+Metadata: 0.8rem (14px)
+```
+
+**Responsive sizing with clamp():**
+```css
+/* Scales fluidly between mobile and desktop */
+h1 {
+  font-size: clamp(2rem, 6vw, 3.5rem);
+  /* Minimum: 32px, Preferred: 6vw, Maximum: 56px */
+}
+
+p {
+  font-size: clamp(0.95rem, 1.5vw, 1.05rem);
+  /* Minimum: 15px, Preferred: 1.5vw, Maximum: 17px */
+}
+```
+
+### Line Height Hierarchy
+
+Different line heights optimize readability for different content types:
+
+| Content Type | Line Height | Reason |
+|---|---|---|
+| **Display (h1-h4)** | 1.1–1.3 | Tight spacing, dramatic appearance |
+| **Body prose** | 1.8 | Open spacing, comfortable reading |
+| **UI labels** | 1.5–1.6 | Compact, grouped with related content |
+| **Code blocks** | 1.6 | More open than inline, but not as loose as prose |
+| **Metadata** | 1.5 | Compact, supporting information |
+
+**Implementation:**
+```css
+h1, h2, h3, h4, h5, h6 { line-height: 1.2; }
+
+body, p, article { line-height: 1.8; }
+
+button, label, .ui-text { line-height: 1.5; }
+
+code, pre { line-height: 1.6; }
+
+.metadata, .byline { line-height: 1.5; }
+```
+
+### Letter Spacing (Tracking)
+
+Letter spacing is used sparingly to improve readability or create visual distinction:
+
+| Element | Tracking | Reason |
+|---|---|---|
+| **Display (h1-h4)** | Normal (-0.025em) | Geometric fonts benefit from tight tracking |
+| **Body text** | Normal (0) | Default is optimal |
+| **UI labels** | 0.05–0.08em | Uppercase labels benefit from open tracking |
+| **Monospace metadata** | 0.02em | Subtle, improves legibility |
+| **Menu items** | -0.035em | Tight tracking makes bold text more readable |
+
+**Implementation:**
+```css
+h1, h2, h3, h4 {
+  letter-spacing: -0.025em;  /* Oxanium is bold, tighten it */
+}
+
+.button, button {
+  letter-spacing: 0.08em;    /* Uppercase buttons are more open */
+  text-transform: uppercase;
+}
+
+nav a {
+  letter-spacing: -0.035em;  /* Menu items are bold, tighten */
+}
+
+.metadata {
+  letter-spacing: 0.02em;    /* Subtle spacing */
+}
+```
+
+### Text Hierarchy: h1 to h6, Body, Metadata
+
+All text elements follow a strict hierarchy:
+
+**Heading hierarchy (h1 → h6):**
+```
+h1: 2–3rem, Oxanium 800, line-height 1.2    (Page title, hero)
+h2: 1.75–2rem, Oxanium 800, line-height 1.2 (Section title)
+h3: 1.5–1.75rem, Oxanium 700, line-height 1.2 (Subsection)
+h4: 1.25–1.5rem, Oxanium 700, line-height 1.3 (Subheading)
+h5: 1.1–1.25rem, Oxanium 700, line-height 1.3 (Minor heading)
+h6: 1rem, Oxanium 700, line-height 1.3      (Small heading)
+```
+
+**Body text hierarchy:**
+```
+

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

: 0.85rem, JetBrains Mono 400, line-height 1.6 (block)
+
: Inherits from 
+```
+
+### Readable Line Lengths
+
+Prose content has optimal line lengths for reading comfort:
+
+| Content Type | Max Width | Characters | Reason |
+|---|---|---|---|
+| **Body prose** | 65ch–75ch | 65–75 chars per line | Optimal for single-column reading |
+| **Wide layout** | 900px | (auto) | Multi-column or prose + sidebar |
+| **Code blocks** | 100% (scrollable) | N/A | Code can be wide; horizontal scroll ok |
+
+**Implementation:**
+```css
+article {
+  max-width: 65ch;    /* ~900px on desktop */
+  margin: 0 auto;
+  padding: 0 1.5rem;
+}
+
+/* Code blocks can be wider */
+pre {
+  overflow-x: auto;   /* Allow horizontal scroll */
+  max-width: 100%;
+}
+```
+
+**Why line length matters:**
+- **Too short** (<50 chars): Eye jumps too much, fatiguing
+- **Too long** (>90 chars): Hard to return to start of next line
+- **Optimal** (65–75 chars): Comfortable reading speed, minimal eye movement
+
+### Color + Typography: Semantic Pairing
+
+Font choice + color choice communicate information together:
+
+| Combination | Meaning | Example |
+|---|---|---|
+| Oxanium + `--accent` | Important heading, call to action | Section title with purple accent |
+| IBM Plex + `--text` | Primary readable content | Body text |
+| IBM Plex + `--text-dim` | Secondary content, supporting info | Byline, date, metadata |
+| JetBrains Mono + `--accent2` | Code, technical content | Keywords in terminal |
+| JetBrains Mono + `--type-*` | Type-specific metadata | "Quote" label |
+
+**Pattern:**
+```css
+h2 {
+  font-family: var(--font-head);
+  color: var(--text);           /* Readable heading */
+}
+
+.article-byline {
+  font-family: var(--font-body);
+  color: var(--text-dim);       /* Secondary info, dimmed */
+}
+
+code {
+  font-family: var(--font-mono);
+  color: var(--accent2);        /* Technical, highlighted */
+}
+```
+
+### Monospace Usage: Terminal, Code, Metadata
+
+Monospace signals technical content and serves multiple purposes:
+
+**Terminal UI (hacker aesthetic):**
+```css
+.terminal-prompt { font-family: var(--font-mono); color: var(--terminal-prompt); }
+.terminal-output { font-family: var(--font-mono); color: var(--terminal-text); }
+/* Size: 0.85–1rem, line-height: 1.5 */
+```
+
+**Code blocks:**
+```css
+pre, code {
+  font-family: var(--font-mono);
+  color: var(--accent2);        /* Neon green in dark mode */
+  background: var(--bg);
+  font-size: 0.85rem;
+  line-height: 1.6;
+}
+```
+
+**Metadata (timestamps, IDs, technical info):**
+```css
+.timestamp, .metadata {
+  font-family: var(--font-mono);
+  color: var(--text-dim);
+  font-size: 0.8rem;
+  letter-spacing: 0.02em;
+}
+```
+
+**Quote attribution:**
+```css
+.quote-attribution {
+  font-family: var(--font-mono);
+  font-style: italic;
+  color: var(--text-dim);
+}
+```
+
+### Responsive Typography: Scaling Across Breakpoints
+
+Typography responds to viewport size for optimal readability:
+
+**Mobile (320px+): Smaller, tighter**
+```css
+/* Base styles (mobile-first) */
+h1 { font-size: 2rem; }
+body { font-size: 0.95rem; }
+```
+
+**Tablet (768px+): Medium, balanced**
+```css
+@media (min-width: 768px) {
+  h1 { font-size: 2.5rem; }
+  body { font-size: 1rem; }
+}
+```
+
+**Desktop (1060px+): Larger, more spacious**
+```css
+@media (min-width: 1060px) {
+  h1 { font-size: 3rem; }
+  body { font-size: 1.05rem; }
+  article { max-width: 70ch; }
+}
+```
+
+**Or use fluid sizing with clamp():**
+```css
+h1 { font-size: clamp(2rem, 6vw, 3.5rem); }
+body { font-size: clamp(0.95rem, 1.5vw, 1.05rem); }
+```
+
+**Line height can also be responsive:**
+```css
+h1 { line-height: 1.1; }
+
+@media (min-width: 768px) {
+  h1 { line-height: 1.2; }  /* More open on larger screens */
+}
+```
+
+### Accessibility: Font Size, Contrast, Readability
+
+Typography must support accessibility:
+
+**Minimum sizes:**
+- **Body text:** 16px minimum (1rem base is 17px on desktop, 16px on mobile)
+- **UI labels:** 14px minimum (0.85rem)
+- **Code/metadata:** 12px minimum (0.75rem for emergency cases, normally 14px)
+
+**Never go below:**
+- 12px for any readable content
+- Users with low vision rely on readable font sizes
+
+**Contrast requirements (paired with colors):**
+- All body text must have ≥4.5:1 contrast with background
+- All headings must have ≥3:1 contrast with background
+- Verified in Section 2 (Color System)
+
+**Readability patterns:**
+```css
+/* Good: comfortable line height, appropriate size */
+p {
+  font-size: 1rem;
+  line-height: 1.8;
+}
+
+/* Bad: too small, too tight */
+p {
+  font-size: 0.75rem;
+  line-height: 1.2;
+}
+```
+
+**Font smoothing for better rendering:**
+```css
+body {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+```
+
+### Typography Quick Reference Table
+
+```
+DISPLAY (Headings)
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+Element      Font        Weight  Size              Line Height
+h1           Oxanium     800     2–3rem            1.2
+h2           Oxanium     800     1.75–2rem         1.2
+h3           Oxanium     700     1.5–1.75rem       1.2
+h4           Oxanium     700     1.25–1.5rem       1.3
+
+BODY (Content)
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+paragraph    IBM Plex    400     1rem              1.8
+strong       IBM Plex    600     1rem              1.8
+em           IBM Plex    300     1rem              1.8
+small        IBM Plex    400     0.85rem           1.5
+
+CODE & METADATA
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+inline code  JetBrains   400     0.9em             1
+code block   JetBrains   400     0.85rem           1.6
+timestamp    JetBrains   400     0.8rem            1.5
+```
+
+---
+
+## 4. Spacing & Layout
+
+### Spacing Scale: Modular System
+
+All spacing uses a **modular scale** based on 0.5rem increments:
+
+```
+0.25rem (4px)   — Minimal gaps
+0.5rem  (8px)   — Tight spacing
+0.75rem (12px)  — Comfortable spacing
+1rem    (16px)  — Default spacing, base unit
+1.5rem  (24px)  — Generous spacing
+2rem    (32px)  — Large sections
+2.5rem  (40px)  — Very large sections
+3rem    (48px)  — Hero/major sections
+4rem    (64px)  — Between major sections
+```
+
+**Why modular scale matters:**
+- Creates visual rhythm and consistency
+- Makes layouts feel intentional, not random
+- Easier to maintain across responsive breakpoints
+- Reduces decision fatigue (limited palette vs unlimited options)
+
+**Implementation pattern:**
+```css
+/* Use custom properties for reusable spacing */
+:root {
+  --space-xs:   0.5rem;
+  --space-sm:   0.75rem;
+  --space-md:   1rem;
+  --space-lg:   1.5rem;
+  --space-xl:   2rem;
+  --space-2xl:  3rem;
+  --space-3xl:  4rem;
+}
+
+/* Apply consistently */
+.card { padding: var(--space-md); }
+section { margin-top: var(--space-2xl); }
+.grid { gap: var(--space-lg); }
+```
+
+### Padding Standards
+
+**Container padding (content inset from edges):**
+
+| Size | Mobile (320px) | Tablet (768px) | Desktop (1060px) |
+|---|---|---|---|
+| Default page padding | 1rem | 1.5rem | 2rem |
+| Card/container padding | 1rem | 1.5rem | 1.5rem |
+| Section padding (vertical) | 1.5rem | 2rem | 2.5rem |
+| Hero section padding | 1.5rem | 2rem | 3rem |
+
+**Implementation:**
+```css
+/* Page-level container */
+.container {
+  padding: 0 1rem;
+  max-width: 1080px;
+  margin: 0 auto;
+}
+
+@media (min-width: 768px) {
+  .container { padding: 0 1.5rem; }
+}
+
+@media (min-width: 1060px) {
+  .container { padding: 0 2rem; }
+}
+
+/* Cards and elevated containers */
+.card {
+  padding: 1rem;
+  background: var(--surface);
+}
+
+@media (min-width: 768px) {
+  .card { padding: 1.5rem; }
+}
+
+/* Sections with vertical spacing */
+section {
+  padding: 1.5rem 0;
+}
+
+@media (min-width: 768px) {
+  section { padding: 2rem 0; }
+}
+```
+
+### Margin Standards
+
+**Spacing between elements:**
+
+| Element | Bottom Margin | Purpose |
+|---|---|---|
+| `

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

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

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

Article Title

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

Article Title

+

Brief description of article content...

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

Your commentary about the quote...

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

Something went wrong. Please try again.

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

danix.xyz

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