eph baum dot dev

← Back to blog

Making Brutalist Design Accessible: A Journey in WCAG AA Compliance

Published on 10/26/2025 7:40 PM by Eph Baum feat. Claude

Making Brutalist Design Accessible: A Journey in WCAG AA Compliance

When I adopted the brutalist theme I was drawn to its bold, unapologetic aesthetic — bright colors, sharp edges, and at least a little bit of nostaligia for early web design. The issue, of course, as I worked to make it my own, is that I worried first about making it look the way that I want first before worring about accessibility.

PageSpeed Accessibility Score

This is particularly problematic for me because I really do care quite a bit about a11y, so the realization that I was actually close to meeting folks’ needs was simulataneously disappointing and pleasing.

Thankfully PageSpeed Insights let me know where I was letting folks down (more than just having mediocre, poorly written, content).

The Problem: Beautiful but Inaccessible

My brutalist theme features a dynamic color system that randomly assigns bright colors to backgrounds, cards, and text elements. While visually striking, this approach had a flaw: it didn’t ensure sufficient contrast between text and backgrounds.

The Web Content Accessibility Guidelines (WCAG) specify that text must have a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (18pt+ or 14pt+ bold) to meet AA standards. My random color assignments often failed this test, making content difficult or impossible to read for users with visual impairments.

The Challenge: Maintaining Aesthetic While Ensuring Accessibility

The core challenge was maintaining the brutalist aesthetic — random, vibrant colors — while ensuring every color combination met accessibility standards. I didn’t want to abandon the dynamic color system entirely, but I needed to make it intelligent.

The Solution: Smart Color Selection

I implemented a comprehensive accessibility system with three key components:

1. Contrast Calculation Engine

First, I built a utility that calculates contrast ratios using the WCAG relative luminance formula:

function getContrastRatio(color1, color2) {
  const lum1 = getRelativeLuminance(hexToRgb(color1));
  const lum2 = getRelativeLuminance(hexToRgb(color2));
  const lighter = Math.max(lum1, lum2);
  const darker = Math.min(lum1, lum2);
  return (lighter + 0.05) / (darker + 0.05);
}

This engine can determine if any color pair meets WCAG AA standards and find accessible alternatives from my existing palette.

2. Intelligent Color Filtering

Instead of completely random color selection, I modified the system to filter out inaccessible combinations:

The system now randomly selects from only the accessible colors, maintaining the random aesthetic while ensuring readability.

3. High Contrast Mode Support

For users who need even higher contrast, I added automatic detection of the prefers-contrast: high media query:

@media (prefers-contrast: high) {
  * {
    color: #000000 !important;
    background-color: #ffffff !important;
  }
  
  [class*='i-uil-'], [class*='i-'], svg, .icon {
    color: #000000 !important;
    background-color: #000000 !important;
  }
}

This provides a clean black-on-white experience when users have high contrast enabled in their system settings.

Technical Implementation Details

Color Pair Validation

For colored text scenarios (like author badges), I created a system that finds valid color pairs:

function findAccessibleTextPairs(colors, colorMap) {
  const accessiblePairs = [];
  
  colors.forEach(textColorName => {
    colors.forEach(bgColorName => {
      if (textColorName !== bgColorName && 
          meetsWCAGAA(textColorHex, bgColorHex, false)) {
        accessiblePairs.push({
          foreground: textColorHex,
          background: bgColorHex,
          ratio: getContrastRatio(textColorHex, bgColorHex)
        });
      }
    });
  });
  
  return accessiblePairs.sort((a, b) => b.ratio - a.ratio);
}

Icon Accessibility

UnoCSS icons posed a unique challenge—they use CSS masks with background-color: currentColor. In high contrast mode, I had to ensure both the color and background-color properties were set correctly:

[class*='i-uil-'], [class*='i-'], svg, .icon {
  color: #000000 !important;
  background-color: #000000 !important; /* Critical for mask visibility */
}

Another area I’d initially overlooked was the footer links. The original design used blue links (text-blue) against a black background, which also failed contrast requirements. I integrated the footer into the color system by:

