diff --git a/.github/plans/revised-frontend-design.prompt.md b/.github/plans/revised-frontend-design.prompt.md new file mode 100644 index 0000000..ca3d81c --- /dev/null +++ b/.github/plans/revised-frontend-design.prompt.md @@ -0,0 +1,632 @@ +# Revised Frontend Design Plan: Tokyo Night Design System Overhaul + +## Summary + +Complete design system overhaul for the Jekyll blog: replace Inter with Geist font, rebrand from "Synthwave" to "Tokyo Night", fix hardcoded legacy colors, distribute accent colors across UI, add fluid typography, scroll-triggered animations, background texture/depth, grid-breaking design elements, generate missing assets, and remove legacy files. + +All changes follow the [frontend design skill](../../.github/skills/front-design/SKILL.md) guidelines. + +--- + +## Step 1: Replace Inter with Geist Font + +**Files:** `_includes/head/custom.html`, `assets/css/main.scss` + +### In `_includes/head/custom.html` + +Replace the Inter Google Font import with Geist from Vercel's CDN: + +```html + + + + + + + +``` + +### In `assets/css/main.scss` + +Change the `--font-body` custom property: + +```css +/* BEFORE */ +--font-body: 'Inter', sans-serif; + +/* AFTER */ +--font-body: 'Geist', sans-serif; +``` + +No other changes needed — all body text references use `var(--font-body)`. + +--- + +## Step 2: Fix ALL Hardcoded Old Synthwave Colors + +**Files:** `assets/css/main.scss`, `index.markdown`, `_posts/2026-02-05-synthwave-redesign-deep-dive.markdown` + +The old synthwave background `#0f0f1e` = `rgb(15, 15, 30)` appears as `rgba(15, 15, 30, ...)` in multiple places. The old synthwave cyan `#00d9ff` = `rgb(0, 217, 255)` also appears. All must be updated to Tokyo Night equivalents. + +### In `assets/css/main.scss` + +| Location | Current | Replacement | +|----------|---------|-------------| +| `.masthead` background | `rgba(15, 15, 30, 0.95)` | `rgba(26, 27, 38, 0.95)` | +| `.page__hero--overlay` gradient | `rgba(15, 15, 30, 0.7)` + `rgba(26, 26, 62, 0.9)` | `rgba(26, 27, 38, 0.7)` + `rgba(36, 40, 59, 0.9)` | +| `.page__footer` background | `rgba(15, 15, 30, 0.95)` | `rgba(26, 27, 38, 0.95)` | +| `tr:hover td` background | `rgba(0, 217, 255, 0.05)` | `rgba(125, 207, 255, 0.05)` | +| `.pagination--pager:hover` background | `rgba(0, 217, 255, 0.1)` | `rgba(125, 207, 255, 0.1)` | + +### In `index.markdown` + +```yaml +# BEFORE +overlay_color: "#0f0f1e" + +# AFTER +overlay_color: "#1A1B26" +``` + +### In `_posts/2026-02-05-synthwave-redesign-deep-dive.markdown` + +```yaml +# BEFORE +overlay_color: "#0f0f1e" + +# AFTER +overlay_color: "#1A1B26" +``` + +--- + +## Step 3: Rename Design System in CSS Comments + +**File:** `assets/css/main.scss` + +```css +/* BEFORE */ +/* ============================================ + SYNTHWAVE DESIGN SYSTEM + Outrun/Retro-Futuristic Theme + ============================================ */ + +/* CSS Custom Properties (Design Tokens) - Tokyo Night Theme */ + +/* AFTER */ +/* ============================================ + TOKYO NIGHT DESIGN SYSTEM + Developer-Centric Dark Theme + ============================================ */ + +/* CSS Custom Properties (Design Tokens) */ +``` + +Also update the syntax highlighting comment: + +```css +/* BEFORE */ +/* Tokyo Night syntax highlighting */ + +/* AFTER (no change needed — this one is already correct) */ +``` + +--- + +## Step 4: Fix `.notice--success` to Use Design Token + +**File:** `assets/css/main.scss` + +```css +/* BEFORE */ +.notice--success { + border-left-color: #00ff88; +} + +/* AFTER */ +.notice--success { + border-left-color: var(--accent-green); +} +``` + +--- + +## Step 5: Fix `.notice--warning` Semantic Color + +**File:** `assets/css/main.scss` + +```css +/* BEFORE */ +.notice--warning { + border-left-color: var(--accent-blue); +} + +/* AFTER */ +.notice--warning { + border-left-color: var(--accent-orange); +} +``` + +--- + +## Step 6: Distribute Accent Colors Across UI + +**File:** `assets/css/main.scss` + +Semantic color mapping: + +| Color | Token | UI Usage | +|-------|-------|----------| +| Blue `#7AA2F7` | `--accent-blue` | Primary links, active states, `.notice` (default), `h1` color — **keep as-is** | +| Purple `#BB9AF7` | `--accent-purple` | Hover glows, card accents, `h2` color, blockquote border — **keep as-is** | +| Cyan `#7DCFFF` | `--accent-cyan` | Inline code, link text, `.notice` default — **keep as-is** | +| Green `#9ECE6A` | `--accent-green` | **NEW:** `.notice--success` border (step 4), `.notice--info` if needed | +| Orange `#FF9E64` | `--accent-orange` | **NEW:** `.notice--warning` border (step 5), date/time metadata | +| Yellow `#E0AF68` | `--accent-yellow` | **NEW:** Blockquote cite/attribution, `` highlight styling | +| Red `#F7768E` | `--accent-red` | **NEW:** `.notice--danger` border | + +### New styles to add: + +```css +/* Notice - Danger */ +.notice--danger { + border-left-color: var(--accent-red); +} + +/* Date metadata styling */ +.page__meta, +.archive__item .page__meta { + color: var(--accent-orange); + font-family: var(--font-mono); + font-size: 0.8em; + letter-spacing: 0.02em; +} + +/* Mark/highlight styling */ +mark { + background: rgba(224, 175, 104, 0.15); + color: var(--accent-yellow); + padding: 0.1em 0.3em; + border-radius: 3px; +} + +/* Blockquote citation */ +blockquote cite, +blockquote footer { + color: var(--accent-yellow); + font-style: normal; + font-size: 0.9em; +} +``` + +--- + +## Step 7: Fluid Typography with `clamp()` + +**File:** `assets/css/main.scss` + +### Add fluid type sizes + +```css +/* In the :root custom properties block, add: */ +--type-hero: clamp(2rem, 5vw, 3.5rem); +--type-h1: clamp(1.75rem, 3.5vw, 2.75rem); +--type-h2: clamp(1.4rem, 2.5vw, 2rem); +--type-h3: clamp(1.15rem, 2vw, 1.5rem); +--type-h4: clamp(1rem, 1.5vw, 1.25rem); +--type-body: clamp(0.95rem, 1.1vw, 1.1rem); +--type-small: clamp(0.8rem, 0.9vw, 0.9rem); +``` + +### Apply to elements + +```css +body { font-size: var(--type-body); } + +h1 { font-size: var(--type-h1); letter-spacing: -0.02em; } +h2 { font-size: var(--type-h2); letter-spacing: -0.01em; } +h3 { font-size: var(--type-h3); } +h4 { font-size: var(--type-h4); } + +.page__title { font-size: var(--type-hero); } +.page__lead { font-size: clamp(1rem, 1.5vw, 1.3rem); } +.hero-tagline { font-size: clamp(0.95rem, 1.3vw, 1.2rem); } +``` + +### CRITICAL: Remove conflicting media query font-size rules + +These fixed font-size rules in breakpoints will override `clamp()` and must be **deleted**: + +```css +/* DELETE from @media (max-width: 1024px): */ +.page__title { font-size: 3em; } + +/* DELETE from @media (max-width: 768px): */ +.page__title { font-size: 2.2em; } +.page__lead { font-size: 1.1em; } +.hero-tagline { font-size: 1.1em; } +.archive__item-title { font-size: 1.3em; } +.page__content h2 { font-size: 1.6em; } + +/* DELETE from @media (max-width: 480px): */ +.page__title { font-size: 1.8em; } +.hero-tagline { font-size: 1em; } +.btn { font-size: 0.9em; } +``` + +Keep all other rules in those breakpoints (padding, display, grid-size, etc.). + +--- + +## Step 8: Scroll-Triggered Animations & Hero Enhancement + +**File:** `assets/css/main.scss` + +### Visibility-safe defaults (CRITICAL) + +The current `animation: fadeInUp 0.5s ease-out backwards` on `.page__content > *` uses `backwards` fill mode, meaning elements start invisible. This is risky — if animation fails to trigger, content is invisible forever. + +**Fix**: Change to forward-only animation that starts visible: + +```css +/* BEFORE */ +.page__content > * { + animation: fadeInUp 0.5s ease-out backwards; +} + +/* AFTER */ +.page__content > * { + animation: fadeInUp 0.5s ease-out both; +} +``` + +### Hero entrance enhancement + +Add a gradient sweep animation on the page title: + +```css +.page__title { + background: linear-gradient( + 90deg, + var(--accent-blue) 0%, + var(--accent-purple) 40%, + var(--accent-cyan) 60%, + var(--text-primary) 100% + ); + background-size: 200% 100%; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: gradientSweep 1.5s ease-out forwards, fadeInUp 0.6s ease-out; +} + +@keyframes gradientSweep { + from { background-position: 100% 50%; } + to { background-position: 0% 50%; } +} +``` + +### Scroll-triggered reveals for archive items + +Use `animation-timeline: view()` with `@supports` guard so non-supporting browsers see content normally: + +```css +/* Scroll-triggered animations — progressive enhancement only */ +@supports (animation-timeline: view()) { + .archive__item { + opacity: 0; + transform: translateY(30px); + animation: fadeInUp 0.6s ease-out both; + animation-timeline: view(); + animation-range: entry 0% entry 30%; + } + + .archive__item:nth-child(even) { + animation-delay: 0.1s; + } +} +``` + +**Key**: Without `@supports`, archive items remain `opacity: 1` (default) — no invisible content risk. + +--- + +## Step 9: Background Texture & Depth + +**File:** `assets/css/main.scss` + +### Increase grid visibility from 3% to 5% + +```css +/* BEFORE */ +background-image: + linear-gradient(rgba(122, 162, 247, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(122, 162, 247, 0.03) 1px, transparent 1px); + +/* AFTER */ +background-image: + linear-gradient(rgba(122, 162, 247, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(122, 162, 247, 0.05) 1px, transparent 1px); +``` + +### Add noise grain overlay via `body::after` + +```css +body::after { + content: ''; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + z-index: 9999; + opacity: 0.025; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); + background-repeat: repeat; + background-size: 256px 256px; + mix-blend-mode: overlay; +} +``` + +### Add secondary radial glow (asymmetric) + +Update the body background to include a purple radial glow anchored bottom-right: + +```css +/* BEFORE */ +body { + background: linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%); + background-attachment: fixed; +} + +/* AFTER */ +body { + background: + radial-gradient(ellipse at 85% 85%, rgba(187, 154, 247, 0.04) 0%, transparent 50%), + linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%); + background-attachment: fixed; +} +``` + +--- + +## Step 10: Grid-Breaking Design Elements + +**File:** `assets/css/main.scss` + +### Floating gradient orb behind hero + +```css +.page__hero--overlay::after { + content: ''; + position: absolute; + top: -20%; + right: -10%; + width: 400px; + height: 400px; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(122, 162, 247, 0.08) 0%, + rgba(187, 154, 247, 0.04) 40%, + transparent 70% + ); + filter: blur(60px); + pointer-events: none; + z-index: 0; +} +``` + +### Oversized decorative blockquote marks + +```css +blockquote { + position: relative; + overflow: visible; +} + +blockquote::before { + content: '"'; + position: absolute; + top: -0.3em; + left: -0.15em; + font-family: var(--font-display); + font-size: 4em; + color: var(--accent-purple); + opacity: 0.12; + line-height: 1; + pointer-events: none; +} +``` + +### Diagonal divider between hero and content + +```css +.page__hero--overlay { + clip-path: polygon(0 0, 100% 0, 100% calc(100% - 30px), 0 100%); + padding-bottom: calc(2em + 30px); /* compensate for clip */ +} +``` + +--- + +## Step 11: Update Print Styles + +**File:** `assets/css/main.scss` + +Add to the existing `@media print` block: + +```css +@media print { + /* Existing rules... */ + + /* Hide decorative elements */ + body::before, + body::after, + .page__hero--overlay::before, + .page__hero--overlay::after { + display: none; + } + + /* Disable animations */ + *, *::before, *::after { + animation: none !important; + transition: none !important; + } +} +``` + +--- + +## Step 12: Clean Up Duplicate `.archive__item:hover` + +**File:** `assets/css/main.scss` + +The `.archive__item:hover` rule is defined twice (once standalone at ~line 300, once grouped with `.toc:hover` at ~line 313). Consolidate into a single rule: + +```css +/* BEFORE (two separate blocks) */ +.archive__item:hover { + transform: translateY(-6px); + box-shadow: var(--glow-purple); + border-color: rgba(187, 154, 247, 0.4); +} + +/* ... later ... */ + +.archive__item:hover, +.toc:hover { + transform: translateY(-6px); + box-shadow: var(--glow-purple); + border-color: rgba(187, 154, 247, 0.4); +} + +/* AFTER (single grouped rule, remove the standalone) */ +.archive__item:hover, +.toc:hover { + transform: translateY(-6px); + box-shadow: var(--glow-purple); + border-color: rgba(187, 154, 247, 0.4); +} +``` + +--- + +## Step 13: Suppress `::before` Arrow on Nav Links + +**File:** `assets/css/main.scss` + +Add to the greedy-nav section: + +```css +.greedy-nav a::before { + display: none; +} +``` + +This prevents the external link arrow `↗` from appearing on the GitHub navigation link. + +--- + +## Step 14: Create Favicon + +**Files:** `assets/images/favicon.svg` (new), `_includes/head/custom.html` + +### Create SVG favicon + +Create a simplified version of `bio-photo.svg` that works at 16x16px — just the "JC" initials on a gradient background circle. + +### Add to `_includes/head/custom.html` + +```html + + + +``` + +**Note:** PNG versions (`favicon-32x32.png`, `apple-touch-icon.png`) require external conversion from the SVG using ImageMagick, Inkscape, or an online tool like realfavicongenerator.net. The SVG favicon alone provides good coverage for modern browsers. + +--- + +## Step 15: Create OG Image + +**File:** `assets/images/og-image.png` (new) + +Create a 1200×630 PNG with: +- Tokyo Night gradient background (`#1A1B26` → `#24283B`) +- Site title "Joel Cano" in Righteous font +- Subtitle "Software Developer & Technology Enthusiast" +- Decorative blue/purple gradient accent elements +- Match the SVG avatar aesthetic + +**Note:** This requires an external image tool (Figma, GIMP, ImageMagick). Cannot be generated as pure code in the Jekyll project. The `_config.yml` already references this path (`og_image: /assets/images/og-image.png`). + +--- + +## Step 16: Handle Blog Post Identity + +**File:** `_posts/2026-02-05-synthwave-redesign-deep-dive.markdown` + +Rather than rewriting 236 lines of narrative content, add a disclaimer note at the top of the post (after front matter): + +```markdown +> **Note (February 2026):** This post was written during the initial design concept phase when the site used an Outrun/Synthwave palette. The design has since evolved into a **Tokyo Night** aesthetic with updated colors, Geist typography, and enhanced visual effects. The design process and architectural decisions described here remain accurate — only the specific color values have changed. +{: .notice--info} +``` + +Also update the front matter: +- Change `overlay_color` to `"#1A1B26"` (step 2) +- Update tags to include `tokyo-night` alongside `synthwave` for historical reference + +--- + +## Step 17: Remove Legacy `blog/` Directory + +**Files to delete:** `blog/atom.xml`, `blog/index.html`, `blog/` directory + +These are artifacts from before the Minimal Mistakes theme migration: +- `blog/index.html` — basic post listing, replaced by the home layout + year-archive +- `blog/atom.xml` — old Atom feed, replaced by `jekyll-feed` plugin + +Confirm they're not referenced anywhere: +- Not in `_config.yml` ✓ +- Not in `_data/navigation.yml` ✓ +- Not in any `_pages/` files ✓ +- Uses old URL format (`http://` not `https://`) confirming staleness ✓ + +```bash +rm -rf blog/ +``` + +--- + +## Verification Checklist + +After all changes: + +- [ ] `bundle exec jekyll clean && bundle exec jekyll serve` — no build errors +- [ ] Hero overlay color matches body background seamlessly (no visible seam) +- [ ] Geist font renders correctly — check Network tab for successful CDN load +- [ ] No FOUT/FOIT — font loads with `display: swap` behavior +- [ ] Fluid type scales smoothly from 320px to 1440px (drag browser width) +- [ ] At each breakpoint (480px, 768px, 1024px), type sizes are appropriate +- [ ] All notice types display correct border colors (info=cyan, warning=orange, success=green, danger=red, primary=purple) +- [ ] Date metadata displays in orange with mono font +- [ ] Scroll animations work in Chrome/Edge (CSS `animation-timeline` supported) +- [ ] In Firefox/Safari, archive items are visible normally (no invisible content) +- [ ] Page content `fadeInUp` stagger works on load +- [ ] Hero title shows gradient sweep animation +- [ ] Noise grain overlay is barely perceptible — adds texture without harming readability +- [ ] Secondary purple glow visible in bottom-right corner +- [ ] Grid lines at 5% opacity — present but not distracting +- [ ] Floating gradient orb visible behind hero area +- [ ] Blockquote decorative quotes visible but subtle (12% opacity) +- [ ] Hero diagonal clip-path creates clean angular divider +- [ ] Favicon appears in browser tab (SVG version) +- [ ] Print preview: white background, no decorative elements, no animations +- [ ] External link arrow `↗` does NOT appear on nav links +- [ ] Blog post disclaimer note renders correctly +- [ ] `/blog/` returns 404 (legacy files removed) +- [ ] Lighthouse performance audit: no regressions from new CSS +- [ ] All links in navigation still work diff --git a/.github/skills/front-design/SKILL.md b/.github/skills/front-design/SKILL.md new file mode 100644 index 0000000..600b6db --- /dev/null +++ b/.github/skills/front-design/SKILL.md @@ -0,0 +1,42 @@ +--- +name: frontend-design +description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics. +license: Complete terms in LICENSE.txt +--- + +This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices. + +The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints. + +## Design Thinking + +Before coding, understand the context and commit to a BOLD aesthetic direction: +- **Purpose**: What problem does this interface solve? Who uses it? +- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction. +- **Constraints**: Technical requirements (framework, performance, accessibility). +- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember? + +**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity. + +Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is: +- Production-grade and functional +- Visually striking and memorable +- Cohesive with a clear aesthetic point-of-view +- Meticulously refined in every detail + +## Frontend Aesthetics Guidelines + +Focus on: +- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font. +- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. +- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. +- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density. +- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays. + +NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character. + +Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations. + +**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well. + +Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision. \ No newline at end of file diff --git a/_includes/head/custom.html b/_includes/head/custom.html index 0b05091..ec5ae39 100644 --- a/_includes/head/custom.html +++ b/_includes/head/custom.html @@ -1,3 +1,34 @@ - - - + + + + + + + + + + + diff --git a/_pages/about.markdown b/_pages/about.markdown index 5bde592..8e929ad 100644 --- a/_pages/about.markdown +++ b/_pages/about.markdown @@ -15,11 +15,13 @@ I'm **Joel Cano**, a passionate software developer who loves crafting elegant so ## What I Do -I'm a software developer with a keen interest in building reliable, scalable, and maintainable applications. I believe in the power of clean code, continuous learning, and sharing knowledge with the community. +I'm a software developer with a deep focus on **software architecture** and building reliable, scalable, and maintainable applications. I specialize in **Ruby on Rails** and **JavaScript** development, crafting well-architected systems that balance pragmatism with elegant design. I believe in the power of clean code, thoughtful architecture, continuous learning, and sharing knowledge with the community. ## My Interests -- **Software Development**: From design patterns to best practices, I'm always exploring ways to write better code +- **Software Architecture**: Designing scalable systems, exploring architectural patterns, and optimizing code organization at scale +- **Ruby on Rails & JavaScript**: Building full-stack applications with elegant, maintainable code +- **Design Patterns**: From SOLID principles to domain-driven design, studying how great architectures are structured - **Open Source**: Contributing to and learning from the global developer community - **Technology Trends**: Staying current with emerging technologies and industry developments - **Problem Solving**: Finding creative solutions to technical challenges @@ -30,11 +32,11 @@ I'm a software developer with a keen interest in building reliable, scalable, an - Python - JavaScript/TypeScript - Ruby -- Go ### Frameworks & Tools +- **Ruby on Rails** - Full-stack web development +- **JavaScript/Node.js** - Backend and frontend development - React / Vue.js -- Node.js - Django / Flask - Docker - Git diff --git a/_posts/2026-02-05-synthwave-redesign-deep-dive.markdown b/_posts/2026-02-05-synthwave-redesign-deep-dive.markdown index 5bafdf1..3231076 100644 --- a/_posts/2026-02-05-synthwave-redesign-deep-dive.markdown +++ b/_posts/2026-02-05-synthwave-redesign-deep-dive.markdown @@ -2,16 +2,19 @@ title: "Behind the Screens: A Deep Dive into My Site's Synthwave Redesign" date: 2026-02-05 categories: [design, web-development, process] -tags: [jekyll, minimal-mistakes, synthwave, css, frontend-design] +tags: [jekyll, minimal-mistakes, synthwave, tokyo-night, css, frontend-design] excerpt: "A detailed look at how I transformed my personal site with an Outrun/Synthwave aesthetic, complete with the planning process, design decisions, and technical implementation using opencode." header: - overlay_color: "#0f0f1e" + overlay_color: "#1A1B26" overlay_filter: "0.5" toc: true toc_label: "What We'll Cover" toc_icon: "palette" --- +> **Note (February 2026):** This post was written during the initial design concept phase when the site used an Outrun/Synthwave palette. The design has since evolved into a **Tokyo Night** aesthetic with updated colors, Geist typography, and enhanced visual effects. The design process and architectural decisions described here remain accurate — only the specific color values have changed. +{: .notice--info} + ## Introduction Every now and then, a website needs more than just new content—it needs a soul transplant. After living with my Jekyll site for a while, I realized it was time for something bolder, something that truly reflected my personality as a developer who appreciates both retro nostalgia and futuristic tech. diff --git a/assets/css/main.scss b/assets/css/main.scss index 84598af..fb481ec 100644 --- a/assets/css/main.scss +++ b/assets/css/main.scss @@ -5,11 +5,11 @@ @import "minimal-mistakes"; /* ============================================ - SYNTHWAVE DESIGN SYSTEM - Outrun/Retro-Futuristic Theme + TOKYO NIGHT DESIGN SYSTEM + Developer-Centric Dark Theme ============================================ */ -/* CSS Custom Properties (Design Tokens) - Tokyo Night Theme */ +/* CSS Custom Properties (Design Tokens) */ :root { /* Background Colors */ --bg-primary: #1A1B26; @@ -39,7 +39,7 @@ /* Typography */ --font-display: 'Righteous', sans-serif; - --font-body: 'Inter', sans-serif; + --font-body: 'Geist', sans-serif; --font-mono: 'JetBrains Mono', monospace; /* Spacing */ @@ -49,12 +49,82 @@ --space-lg: 2rem; --space-xl: 4rem; + /* Fluid Typography */ + --type-hero: clamp(2rem, 5vw, 3.5rem); + --type-h1: clamp(1.75rem, 3.5vw, 2.75rem); + --type-h2: clamp(1.4rem, 2.5vw, 2rem); + --type-h3: clamp(1.15rem, 2vw, 1.5rem); + --type-h4: clamp(1rem, 1.5vw, 1.25rem); + --type-body: clamp(0.95rem, 1.1vw, 1.1rem); + --type-small: clamp(0.8rem, 0.9vw, 0.9rem); + /* Transitions */ --transition-fast: 0.2s ease; --transition-medium: 0.3s ease; --transition-slow: 0.4s ease; } +/* Light theme - respects prefers-color-scheme OR user selection */ +@media (prefers-color-scheme: light) { + :root:not([data-theme]) { + /* Background Colors - Light theme */ + --bg-primary: #FAFAFA; + --bg-secondary: #F5F5F5; + --bg-card: #FFFFFF; + --bg-code: #F8F8F8; + + /* Accent Colors - Same across both themes */ + --accent-blue: #5BA3F5; + --accent-purple: #A855F7; + --accent-green: #84CC16; + --accent-cyan: #06B6D4; + --accent-red: #EF4444; + --accent-orange: #F97316; + --accent-yellow: #FBBF24; + + /* Text Colors - Light theme */ + --text-primary: #1F2937; + --text-secondary: #4B5563; + --text-muted: #9CA3AF; + + /* Border & Glow - Light theme */ + --border-color: rgba(91, 163, 245, 0.15); + --glow-blue: 0 0 20px rgba(91, 163, 245, 0.2); + --glow-purple: 0 0 20px rgba(168, 85, 247, 0.2); + --glow-cyan: 0 0 20px rgba(6, 182, 212, 0.2); + } +} + +/* Explicit light theme via data-theme attribute */ +:root[data-theme="light"] { + /* Background Colors - Light theme */ + --bg-primary: #FAFAFA; + --bg-secondary: #F5F5F5; + --bg-card: #FFFFFF; + --bg-code: #F8F8F8; + + /* Accent Colors - Same across both themes */ + --accent-blue: #5BA3F5; + --accent-purple: #A855F7; + --accent-green: #84CC16; + --accent-cyan: #06B6D4; + --accent-red: #EF4444; + --accent-orange: #F97316; + --accent-yellow: #FBBF24; + + /* Text Colors - Light theme */ + --text-primary: #1F2937; + --text-secondary: #4B5563; + --text-muted: #9CA3AF; + + /* Border & Glow - Light theme */ + --border-color: rgba(91, 163, 245, 0.15); + --glow-blue: 0 0 20px rgba(91, 163, 245, 0.2); + --glow-purple: 0 0 20px rgba(168, 85, 247, 0.2); + --glow-cyan: 0 0 20px rgba(6, 182, 212, 0.2); +} + + /* ============================================ BASE STYLES ============================================ */ @@ -67,13 +137,16 @@ html { body { font-family: var(--font-body); + font-size: var(--type-body); color: var(--text-primary); - background: linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%); + background: + radial-gradient(ellipse at 85% 85%, rgba(187, 154, 247, 0.04) 0%, transparent 50%), + linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 100%); background-attachment: fixed; line-height: 1.7; } -/* Grid Pattern Overlay */ +/* Grid Pattern Overlay - Dark theme */ body::before { content: ''; position: fixed; @@ -82,13 +155,45 @@ body::before { right: 0; bottom: 0; background-image: - linear-gradient(rgba(122, 162, 247, 0.03) 1px, transparent 1px), - linear-gradient(90deg, rgba(122, 162, 247, 0.03) 1px, transparent 1px); + linear-gradient(rgba(122, 162, 247, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(122, 162, 247, 0.05) 1px, transparent 1px); background-size: 60px 60px; pointer-events: none; z-index: -1; } +/* Grid Pattern Overlay - Light theme */ +:root[data-theme="light"] body::before { + background-image: + linear-gradient(rgba(91, 163, 245, 0.08) 1px, transparent 1px), + linear-gradient(90deg, rgba(91, 163, 245, 0.08) 1px, transparent 1px); +} + +@media (prefers-color-scheme: light) { + body:not([data-theme])::before { + background-image: + linear-gradient(rgba(91, 163, 245, 0.08) 1px, transparent 1px), + linear-gradient(90deg, rgba(91, 163, 245, 0.08) 1px, transparent 1px); + } +} + +/* Noise grain overlay */ +body::after { + content: ''; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + z-index: 9999; + opacity: 0.025; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); + background-repeat: repeat; + background-size: 256px 256px; + mix-blend-mode: overlay; +} + /* ============================================ TYPOGRAPHY ============================================ */ @@ -101,14 +206,24 @@ h1, h2, h3, h4, h5, h6 { } h1 { + font-size: var(--type-h1); + letter-spacing: -0.02em; color: var(--accent-blue); } h2 { + font-size: var(--type-h2); + letter-spacing: -0.01em; color: var(--accent-purple); } -h3, h4 { +h3 { + font-size: var(--type-h3); + color: var(--text-primary); +} + +h4 { + font-size: var(--type-h4); color: var(--text-primary); } @@ -161,11 +276,21 @@ a[href^="http"]::before { ============================================ */ .masthead { - background: rgba(15, 15, 30, 0.95); + background: rgba(26, 27, 38, 0.95); backdrop-filter: blur(10px); border-bottom: 1px solid var(--border-color); } +:root[data-theme="light"] .masthead { + background: rgba(255, 255, 255, 0.95); +} + +@media (prefers-color-scheme: light) { + .masthead:not([data-theme]) { + background: rgba(255, 255, 255, 0.95); + } +} + .masthead__inner-wrap { padding: 0.75em 1em; } @@ -189,6 +314,10 @@ a[href^="http"]::before { display: none; } +.greedy-nav a[href^="http"]::before { + display: none; +} + .greedy-nav a:hover { color: var(--accent-cyan); text-shadow: 0 0 10px var(--accent-cyan); @@ -217,10 +346,30 @@ a[href^="http"]::before { .page__hero--overlay { background: linear-gradient(180deg, - rgba(15, 15, 30, 0.7) 0%, - rgba(26, 26, 62, 0.9) 100% + rgba(26, 27, 38, 0.7) 0%, + rgba(36, 40, 59, 0.9) 100% ); - padding: 3em 0 2em; + padding: 3em 0 calc(2em + 30px); + clip-path: polygon(0 0, 100% 0, 100% calc(100% - 30px), 0 100%); + position: relative; + overflow: visible; +} + +/* Hero overlay - Light theme */ +:root[data-theme="light"] .page__hero--overlay { + background: linear-gradient(180deg, + rgba(245, 245, 245, 0.85) 0%, + rgba(255, 255, 255, 0.95) 100% + ); +} + +@media (prefers-color-scheme: light) { + .page__hero--overlay:not([data-theme]) { + background: linear-gradient(180deg, + rgba(245, 245, 245, 0.85) 0%, + rgba(255, 255, 255, 0.95) 100% + ); + } } .page__hero--overlay::before { @@ -236,13 +385,66 @@ a[href^="http"]::before { pointer-events: none; } +:root[data-theme="light"] .page__hero--overlay::before { + background: + radial-gradient(ellipse at 50% 0%, rgba(91, 163, 245, 0.08) 0%, transparent 50%), + radial-gradient(ellipse at 50% 100%, rgba(168, 85, 247, 0.06) 0%, transparent 50%); +} + +@media (prefers-color-scheme: light) { + .page__hero--overlay:not([data-theme])::before { + background: + radial-gradient(ellipse at 50% 0%, rgba(91, 163, 245, 0.08) 0%, transparent 50%), + radial-gradient(ellipse at 50% 100%, rgba(168, 85, 247, 0.06) 0%, transparent 50%); + } +} + +.page__hero--overlay::after { + content: ''; + position: absolute; + top: -20%; + right: -10%; + width: 400px; + height: 400px; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(122, 162, 247, 0.08) 0%, + rgba(187, 154, 247, 0.04) 40%, + transparent 70% + ); + filter: blur(60px); + pointer-events: none; + z-index: 0; +} + +:root[data-theme="light"] .page__hero--overlay::after { + background: radial-gradient( + circle, + rgba(168, 85, 247, 0.06) 0%, + rgba(168, 85, 247, 0.02) 40%, + transparent 70% + ); +} + +@media (prefers-color-scheme: light) { + .page__hero--overlay:not([data-theme])::after { + background: radial-gradient( + circle, + rgba(168, 85, 247, 0.06) 0%, + rgba(168, 85, 247, 0.02) 40%, + transparent 70% + ); + } +} + .page__hero-image { filter: saturate(1.2) contrast(1.1); } .page__title { font-family: var(--font-display); - font-size: 2.5em; + font-size: var(--type-hero); text-align: center; margin-bottom: 0.3em; animation: fadeInUp 0.6s ease-out; @@ -250,7 +452,7 @@ a[href^="http"]::before { .page__lead { font-family: var(--font-body); - font-size: 1.2em; + font-size: clamp(1rem, 1.5vw, 1.3rem); color: var(--text-secondary); text-align: center; max-width: 600px; @@ -261,7 +463,7 @@ a[href^="http"]::before { /* Tagline with blinking cursor - styled for content area */ .hero-tagline { font-family: var(--font-mono); - font-size: 1.1em; + font-size: clamp(0.95rem, 1.3vw, 1.2rem); color: var(--accent-cyan); text-align: center; margin: 1em 0; @@ -272,6 +474,16 @@ a[href^="http"]::before { letter-spacing: 0.05em; } +:root[data-theme="light"] .hero-tagline { + background: rgba(6, 182, 212, 0.08); +} + +@media (prefers-color-scheme: light) { + .hero-tagline:not([data-theme]) { + background: rgba(6, 182, 212, 0.08); + } +} + .hero-tagline::after { content: '|'; animation: blink 1s infinite; @@ -308,12 +520,6 @@ a[href^="http"]::before { transition: all var(--transition-medium); } -.archive__item:hover { - transform: translateY(-6px); - box-shadow: var(--glow-purple); - border-color: rgba(187, 154, 247, 0.4); -} - .toc, .page__related, .pagination { @@ -330,6 +536,18 @@ a[href^="http"]::before { border-color: rgba(187, 154, 247, 0.4); } +:root[data-theme="light"] .archive__item:hover, +:root[data-theme="light"] .toc:hover { + border-color: rgba(168, 85, 247, 0.3); +} + +@media (prefers-color-scheme: light) { + .archive__item:hover:not([data-theme]), + .toc:hover:not([data-theme]) { + border-color: rgba(168, 85, 247, 0.3); + } +} + .archive__item-title { font-family: var(--font-display); color: var(--text-primary); @@ -448,6 +666,20 @@ a[href^="http"]::before { overflow-x: auto; } +:root[data-theme="light"] .highlight { + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.03), + 0 0 30px rgba(91, 163, 245, 0.05); +} + +@media (prefers-color-scheme: light) { + .highlight:not([data-theme]) { + box-shadow: + inset 0 0 20px rgba(0, 0, 0, 0.03), + 0 0 30px rgba(91, 163, 245, 0.05); + } +} + .highlight code { font-family: var(--font-mono); font-size: 0.9em; @@ -477,11 +709,45 @@ a[href^="http"]::before { font-size: 0.9em; } +/* Light theme syntax highlighting */ +:root[data-theme="light"] .highlight .c { color: #6B7280; } /* Comment */ +:root[data-theme="light"] .highlight .k { color: #A855F7; } /* Keyword */ +:root[data-theme="light"] .highlight .s { color: #84CC16; } /* String */ +:root[data-theme="light"] .highlight .mi { color: #F97316; } /* Number */ +:root[data-theme="light"] .highlight .nf { color: #5BA3F5; } /* Function */ +:root[data-theme="light"] .highlight .nc { color: #FBBF24; } /* Class */ +:root[data-theme="light"] .highlight .o { color: #A855F7; } /* Operator */ +:root[data-theme="light"] .highlight .p { color: #4B5563; } /* Punctuation */ +:root[data-theme="light"] .highlight .nb { color: #06B6D4; } /* Built-in */ +:root[data-theme="light"] .highlight .nv { color: #1F2937; } /* Variable */ +:root[data-theme="light"] .highlight .nl { color: #EF4444; } /* Label */ + +:root[data-theme="light"] .highlighter-rouge { + background: rgba(91, 163, 245, 0.08); + color: var(--accent-cyan); +} + +@media (prefers-color-scheme: light) { + .highlight:not([data-theme]) .c { color: #6B7280; } /* Comment */ + .highlight:not([data-theme]) .k { color: #A855F7; } /* Keyword */ + .highlight:not([data-theme]) .s { color: #84CC16; } /* String */ + .highlight:not([data-theme]) .mi { color: #F97316; } /* Number */ + .highlight:not([data-theme]) .nf { color: #5BA3F5; } /* Function */ + .highlight:not([data-theme]) .nc { color: #FBBF24; } /* Class */ + .highlight:not([data-theme]) .o { color: #A855F7; } /* Operator */ + .highlight:not([data-theme]) .p { color: #4B5563; } /* Punctuation */ + .highlight:not([data-theme]) .nb { color: #06B6D4; } /* Built-in */ + .highlight:not([data-theme]) .nv { color: #1F2937; } /* Variable */ + .highlight:not([data-theme]) .nl { color: #EF4444; } /* Label */ +} + /* ============================================ BLOCKQUOTES ============================================ */ blockquote { + position: relative; + overflow: visible; border-left: 4px solid var(--accent-purple); background: rgba(187, 154, 247, 0.05); padding: 1em 1.5em; @@ -491,6 +757,29 @@ blockquote { border-radius: 0 8px 8px 0; } +:root[data-theme="light"] blockquote { + background: rgba(168, 85, 247, 0.03); +} + +@media (prefers-color-scheme: light) { + blockquote:not([data-theme]) { + background: rgba(168, 85, 247, 0.03); + } +} + +blockquote::before { + content: '"'; + position: absolute; + top: -0.3em; + left: -0.15em; + font-family: var(--font-display); + font-size: 4em; + color: var(--accent-purple); + opacity: 0.12; + line-height: 1; + pointer-events: none; +} + blockquote p:last-child { margin-bottom: 0; } @@ -581,7 +870,17 @@ td { } tr:hover td { - background: rgba(0, 217, 255, 0.05); + background: rgba(125, 207, 255, 0.05); +} + +:root[data-theme="light"] tr:hover td { + background: rgba(91, 163, 245, 0.08); +} + +@media (prefers-color-scheme: light) { + tr:hover td:not([data-theme]) { + background: rgba(91, 163, 245, 0.08); + } } /* ============================================ @@ -589,12 +888,22 @@ tr:hover td { ============================================ */ .page__footer { - background: rgba(15, 15, 30, 0.95); + background: rgba(26, 27, 38, 0.95); border-top: 1px solid var(--border-color); padding: 2em 0; margin-top: 3em; } +:root[data-theme="light"] .page__footer { + background: rgba(255, 255, 255, 0.95); +} + +@media (prefers-color-scheme: light) { + .page__footer:not([data-theme]) { + background: rgba(255, 255, 255, 0.95); + } +} + .page__footer a { color: var(--text-secondary); } @@ -633,7 +942,7 @@ tr:hover td { } .pagination--pager:hover { - background: rgba(0, 217, 255, 0.1); + background: rgba(125, 207, 255, 0.1); border-color: var(--accent-cyan); } @@ -654,11 +963,40 @@ tr:hover td { } .notice--warning { - border-left-color: var(--accent-blue); + border-left-color: var(--accent-orange); } .notice--success { - border-left-color: #00ff88; + border-left-color: var(--accent-green); +} + +.notice--danger { + border-left-color: var(--accent-red); +} + +/* Date metadata styling */ +.page__meta, +.archive__item .page__meta { + color: var(--accent-orange); + font-family: var(--font-mono); + font-size: 0.8em; + letter-spacing: 0.02em; +} + +/* Mark/highlight styling */ +mark { + background: rgba(224, 175, 104, 0.15); + color: var(--accent-yellow); + padding: 0.1em 0.3em; + border-radius: 3px; +} + +/* Blockquote citation */ +blockquote cite, +blockquote footer { + color: var(--accent-yellow); + font-style: normal; + font-size: 0.9em; } /* ============================================ @@ -718,30 +1056,16 @@ tr:hover td { ============================================ */ @media (max-width: 1024px) { - .page__title { - font-size: 3em; - } - .sidebar { margin-top: 2em; } } @media (max-width: 768px) { - .page__title { - font-size: 2.2em; - } - .page__lead { - font-size: 1.1em; padding: 0 1em; } - .hero-tagline { - font-size: 1.1em; - margin: 1.5em 1em; - } - .archive__item, .toc { padding: 1em; @@ -764,37 +1088,18 @@ tr:hover td { display: none; } - .archive__item-title { - font-size: 1.3em; - } - - .page__content h2 { - font-size: 1.6em; - } - .author__avatar img { max-width: 120px; } } @media (max-width: 480px) { - .page__title { - font-size: 1.8em; - } - .page__hero--overlay { padding: 3em 0 2em; } - .hero-tagline { - font-size: 1em; - padding: 0.75em; - margin: 1em 0.5em; - } - .btn { padding: 0.6em 1.2em; - font-size: 0.9em; } .greedy-nav .visible-links { @@ -848,6 +1153,20 @@ tr:hover td { a::after { display: none; } + + /* Hide decorative elements */ + body::before, + body::after, + .page__hero--overlay::before, + .page__hero--overlay::after { + display: none; + } + + /* Disable animations */ + *, *::before, *::after { + animation: none !important; + transition: none !important; + } } /* ============================================ @@ -870,7 +1189,7 @@ tr:hover td { /* Staggered page load animation */ .page__content > * { - animation: fadeInUp 0.5s ease-out backwards; + animation: fadeInUp 0.5s ease-out both; } .page__content > *:nth-child(1) { animation-delay: 0.1s; } @@ -878,3 +1197,85 @@ tr:hover td { .page__content > *:nth-child(3) { animation-delay: 0.3s; } .page__content > *:nth-child(4) { animation-delay: 0.4s; } .page__content > *:nth-child(5) { animation-delay: 0.5s; } + +/* Hero title gradient sweep */ +.page__title { + background: linear-gradient( + 90deg, + var(--accent-blue) 0%, + var(--accent-purple) 40%, + var(--accent-cyan) 60%, + var(--text-primary) 100% + ); + background-size: 200% 100%; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: gradientSweep 1.5s ease-out forwards, fadeInUp 0.6s ease-out; +} + +@keyframes gradientSweep { + from { background-position: 100% 50%; } + to { background-position: 0% 50%; } +} + +/* Scroll-triggered animations — progressive enhancement only */ +@supports (animation-timeline: view()) { + .archive__item { + opacity: 0; + transform: translateY(30px); + animation: fadeInUp 0.6s ease-out both; + animation-timeline: view(); + animation-range: entry 0% entry 30%; + } + + .archive__item:nth-child(even) { + animation-delay: 0.1s; + } +} +/* ============================================ + THEME TOGGLE BUTTON + ============================================ */ + +#theme-toggle { + position: fixed; + top: 0.75em; + right: 1.5em; + z-index: 9998; + width: 2.5em; + height: 2.5em; + border: 1px solid var(--border-color); + background: var(--bg-card); + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.3em; + line-height: 1; + transition: all var(--transition-fast); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + padding: 0; + font-weight: normal; + text-align: center; +} + +#theme-toggle:hover { + transform: scale(1.1); + box-shadow: var(--glow-purple); + border-color: var(--accent-purple); +} + +#theme-toggle:active { + transform: scale(0.95); +} + +@media (max-width: 768px) { + #theme-toggle { + width: 2.2em; + height: 2.2em; + font-size: 1.1em; + top: 0.75em; + right: 1em; + } +} \ No newline at end of file diff --git a/assets/images/favicon.svg b/assets/images/favicon.svg new file mode 100644 index 0000000..c257d34 --- /dev/null +++ b/assets/images/favicon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + JC + \ No newline at end of file diff --git a/assets/js/theme-toggle.js b/assets/js/theme-toggle.js new file mode 100644 index 0000000..ef7bf9b --- /dev/null +++ b/assets/js/theme-toggle.js @@ -0,0 +1,94 @@ +// Theme Toggle Script +(function() { + const THEME_KEY = 'theme-preference'; + const DARK_THEME = 'dark'; + const LIGHT_THEME = 'light'; + + // Get the saved theme preference or system preference + function getThemePreference() { + const saved = localStorage.getItem(THEME_KEY); + if (saved) { + return saved; + } + + // Check system preference + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { + return LIGHT_THEME; + } + + // Default to dark + return DARK_THEME; + } + + // Apply theme to document + function applyTheme(theme) { + const html = document.documentElement; + html.setAttribute('data-theme', theme); + localStorage.setItem(THEME_KEY, theme); + updateToggleButton(theme); + } + + // Update toggle button appearance + function updateToggleButton(theme) { + const btn = document.getElementById('theme-toggle'); + if (btn) { + btn.setAttribute('data-theme-current', theme); + btn.setAttribute('aria-label', `Switch to ${theme === DARK_THEME ? 'light' : 'dark'} theme`); + btn.textContent = theme === DARK_THEME ? '☀️' : '🌙'; + } + } + + // Create and inject toggle button + function createToggleButton() { + const currentTheme = document.documentElement.getAttribute('data-theme') || DARK_THEME; + const btn = document.createElement('button'); + btn.id = 'theme-toggle'; + btn.setAttribute('type', 'button'); + btn.textContent = currentTheme === DARK_THEME ? '☀️' : '🌙'; + btn.setAttribute('aria-label', `Switch to ${currentTheme === DARK_THEME ? 'light' : 'dark'} theme`); + btn.onclick = function(e) { + e.preventDefault(); + toggleTheme(); + }; + document.body.appendChild(btn); + } + + // Initialize on page load + function init() { + const theme = getThemePreference(); + applyTheme(theme); + + // Create button immediately after applying theme + if (!document.getElementById('theme-toggle')) { + // Wait for DOM to be ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', createToggleButton); + } else { + createToggleButton(); + } + } + } + + // Expose toggle function + window.toggleTheme = function() { + const current = document.documentElement.getAttribute('data-theme') || DARK_THEME; + const next = current === DARK_THEME ? LIGHT_THEME : DARK_THEME; + applyTheme(next); + }; + + // Listen for system preference changes + if (window.matchMedia) { + window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (e) => { + if (!localStorage.getItem(THEME_KEY)) { + applyTheme(e.matches ? LIGHT_THEME : DARK_THEME); + } + }); + } + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/blog/atom.xml b/blog/atom.xml deleted file mode 100644 index c5e9fd5..0000000 --- a/blog/atom.xml +++ /dev/null @@ -1,23 +0,0 @@ ---- ---- - - - Joel Cano's Blog - - - {{ site.time | date_to_xmlschema }} - http://canito0890.github.io/blog - - Joel Cano - joel.cano.avila@gmail.com - - {% for post in site.posts %} - - {{ post.title }} - - {{ post.date | date_to_xmlschema }} - http://canito0890.github.io{{ post.id }} - {{ post.content | xml_escape }} - - {% endfor %} - diff --git a/blog/index.html b/blog/index.html deleted file mode 100644 index 1e291ac..0000000 --- a/blog/index.html +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: default -title: Joel Cano's Blog ---- -

{{ page.title }}

- diff --git a/index.markdown b/index.markdown index 1ca93a7..39d172e 100644 --- a/index.markdown +++ b/index.markdown @@ -2,7 +2,7 @@ layout: home author_profile: true header: - overlay_color: "#0f0f1e" + overlay_color: "#1A1B26" overlay_filter: "0.7" overlay_image: excerpt: "Welcome to my corner of the internet. Here I share thoughts on software development, technology, and continuous learning."