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 --- THEMING-STANDARD.md | 7009 --------------------------------------------------- 1 file changed, 7009 deletions(-) delete mode 100644 THEMING-STANDARD.md (limited to 'THEMING-STANDARD.md') diff --git a/THEMING-STANDARD.md b/THEMING-STANDARD.md deleted file mode 100644 index 366cacb..0000000 --- a/THEMING-STANDARD.md +++ /dev/null @@ -1,7009 +0,0 @@ -# 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 | -| `