stealthpuppy

Changelog

A record of changes to the stealthpuppy.com theme and site design.

This changelog covers changes to the site theme and design. It does not track changes to individual blog posts or article content. Note: the site theme is not licensed for copying or modification.

2026-08-11

Reading experience

  • Reading progress bar — posts with a 6+ minute reading time now show a topbar progress indicator that fills as the article is scrolled; position is recalculated on scroll and resize, the topbar is made sticky so the bar stays visible, and the effect is skipped under prefers-reduced-motion
  • Collapsible tablet TOC — post pages gain a collapsible table of contents for tablet-width viewports; the TOC script now populates and keeps both the tablet and desktop navigation states in sync
  • Copy-action toast feedback — copying a post link or a code block now shows a toast notification, including a clearer failure message when clipboard access is unavailable
  • Mobile nav toggle state — the mobile sidebar toggle now supports distinct menu/close icons and truly toggles open/closed on each click; aria-expanded and aria-label are kept in sync with the current state
  • Breadcrumbs on tag/category pages — the category and tag listing layouts gain a breadcrumb trail styled for dark, light, and accessible themes, including hover states and a readable current-page label
  • Accent swatch tooltips — sidebar accent-theme swatches gain data-theme-label attributes and ::after hover/focus-visible tooltips showing the theme name, with light-mode styling and reduced-motion-safe behaviour
  • Decorative icons hidden from screen readers — aria-hidden="true" added to non-informative SVG icons in the shortcuts modal, sidebar fallback icon, back-to-top button, and the home “View all posts” link

Code blocks

  • Line numbers — code-blocks.js now wraps each <pre> in a two-column shell and generates line numbers based on the block’s actual line count; corresponding styles added for dark, light, and accessible themes; header insertion updated so language labels and copy controls still render correctly with the new wrapper
  • Loading state — the search modal now shows a spinner while the Lunr index is fetched and built, hidden on success, failure, or modal close

Site structure

  • Community page URL — the Community nav link now points to /community/; the page file was renamed from community_review.md to community.md so the URL and source filename match

2026-05-12

Community page

  • Award logo sizing fixed — award images gain a no-zoom class and .award-entry__logo now applies a fixed width (w-[84px]), left alignment, and resets border/radius, preventing inconsistent zoom/rounding across logos

2026-05-11

View transitions

  • MPA page transitions — @view-transition { navigation: auto } added to the CSS so the browser plays a cross-fade animation when navigating between pages; old and new root snapshots each animate for 250ms with ease-out timing; the entire effect is suppressed under prefers-reduced-motion: reduce
  • Shared hero image transition — the post-card thumbnail and the full-post hero image share a view-transition-name keyed to the post slug (post-img-{slug}), so the thumbnail morphs smoothly into the full hero image when opening a post
  • Scroll anchor offset — scroll-padding-top: 4rem added to html so anchor-linked headings clear the fixed topbar after a transition

Container queries

  • Post card container — .post-card now carries container-type: inline-size; container-name: post-card; the list-view layout rules (thumbnail width, body padding, excerpt clamp) are applied inside a @container post-card (min-width: 480px) query rather than a viewport media query, so cards adapt to their actual rendered width regardless of how the grid is configured

Focus ring refinement

  • Consistent 3px outline — the standard :focus-visible ring increased from 2px to 3px; the accessible-mode ring reduced from 4px to 3px, giving both a uniform thickness with a slightly larger offset

About & Community Review page — timeline components

  • Presentations timeline — the flat presentation list in _data/presentations.yml is now grouped by year via Liquid group_by and rendered as a visual vertical timeline with circular year markers and per-entry month badges; data fields changed from a single date string to structured year, month, and month_name fields
  • Awards timeline — the MVP award list in _data/awards.yml changed from a single years string to start / end integer fields (supporting "Present" as end value); rendered as a .award-timeline with logo, category, and date-range display
  • Publications timeline — the publications list on the Community Review page is now grouped by year (extracted from the existing date string) and rendered in the same vertical timeline style as presentations

Tailwind CSS

  • Upgraded to v4.3.0 — tailwindcss, @tailwindcss/cli, @tailwindcss/node, and @tailwindcss/oxide updated to v4.3.0

