Resolved

Hardcoded CSS colors cause inconsistent theming across components

CSS
Frontend
Astro
Design System
Theming
Maintainability
JavaScript
SVG

Frontend components used scattered hardcoded hex colors (#1f2430, #5ccfe6) making theme management difficult | Required centralized CSS variables for maintainability

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:

  1. Inconsistent theming - Same logical colors had different values in different components
  2. Maintenance overhead - Changing the theme required manual updates in 7+ files
  3. JavaScript compatibility issues - Canvas/SVG contexts couldn’t directly use CSS variables without runtime workarounds

Culprit

Three root causes:

  1. Decentralized color definitions - Colors defined inline in .astro components and CSS files without single source of truth
  2. Mixed rendering contexts - Both CSS-styled DOM elements and JavaScript-rendered Canvas/SVG required color values
  3. 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:

  1. DOM Elements: Replaced hex values with var(--ayu-mirage-text) in CSS
  2. Astro Components: Updated template literals from #1f2430 to var(--ayu-mirage)
  3. 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:

  1. Added getComputedStyle() wrapper function in FrameworkNetworkCanvas.astro
  2. 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