function applyFooterColors() {
  const footer = document.querySelector('[data-brutal-footer]');
  const footerLinks = footer.querySelectorAll('a');
  
  footerLinks.forEach((link) => {
    const linkColor = getRandomAccessibleColorExcluding();
    link.classList.add(`text-${linkColor}`);
    link.style.color = colorMap[linkColor];
  });
}

This ensures footer links (GitHub, Buy Me a Kombucha, RSS feed) use accessible colors from the palette while maintaining the brutalist aesthetic.

Results: Accessibility Without Compromise

The implementation successfully achieves:

Lessons Learned

1. Accessibility Can Enhance Design

Rather than constraining creativity, accessibility requirements forced me to think more carefully about color relationships. The result is a more sophisticated color system that’s both beautiful and functional.

2. Progressive Enhancement Works

The system gracefully degrades—if contrast utilities fail to load, it falls back to safe defaults. This ensures accessibility even when JavaScript fails.

3. User Preferences Matter

The prefers-contrast: high media query is a powerful tool that respects user preferences without requiring manual configuration.

The Broader Impact

This work demonstrates that accessibility and aesthetic design aren’t mutually exclusive. By implementing smart color selection algorithms, we can create visually striking designs that work for everyone.

The brutalist aesthetic—with its bold colors and unapologetic style—can coexist with inclusive design principles. The key is making the randomness intelligent rather than purely chaotic.

Next Steps

While this implementation achieves WCAG AA compliance, there’s always room for improvement:

Conclusion

Making my brutalist blog accessible wasn’t about compromising the design — it was about making it smarter. By implementing intelligent color selection algorithms and respecting user preferences, I created a site that maintains its bold aesthetic while ensuring everyone can read the content.

The result is a more inclusive web that doesn’t sacrifice creativity for accessibility. Sometimes, constraints lead to better solutions.


Have you implemented accessibility improvements in your designs? What challenges did you face? I’d love to hear about your experiences with inclusive design.

Written by Eph Baum feat. Claude

  • Making Brutalist Design Accessible: A Journey in WCAG AA Compliance

    Making Brutalist Design Accessible: A Journey in WCAG AA Compliance

    How I transformed my brutalist blog theme to meet WCAG AA accessibility standards while preserving its vibrant, random aesthetic. Talking about contrast ratios, color theory, and inclusive design.

  • Building Horror Movie Season: A Journey in AI-Augmented Development

    Building Horror Movie Season: A Journey in AI-Augmented Development

    How I built a production web app primarily through 'vibe coding' with Claude, and what it taught me about the future of software development. A deep dive into AI-augmented development, the Horror Movie Season app, and reflections on the evolving role of engineers in the age of LLMs.

  • Chaos Engineering: Building Resiliency in Ourselves and Our Systems

    Chaos Engineering: Building Resiliency in Ourselves and Our Systems

    Chaos Engineering isn't just about breaking systems — it's about building resilient teams, processes, and cultures. Learn how deliberate practice strengthens both technical and human architecture, and discover "Eph's Law": If a single engineer can bring down production, the failure isn't theirs — it's the process.

  • Using LLMs to Audit and Clean Up Your Codebase: A Real-World Example

    Using LLMs to Audit and Clean Up Your Codebase: A Real-World Example

    How I used an LLM to systematically audit and remove 228 unused image files from my legacy dev blog repository, saving hours of manual work and demonstrating the practical value of AI-assisted development.

  • Migrating from Ghost CMS to Astro: A Complete Journey

    Migrating from Ghost CMS to Astro: A Complete Journey

    The complete 2-year journey of migrating from Ghost CMS to Astro—from initial script development in October 2023 to final completion in October 2025. Documents the blog's 11-year evolution, custom backup conversion script, image restoration process, and the intensive 4-day development sprint. Includes honest insights about how a few days of actual work got spread across two years due to life priorities.

  • 50 Stars - Puzzle Solver (of Little Renown)

    50 Stars - Puzzle Solver (of Little Renown)

    From coding puzzle dropout to 50-star champion—discover how AI became the ultimate coding partner for completing Advent of Code 2023. A celebration of persistence, imposter syndrome, and the surprising ways generative AI can help you level up your problem-solving game.