Post card image sizing

  • Responsive sizes refinement — post-card <source> sizes attribute gains a 1280px breakpoint (50vw) and a 33vw fallback for larger grids
  • List-view layout scoped to selectors — the previous container-query-based list view rules were replaced with scoped #posts-container.posts-list selectors (fixed thumbnail width, no max-height, adjusted body padding, single-line excerpt truncation), keeping grid thumbnails consistent while avoiding container-query side effects in list view

2026-05-09

Post hero image

  • Height capped across all display sizes — the post hero image container previously used aspect-[21/9] with no maximum height, producing a 703px-tall hero at 1080p, 977px at 1440p, and 1526px at 4K; the aspect ratio has been replaced with height: clamp(220px, 35vw, 480px), which scales smoothly from 220px on mobile (up from 161px — better visual impact) through ~350px at 720p, and caps at 480px at 1080p and above regardless of viewport width
  • LCP performance attributes — the hero <img> element now carries fetchpriority="high", loading="eager", and decoding="auto", signalling to the browser that this is the Largest Contentful Paint element and should be loaded as a high-priority resource
  • Title overlay on hero — the post title, category label, and metadata bar are now rendered inside the hero image over a gradient overlay (transparent → rgba(0,0,0,0.85)), removing the visual gap between image and content; hero height increased to clamp(360px, 48vw, 580px) to accommodate the text comfortably; the image is inset with content-matching padding (px-6 pt-6 lg:px-10) and given rounded corners (rounded-xl) and a shadow; the attribution pin moves to the top-right corner to avoid overlap with the title text; posts without a hero image fall back to the original full header layout below the fold; the sizes attribute on the responsive <source> element updated to account for padding (calc(100vw - 48px) on mobile, calc(100vw - 360px) on desktop)

2026-05-07

Accessibility & visual improvements

  • Title hierarchy — post page title bumped to text-3xl sm:text-4xl lg:text-5xl with tracking-tight; .post-content h1 reduced from 36px to 30px so in-body H1s never exceed the page title; about and blog page titles gain sm:text-4xl for visual parity
  • Accent contrast token — a new --color-accent-deepest CSS token (one shade darker than --color-accent-dark) is defined for all eight themes, the rainbow theme, and the accessible theme; it is used for any background surface that pairs with white text or icons, bringing those surfaces to WCAG 2.1 AA contrast; affected components: view-toggle active state, skip link, back-to-top button, project card link, project card tags, category filter active button, copy-URL button, and code block language header
  • Reading column width — post main content area narrowed from max-w-3xl (~768px) to max-w-[68ch] for a more comfortable ~65–70 character line length; text-wrap: balance applied to all post and card headings; text-wrap: pretty applied to post body paragraphs and list items
  • Sidebar navigation — deduplicated the sidebar: the “Navigate” section (which duplicated main nav links) is replaced by a “More” section containing Archive and RSS Feed only; primary navigation (About, Blog, Projects) is now driven by site.menu in the sidebar header; RSS Feed link moved from the Connect section into More
  • Theme-aware hero dots — the homepage radial dot pattern used a hardcoded indigo colour regardless of the active accent theme; it now uses color-mix(in srgb, var(--color-accent) 20%, transparent) in dark mode and var(--color-accent-dark) 12% in light mode, so the dots shift colour when the accent theme is changed
  • Code comment contrast — dark-mode syntax comment colour raised from #64748b (slate-500, ~3.5:1) to #94a3b8 (slate-400, ~5.4:1) against the #1e293b code background, clearing WCAG AA for body text
  • Touch targets — .dark-toggle gains min-width: 44px; min-height: 44px; .accent-swatch gains an invisible ::before expanded hit area (~36px total); .post-tag-pill padding increased from py-0.5 to py-1.5 with min-height: 32px

2026-04-03

