Working with an Astro-based portfolio frontend to refactor hardcoded Ayu Mirage theme colors into centralized CSS custom properties, addressing inconsistent styling across components including SVG/Canvas elements that required JavaScript runtime variable access.
Issue
Frontend components contained scattered hardcoded hex color values (#1f2430, #5ccfe6, #bae67e) across multiple files, causing:
- Inconsistent theming - Same logical colors had different values in different components
- Maintenance overhead - Changing the theme required manual updates in 7+ files
- JavaScript compatibility issues - Canvas/SVG contexts couldn’t directly use CSS variables without runtime workarounds
Culprit
Three root causes:
- Decentralized color definitions - Colors defined inline in
.astrocomponents and CSS files without single source of truth - Mixed rendering contexts - Both CSS-styled DOM elements and JavaScript-rendered Canvas/SVG required color values
- No semantic naming - Raw hex values (
#bae67e) provided no context about their purpose (e.g., “text color”)
Key problematic files:
src/layouts/BaseLayout.astro(hardcoded background)src/components/FrameworkNetworkCanvas.astro(hardcoded in both CSS and JS)src/styles/global.css(inconsistent paragraph color definitions)
Solution
CSS Variables Centralization
Created three semantic variables in global.css:
--ayu-mirage: #1f2430; /* Background */
--ayu-mirage-header: #5ccfe6; /* Headers */
--ayu-mirage-text: #bae67e; /* Text */
Component Refactoring
Updated 7 files to use variables:
- DOM Elements: Replaced hex values with
var(--ayu-mirage-text)in CSS - Astro Components: Updated template literals from
#1f2430tovar(--ayu-mirage) - JavaScript Contexts: Implemented runtime variable reading:
ctx.fillStyle = getComputedStyle(document.documentElement)
.getPropertyValue('--ayu-mirage')
.trim() || '#1f2430'; // Fallback
Special Handling for Canvas/SVG
For D3.js and native Canvas contexts that can’t use CSS variables directly:
- Added
getComputedStyle()wrapper function inFrameworkNetworkCanvas.astro - Applied same pattern to SVG text fills:
d3.select(this).style('fill',
getComputedStyle(document.documentElement)
.getPropertyValue('--ayu-mirage-text')
);
Benefits Achieved
- Single source of truth: All colors defined in one location
- Runtime consistency: JavaScript and CSS use identical values
- Future extensibility: Easy to add theme variants (light/dark) via variable overrides
- Self-documenting: Variable names (
--ayu-mirage-text) clarify purpose better than hex values