Bug fixes

  • Share buttons — full URL — url: https://stealthpuppy.com is now set in _config.yml so Jekyll’s absolute_url filter produces a full URL in LinkedIn and Bluesky share buttons; previously only the relative path (e.g. /evergreen-workbench/) was passed
  • Utterances comments label — the utterances label attribute was incorrectly set to page.url (a URL path) instead of site.utterance.label (comment); GitHub issues created by comments were not tagged with the correct label and did not appear in the repository issues list
  • Dark mode flash on navigation — an inline <style>html.dark{background:#0f172a}</style> is now injected in <head> immediately after the theme-init script, so the dark background colour is applied before style.css loads, eliminating the white flash visible when navigating between pages in dark mode

Accent themes

  • Rainbow theme — a new rainbow accent option replaces the gray swatch; when selected, all accent-coloured elements (links, active nav, focus rings, code highlights) smoothly cycle through the full colour spectrum via a CSS @keyframes animation on a registered @property --accent-hue; the swatch displays a conic gradient preview

2026-03-26

Post metadata

  • Updated date — a “Updated” date now appears in the post header metadata strip, sourced from page.last_modified_at (via jekyll-last-modified-at), showing when the post file was last committed to git; it only renders when the updated date differs from the published date
  • Category icon — the category icon in post metadata changed from a price-tag shape to a folder icon, better representing the concept of a category
  • Tag pills — tag pills in the post header now include a small tag SVG icon before the tag name, and each pill links to its tag landing page

Outdated / content warning

  • A new front-matter flag outdated: true on a post renders a warning callout below the post header, alerting readers that the content may no longer be accurate
  • The warning text is configurable via outdated_notice in front matter; a default message is shown if the field is omitted

Series navigation

  • Posts that belong to a series can set series and series_part in front matter; a series navigation block listing all parts (with the current part highlighted) is rendered between the post summary and the article body
  • The duplicate series/part label that previously appeared in the post-meta strip has been removed — series information is now only shown in the dedicated series navigation block

Post layout order

  • The post summary (description callout) is now rendered above the series navigation block, so the description always appears immediately after the post header

Projects page

  • A page-level “Updated” date now appears in the Projects page header, showing when projects.md was last committed to git

Share buttons

  • Share buttons now use solid filled brand colours (LinkedIn #0A66C2, Bluesky #1185FE) instead of the previous ghost/outline style
  • The “Copy link” button uses the current accent colour; it briefly turns green on successful copy
  • Brand colours are consistent across light and dark modes

2026-03-23

Accessible theme

A new high-contrast accessible theme designed for low vision and colour-blind users:

  • Yellow-on-black palette — foreground #fde047 on #000 background; contrast ratio ~15:1 (exceeds WCAG AAA 7:1 requirement)
  • Dedicated toggle button — eye icon button in the topbar and sidebar, separate from the light/dark toggle; button shows a filled/pressed state when active
  • Always-underlined links — all links show underlines regardless of hover, removing colour as the sole link indicator
  • Yellow borders — all card, callout, sidebar, and code block borders switched to yellow for visibility
  • Visible focus rings — focus outlines use yellow and are always shown, not suppressed by :focus-visible
  • Desaturated hero images — post hero images rendered in greyscale to reduce visual noise
  • Accent swatches hidden — the colour accent picker is hidden in accessible mode (accent colour is fixed to yellow)
  • Accessible state persists across reloads — stored in localStorage under key accessible, independent of the dark/light theme key
  • New icon — _includes/icons/eye.svg (Lucide eye) added for the accessible toggle button

Theme toggle

  • Separated into two independent controls: a binary light/dark toggle (moon/sun icons) and a dedicated accessible theme button (eye icon)
  • Previously the single toggle cycled through three states; it is now a simple light ↔ dark binary, with accessible mode enabled and disabled via its own button
  • Exiting accessible mode via the dark toggle enters dark mode; exiting via the accessible button restores the previously saved light/dark preference

Topbar icon consistency

  • Fixed a CSS specificity issue where the <span> wrappers around moon/sun icons inherited a different colour than the unwrapped search icon in light mode; all three topbar icons now render at the same muted colour

Accessibility

Improvements for screen readers, keyboard users, and users with visual preferences:

  • Focus management — focus traps and focus restoration added to search modal, keyboard shortcuts modal, and mobile sidebar; image zoom overlay now uses role="dialog" with focus trapped inside and restored on close
  • ARIA fixes — search results corrected from role="listbox"/role="option" to role="list" (appropriate for link-based results); removed incorrect role="tooltip" from hero image credit span
  • ARIA additions — aria-label added to search input (placeholder alone is insufficient for screen readers); aria-pressed added to dark mode toggle, grid/list view toggle, and accent swatch buttons; aria-expanded added to mobile sidebar toggle; aria-label added to TOC aside (role="navigation"); descriptive aria-label added to blog pagination links
  • Decorative SVGs — aria-hidden="true" added to decorative icons in post metadata (calendar, clock, tag) so screen readers skip them
  • Back-to-top button — removed from tab order when visually hidden (aria-hidden + tabindex toggled in JS)
  • Non-colour active state — TOC active heading link bumped to font-semibold so the active state is distinguishable without relying on colour alone
  • High-contrast mode — added @media (prefers-contrast: more) block with stronger borders and text for OS high-contrast mode

Code blocks

  • Language label in the header bar is now white in light mode (previously inherited body text colour)
  • Moved the copy-to-clipboard button from the header bar into the code listing itself, floating in the top-right corner below the header
  • Reduced header bar height with tighter padding
  • Fixed corner radii: header top corners are rounded, the join between header and code listing is flush (no gap or double radius), code listing bottom corners are rounded

Post summary

  • Changed from right-side-only rounded corners to fully rounded on all four corners, matching the callout style

Callouts

  • Note, tip, warning, and danger callouts changed from right-side-only rounded corners to fully rounded on all four corners

Author bio

  • Removed social links (GitHub, LinkedIn, Bluesky) from the author bio card at the bottom of posts — social links remain in the sidebar

Images in posts

  • Removed the link hover highlight colour on images wrapped in anchor tags
  • Added a magnifying glass icon overlay that appears on hover to indicate the image can be zoomed/enlarged
  • Split the single combined navigation list into two labelled sections: Navigate (internal site pages) and Connect (external social links)
  • Restructured the sidebar into three explicit zones:
    • Header (avatar, name, tagline, main nav) — fixed, shrinks at short viewport heights
    • Navigation (Navigate + Connect sections) — scrollable when content overflows
    • Footer (accent swatches) — fixed, always visible at full size regardless of viewport height
  • Accent colour swatches no longer resize at short viewport heights (≤780px) — they remain 20px and centred at the bottom of the sidebar
  • Sidebar tagline continues to hide at very short viewports (≤719px)
  • Swatches centred horizontally in the footer

2026-03-22

Accent colour swatches

  • Removed the indigo swatch and replaced it with a blue swatch as the first option
  • Updated all swatch colours to more vivid, saturated values matching standard Tailwind 500-level palette
  • Added a gray swatch as the eighth option
  • Updated accent-theme.js to reflect the new swatch set — blue is now the default fallback theme when no preference is stored

Topbar glass effect

  • Upgraded the top navigation bar from a near-opaque frosted background to a proper glassmorphism effect:
    • Background opacity reduced from 90% to 60% (dark) / 65% (light) so page content bleeds through the blur
    • Blur increased from backdrop-blur (~8px) to backdrop-blur-lg (16px)
    • Added backdrop-saturate-150 to boost the colour richness of blurred content behind the bar
    • Softened border opacity to 50–60%
    • Added a hairline inset top-edge highlight (box-shadow: inset 0 1px 0 rgba(255,255,255,0.06)) characteristic of glass surfaces

UI effects and micro-interactions

Added four new interaction effects, all disabled for users with prefers-reduced-motion: reduce:

  • Post cards — cards lift 3px (translateY(-3px)) and gain a shadow on hover; hero image zoom increased from 1.02 to 1.05
  • Search modal — the ⌘K modal now animates open (scale + fade in, 180ms) and closed (scale + fade out, 150ms) instead of appearing instantly
  • TOC active indicator — the active heading link in the table of contents shows a 2px accent-coloured left border that transitions in, with a subtle padding-left nudge to the link text
  • Icon micro-interactions — the dark mode toggle SVG rotates 15° on hover; sidebar social link icons lift 2px on hover

2026-03-21

Colour highlights

Added targeted colour highlights across the theme to improve visual clarity without introducing a second accent colour.

  • Social icon brand colours — sidebar and author bio social icons (GitHub, LinkedIn, Bluesky, Ko-fi, Spotify, RSS) now show their platform brand colour on hover
  • Share button brand colours — LinkedIn and Bluesky share buttons show platform brand colours on hover
  • Post metadata icons — calendar, clock, and tag icons in the post header now use the accent colour
  • Tag pill background tint — tag pills gain a subtle accent-tinted background using color-mix()
  • View toggle active state — the active grid/list view button on the blog page now uses the accent colour instead of flat dark grey
  • Callout type differentiation — three new callout variants alongside the existing .note class:
    • .warning — amber border and background
    • .tip — green border and background
    • .danger — red border and background

Hero image photo attribution

  • Added an attribution icon overlay on hero images linking to the original photographer
  • Removed the separate attribution page in favour of per-post attribution data in front matter

Image zoom

  • Replaced medium-zoom with a lightweight custom implementation — removes the vendor script dependency
  • Zoomed image is capped at 95 vw × 95 vh and centred in the viewport
  • Backdrop uses backdrop-blur-sm and bg-black/60 — matching the search modal blur effect
  • Fixed navigation hijack caused by images wrapped in <a> links (common in Markdown): click is intercepted and preventDefault() called so the browser no longer navigates to the raw image URL

Mermaid diagrams

  • Fixed arrow and edge colours in dark mode — lineColor overridden to slate-400 (#94a3b8) so connecting lines are visible against the dark background

Table of contents

  • Active heading link now renders in the current accent colour in light mode — previously the light-mode override rule had higher specificity and suppressed the accent colour

Sharing

  • Fixed LinkedIn share button — updated from the deprecated /shareArticle endpoint to /sharing/share-offsite/, which correctly pre-populates the LinkedIn post composer with the page URL and Open Graph metadata
  • Removed the separate attribution page link from the footer
  • Added a link to the site changelog

2026-03-20

Keyboard shortcuts

  • Added a keyboard shortcuts modal (press ? to open) listing all available keyboard shortcuts
  • Shortcuts include: ⌘K / Ctrl+K for search, D for dark mode toggle, G for grid/list view, T for back to top

Mermaid diagram support

  • Added client-side rendering of Mermaid diagrams in posts
  • Diagrams are defined in fenced code blocks with the mermaid language identifier
  • Theme-aware: diagram colours adapt to light and dark mode

UI refinements

  • Compact sidebar layout for short viewport heights (≤780px) such as 720p screens
  • Sidebar tagline hidden on very short viewports (≤719px)
  • Minor link hover and spacing improvements throughout

2026-03-19

New theme — complete rewrite

Replaced the Hydejack 9.x theme with a fully custom Jekyll layout built on Tailwind CSS v4 and @tailwindcss/typography.

Layout and structure

  • Sidebar layout — fixed sidebar with avatar, navigation links, social links, search, dark mode toggle, and accent colour swatches
  • New layouts — base, default, post, blog, list, page, about, plain
  • Post layout — hero image, metadata bar (date, reading time, category), description summary callout, article body, table of contents sidebar, related posts, and Utterances comments
  • Blog layout — paginated post grid with grid/list view toggle, persisted to localStorage
  • Post cards — image thumbnail, excerpt, category badge, and estimated reading time

Styling

  • Palette — slate/indigo base with slate-900 background and indigo-400 accent
  • Dark mode — class-based (.dark on <html>) with localStorage persistence; slate-950 sidebar contrasts against slate-900 content area
  • Light mode — white content area, gray-50 sidebar
  • Accent colour themes — 8 swappable accent colours (indigo, blue, purple, pink, red, orange, amber, green)
  • Syntax highlighting — full light and dark token colour overrides for Rouge (Monokai-inspired dark, high-contrast light)
  • Inline code — accent-coloured pill style with monospace font
  • Article links — block highlight on hover (accent background, white text)
  • Image spacing — ~8px above and below inline images via negative margin technique

JavaScript

Script Purpose
dark-mode.js Class-based dark mode toggle with localStorage persistence
search.js ⌘K / Ctrl+K modal with live fuzzy search via Lunr.js
toc.js Dynamic table of contents from article headings with IntersectionObserver active-link tracking
zoom.js Custom image zoom lightbox (originally medium-zoom; replaced 2026-03-21)
code-blocks.js Language badge injection and one-click copy button on code blocks
sidebar.js Mobile sidebar open/close with overlay

Build pipeline

  • npm run build — full build (fonts + icons + CSS)
  • npm run build:css — Tailwind CSS compilation only
  • npm run build:icons — copies SVG icons from npm packages (simple-icons, lucide)
  • Self-hosted Geist variable fonts (sans and mono)

Removed

  • Hydejack 9.x theme files (SASS partials, legacy JS bundles)
  • IcoMoon icon font
  • PWA web manifest and app icons
  • Category filter bar from blog page (simplified to post grid)