 

 

 



 This list displays only color combinations that meet **WCAG AA requirements for normal text**.

 The minimum required contrast ratio is **4.5:1**. The background color is shown on the left, and the text or icon color is shown on the right.

 





 Note: the contrast of icons and important graphical elements should also be checked. For large text, the WCAG AA minimum contrast ratio is 3:1.

 

 
  /*
   * All styles are scoped to the tool to avoid affecting
   * the rest of the Drupal website.
   */

  .wcag-color-tool {
    --wcag-text-color: #191919;
    --wcag-border-color: #cccccc;
    --wcag-light-background: #f2f2f2;
    --wcag-success-background: #e8f5e9;
    --wcag-success-color: #176b2c;

    color: var(--wcag-text-color);
    font-family: inherit;
    line-height: 1.5;
  }

  .wcag-color-tool *,
  .wcag-color-tool *::before,
  .wcag-color-tool *::after {
    box-sizing: border-box;
  }

  .wcag-color-tool__information {
    padding: 16px 20px;
    margin: 0 0 24px;
    border-left: 5px solid #00613b;
    background: #f5f5f5;
  }

  .wcag-color-tool__information p {
    margin: 0 0 12px;
  }

  .wcag-color-tool__information p:last-child {
    margin-bottom: 0;
  }

  .wcag-color-tool__information strong {
    color: #00613b;
  }

  .wcag-color-tool__summary {
    display: inline-block;
    margin: 0 0 20px;
    padding: 10px 14px;
    border-radius: 5px;
    background: var(--wcag-success-background);
    color: var(--wcag-success-color);
    font-weight: bold;
  }

  .wcag-color-tool__table-container {
    overflow-x: auto;
    width: 100%;
    border: 1px solid var(--wcag-border-color);
    border-radius: 5px;
  }

  .wcag-color-tool table {
    width: 100%;
    min-width: 850px;
    border-collapse: collapse;
  }

  .wcag-color-tool th,
  .wcag-color-tool td {
    padding: 12px;
    border-bottom: 1px solid var(--wcag-border-color);
    text-align: left;
    vertical-align: middle;
  }

  .wcag-color-tool th {
    background: var(--wcag-light-background);
    font-size: 14px;
  }

  .wcag-color-tool tr:last-child td {
    border-bottom: 0;
  }

  .wcag-color-tool__color-code {
    font-family: Consolas, Monaco, monospace;
    font-weight: bold;
    white-space: nowrap;
  }

  .wcag-color-tool__preview {
    display: inline-block;
    min-width: 130px;
    padding: 10px 14px;
    border-radius: 4px;
    text-align: center;
    font-weight: bold;
    white-space: nowrap;
  }

  .wcag-color-tool__ratio {
    color: var(--wcag-success-color);
    font-family: Consolas, Monaco, monospace;
    font-weight: bold;
    white-space: nowrap;
  }

  .wcag-color-tool__compliance {
    color: var(--wcag-success-color);
    font-weight: bold;
    white-space: nowrap;
  }

  .wcag-color-tool__empty {
    padding: 20px;
    border: 1px solid #e0b400;
    background: #fff8d6;
  }

  .wcag-color-tool__note {
    margin-top: 24px;
    color: #666666;
    font-size: 14px;
  }

  @media (max-width: 700px) {
    .wcag-color-tool__information {
      padding: 14px 16px;
    }
  }
 
(function () {
  'use strict';

  /*
   * WCAG AA threshold for normal text.
   * Use 3 for large text only.
   */
  const CONTRAST_THRESHOLD = 4.5;

  const colors = [
    "#EE0000",
    "#ED694B",
    "#FF6900",
    "#FF8A00",
    "#FFAC00",
    "#FDC686",
    "#FFDCB3",
    "#E8BF00",
    "#FFD616",
    "#F5D955",
    "#FFED99",
    "#FFF5C5",

    "#00613B",
    "#438D42",
    "#78BE21",
    "#C7D64F",
    "#D9EDC0",

    "#098192",
    "#05C3DD",
    "#76DAE8",
    "#99E1EF",
    "#D7FAFF",

    "#002D62",
    "#0679CC",
    "#ADCCF0",
    "#CCE4FF",
    "#EBF4FF",

    "#501658",
    "#772583",
    "#9E4AAB",
    "#C768CF",
    "#FFD0F2",

    "#000000",
    "#191919",
    "#333333",
    "#4C4C4C",
    "#666666",
    "#808080",
    "#999999",
    "#B2B2B2",
    "#CCCCCC",
    "#E5E5E5",
    "#F2F2F2",
    "#FFFFFF",

    "#55555A",
    "#1C1C1F",
    "#75748B",
    "#9897A8",
    "#C8C8DA",
    "#D5D3E3",
    "#E0DDEE",
    "#E9E6F4",
    "#F2F1F9",
    "#FCFCFC"
  ];

  const uniqueColors = [
    ...new Set(colors.map(function (color) {
      return color.toUpperCase();
    }))
  ];

  function hexToRgb(hex) {
    return {
      r: parseInt(hex.substring(1, 3), 16),
      g: parseInt(hex.substring(3, 5), 16),
      b: parseInt(hex.substring(5, 7), 16)
    };
  }

  function relativeLuminance(hex) {
    const rgb = hexToRgb(hex);

    const channels = [rgb.r, rgb.g, rgb.b].map(function (channel) {
      const value = channel / 255;

      return value <= 0.04045
        ? value / 12.92
        : Math.pow((value + 0.055) / 1.055, 2.4);
    });

    return (
      0.2126 * channels[0] +
      0.7152 * channels[1] +
      0.0722 * channels[2]
    );
  }

  function contrastRatio(color1, color2) {
    const luminance1 = relativeLuminance(color1);
    const luminance2 = relativeLuminance(color2);

    const lighter = Math.max(luminance1, luminance2);
    const darker = Math.min(luminance1, luminance2);

    return (lighter + 0.05) / (darker + 0.05);
  }

  function initializeTool(tool) {
    if (tool.dataset.wcagInitialized === 'true') {
      return;
    }

    tool.dataset.wcagInitialized = 'true';

    const summary = tool.querySelector('[data-wcag-summary]');
    const results = tool.querySelector('[data-wcag-results]');

    const acceptedPairs = [];

    uniqueColors.forEach(function (background) {
      uniqueColors.forEach(function (foreground) {
        const ratio = contrastRatio(background, foreground);

        if (ratio >= CONTRAST_THRESHOLD) {
          acceptedPairs.push({
            background: background,
            foreground: foreground,
            ratio: ratio
          });
        }
      });
    });

    acceptedPairs.sort(function (a, b) {
      if (a.background < b.background) {
        return -1;
      }

      if (a.background > b.background) {
        return 1;
      }

      return b.ratio - a.ratio;
    });

    summary.textContent =
      acceptedPairs.length +
      ' compliant combination(s) for WCAG AA normal text — minimum ratio: ' +
      CONTRAST_THRESHOLD +
      ':1';

    if (acceptedPairs.length === 0) {
      results.innerHTML =
        '<div class="wcag-color-tool__empty">' +
        'No combination meets the WCAG AA minimum contrast ratio of ' +
        CONTRAST_THRESHOLD +
        ':1 for normal text.' +
        '

'; return; } let html = '' + '| Background | Text / Icon | Preview | Contrast Ratio | Compliance |
|---|---|---|---|---|
| ' + '' + pair.background + '' + ' | ' + '' + pair.foreground + '' + ' | ' + '' + 'Sample text' + '' + ' | ' + '' + pair.ratio.toFixed(2) + ':1' + '' + ' | ' + '' + '✓ WCAG AA' + '' + ' |

' + '

'; results.innerHTML = html; } function initializeAllTools(context) { const scope = context || document; scope.querySelectorAll('[data-wcag-color-tool]').forEach(function (tool) { initializeTool(tool); }); } /* * Compatibility with Drupal behaviors. * This ensures the tool also works after an AJAX refresh. */ if (typeof Drupal !== 'undefined' &amp;&amp; Drupal.behaviors) { Drupal.behaviors.wcagColorTool = { attach: function (context) { initializeAllTools(context); } }; } else { document.addEventListener('DOMContentLoaded', function () { initializeAllTools(document); }); } })(); 

 

 

 





 

 





  

## WCAG AA Colors

 Graphic Charter 2024 

 

 ### Usage Context (Backgrounds)

- all\_inclusive All colors
- light\_mode Light Backgrounds
- dark\_mode Dark Backgrounds
 
 

 ### Background Selector

  

 

      search  

 WCAG AA (≥ 4.5:1) grid\_view reorder 

 brightness\_4 

  ## All combinations

Select a background color from the menu to start.

 

0 combination(s) found

 

  

  | Text Color | Preview | Ratio | Compliance |
|---|---|---|---|

 

 

  

    
  :root {
    /* Main Brand Variables */
    --v-brand-red: #EE0000;
    --v-brand-navy: #002D62;
    --v-brand-green: #00613B;
    
    /* Light Theme Variables */
    --bg-app: #F4F5F8;
    --bg-panel: #FFFFFF;
    --bg-hover: #F2F2F2;
    --border-color: #E0E4EC;
    --text-main: #191919;
    --text-muted: #666666;
    --shadow-sm: 0 2px 4px rgba(0,0,0,0.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.08);
  }

  .v-wcag-dashboard[data-theme="dark"] {
    --bg-app: #121418;
    --bg-panel: #1C1F26;
    --bg-hover: #2D323E;
    --border-color: #2D323E;
    --text-main: #F2F2F2;
    --text-muted: #9897A8;
  }

  .v-wcag-dashboard {
    display: flex;
    height: 90vh;
    min-height: 600px;
    font-family: 'Noto Sans', sans-serif;
    background: var(--bg-app);
    color: var(--text-main);
    border-radius: 12px;
    overflow: hidden;
    border: 1px solid var(--border-color);
  }

  /* --- SIDEBAR --- */
  .v-sidebar {
    width: 280px;
    background: var(--bg-panel);
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
  }

  .v-brand-header {
    padding: 20px;
    display: flex;
    gap: 12px;
    align-items: center;
    border-bottom: 1px solid var(--border-color);
  }

  .v-accent-line {
    width: 4px;
    height: 32px;
    background: var(--v-brand-red);
    border-radius: 4px;
  }

  .v-logo-title { margin: 0; font-size: 1rem; font-weight: 700; }
  .v-version { font-size: 0.75rem; color: var(--text-muted); }

  .v-sidebar-scroll {
    overflow-y: auto;
    flex: 1;
    padding: 16px 0;
  }

  .v-nav-section { margin-bottom: 24px; padding: 0 16px; }
  .v-nav-title { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--text-muted); margin-bottom: 12px; font-weight: 700; }
  .v-nav-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 4px; }
  
  .v-nav-btn {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border: none;
    background: transparent;
    color: var(--text-main);
    font-size: 0.85rem;
    font-weight: 500;
    border-radius: 6px;
    cursor: pointer;
    text-align: left;
    transition: background 0.2s;
  }

  .v-nav-btn:hover { background: var(--bg-hover); }
  .v-nav-btn.active { background: var(--v-brand-navy); color: #FFF; }
  .v-nav-btn.active .material-icons-outlined { color: #FFF; }

  /* Accordion styles for Color Families */
  .v-family-group { margin-bottom: 8px; }
  .v-family-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 8px 12px;
    background: var(--bg-hover);
    border: none;
    border-radius: 6px;
    color: var(--text-main);
    font-weight: 600;
    font-size: 0.8rem;
    cursor: pointer;
  }
  .v-family-list {
    list-style: none; padding: 4px 0 0 12px; margin: 0; display: none;
  }
  .v-family-list.open { display: block; }
  
  .v-color-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.8rem;
    border: 1px solid transparent;
  }
  .v-color-item:hover { background: var(--bg-hover); }
  .v-color-item.selected { border-color: var(--v-brand-navy); background: rgba(0, 45, 98, 0.05); font-weight: 600; }
  .v-swatch-mini { width: 16px; height: 16px; border-radius: 50%; border: 1px solid rgba(128,128,128,0.3); }

  /* --- MAIN CONTENT --- */
  .v-main-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .v-toolbar {
    padding: 16px 24px;
    background: var(--bg-panel);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
  }

  .v-search-wrapper {
    position: relative;
    flex: 1;
    max-width: 400px;
  }
  .v-search-wrapper input {
    width: 100%;
    padding: 8px 16px 8px 36px;
    border-radius: 20px;
    border: 1px solid var(--border-color);
    background: var(--bg-app);
    color: var(--text-main);
    outline: none;
  }
  .v-search-wrapper .material-icons-outlined {
    position: absolute;
    left: 10px; top: 50%; transform: translateY(-50%);
    color: var(--text-muted); font-size: 18px;
  }

  .v-toolbar-actions { display: flex; align-items: center; gap: 16px; }

  .v-aa-badge {
    background: rgba(120, 190, 33, 0.15);
    color: #00613B;
    border: 1px solid #78BE21;
    padding: 4px 12px;
    border-radius: 18px;
    font-size: 0.8rem;
    font-weight: 700;
  }

  .v-view-switcher { display: flex; gap: 4px; }
  .v-icon-btn { padding: 6px; border: 1px solid transparent; background: transparent; border-radius: 6px; cursor: pointer; color: var(--text-muted); display:flex; align-items:center; }
  .v-icon-btn:hover { background: var(--bg-hover); }
  .v-icon-btn.active { background: var(--bg-hover); color: var(--text-main); border-color: var(--border-color); }

  .v-results-header { padding: 24px; display: flex; justify-content: space-between; align-items: flex-end; }
  .v-results-header h2 { margin: 0 0 4px 0; font-size: 1.5rem; }
  .v-text-muted { margin: 0; color: var(--text-muted); font-size: 0.85rem; }
  .v-stats-badge { background: var(--bg-panel); border: 1px solid var(--border-color); color: var(--text-main); padding: 6px 12px; border-radius: 20px; font-weight: 600; font-size: 0.85rem; }

  .v-views-container { flex: 1; overflow-y: auto; padding: 0 24px 24px 24px; }

  /* GRID */
  .v-grid-layout { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
  .v-card { background: var(--bg-panel); border-radius: 10px; border: 1px solid var(--border-color); overflow: hidden; display: flex; flex-direction: column; transition: transform 0.2s ease, box-shadow 0.2s ease; }
  .v-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--border-color); }
  .v-card-preview { padding: 20px; height: 100px; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; font-weight: 700; text-align: center; position: relative; }
  .v-card-badge { position: absolute; top: 10px; right: 10px; padding: 3px 6px; border-radius: 4px; font-size: 0.65rem; font-weight: 700; background: rgba(0,0,0,0.3); color: #FFF; text-transform: uppercase; }
  .v-card-body { padding: 16px; border-top: 1px solid var(--border-color); }
  .v-card-meta { display: flex; justify-content: space-between; align-items: center; }
  .v-ratio { font-weight: 800; font-size: 1.1rem; font-family: monospace; color: var(--text-main); }

  /* TABLE */
  .v-table-layout { background: var(--bg-panel); border-radius: 10px; border: 1px solid var(--border-color); overflow: hidden; }
  .v-table-layout table { width: 100%; border-collapse: collapse; text-align: left; font-size: 0.85rem; }
  .v-table-layout th { background: var(--bg-app); padding: 12px 16px; color: var(--text-muted); font-weight: 600; border-bottom: 1px solid var(--border-color); }
  .v-table-layout td { padding: 12px 16px; border-bottom: 1px solid var(--border-color); vertical-align: middle; }
  .v-table-preview-pill { padding: 4px 12px; border-radius: 4px; font-weight: 600; display: inline-block; }
 
(function() {
  const CONTRAST_THRESHOLD_AA = 4.5;

  // Dictionary aligned with Graphic Charter names
  const colorFamilies = {
    reds: [
      { hex: "#EE0000", name: "Red" },
      { hex: "#ED694B", name: "Pale Red" }
    ],
    oranges: [
      { hex: "#FF6900", name: "Orange" },
      { hex: "#FF8A00", name: "Vivid Orange" },
      { hex: "#FFAC00", name: "Light Orange" },
      { hex: "#FDC686", name: "Apricot" },
      { hex: "#FFDCB3", name: "Very Light Orange" }
    ],
    yellows: [
      { hex: "#E8BF00", name: "Dark Yellow" },
      { hex: "#FFD616", name: "Yellow" },
      { hex: "#F5D955", name: "Golden Yellow" },
      { hex: "#FFED99", name: "Pale Yellow" },
      { hex: "#FFF5C5", name: "Very Light Yellow" }
    ],
    greens: [
      { hex: "#00613B", name: "Dark Green" },
      { hex: "#438D42", name: "Forest Green" },
      { hex: "#78BE21", name: "Apple Green" },
      { hex: "#C7D64F", name: "Light Green" },
      { hex: "#D9EDC0", name: "Very Light Green" }
    ],
    turquoises: [
      { hex: "#098192", name: "Dark Turquoise" },
      { hex: "#05C3DD", name: "Turquoise" },
      { hex: "#76DAE8", name: "Light Turquoise" },
      { hex: "#99E1EF", name: "Pale Turquoise" },
      { hex: "#D7FAFF", name: "Very Light Turquoise" }
    ],
    blues: [
      { hex: "#002D62", name: "Navy" },
      { hex: "#0679CC", name: "Vivid Blue" },
      { hex: "#ADCCF0", name: "Light Blue" },
      { hex: "#CCE4FF", name: "Pale Blue" },
      { hex: "#EBF4FF", name: "Very Light Blue" }
    ],
    purples: [
      { hex: "#501658", name: "Dark Purple" },
      { hex: "#772583", name: "Purple" },
      { hex: "#9E4AAB", name: "Light Purple" },
      { hex: "#C768CF", name: "Pale Purple" },
      { hex: "#FFD0F2", name: "Very Light Purple" }
    ],
    greyscales: [
      { hex: "#000000", name: "Black" },
      { hex: "#191919", name: "Black 90" },
      { hex: "#333333", name: "Black 80" },
      { hex: "#4C4C4C", name: "Black 70" },
      { hex: "#666666", name: "Black 60" },
      { hex: "#808080", name: "Black 50" },
      { hex: "#999999", name: "Black 40" },
      { hex: "#B2B2B2", name: "Black 30" },
      { hex: "#CCCCCC", name: "Black 20" },
      { hex: "#E5E5E5", name: "Black 10" },
      { hex: "#F2F2F2", name: "Black 0" },
      { hex: "#FFFFFF", name: "White" }
    ],
    neutrals: [
      { hex: "#1C1C1F", name: "Neutral 90" },
      { hex: "#55555A", name: "Grey (Pantone)" },
      { hex: "#75748B", name: "Neutral 80" },
      { hex: "#9897A8", name: "Neutral 70" },
      { hex: "#C8C8DA", name: "Neutral 60" },
      { hex: "#D5D3E3", name: "Neutral 50" },
      { hex: "#E0DDEE", name: "Neutral 40" },
      { hex: "#E9E6F4", name: "Neutral 30" },
      { hex: "#F2F1F9", name: "Neutral 20" },
      { hex: "#FCFCFC", name: "Neutral" }
    ]
  };

  const allColorsObj = {};
  let allColorsHex = [];
  
  // Flatten families into a dictionary for quick lookup
  Object.values(colorFamilies).forEach(family => {
    family.forEach(color => {
      if(!allColorsObj[color.hex.toUpperCase()]) {
          allColorsObj[color.hex.toUpperCase()] = color.name;
          allColorsHex.push(color.hex.toUpperCase());
      }
    });
  });

  // State
  let state = {
    activeBg: null, 
    searchQuery: '',
    contextFilter: 'all' // all, light, dark
  };

  // WCAG Calculation Utilities
  function hexToRgb(hex) {
    return { r: parseInt(hex.substring(1, 3), 16), g: parseInt(hex.substring(3, 5), 16), b: parseInt(hex.substring(5, 7), 16) };
  }
  function relativeLuminance(hex) {
    const rgb = hexToRgb(hex);
    const channels = [rgb.r, rgb.g, rgb.b].map(val => {
      val /= 255;
      return val <= 0.04045 ? val / 12.92 : Math.pow((val + 0.055) / 1.055, 2.4);
    });
    return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
  }
  function contrastRatio(bg, fg) {
    const l1 = relativeLuminance(bg);
    const l2 = relativeLuminance(fg);
    return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
  }
  function isLight(hex) {
    return relativeLuminance(hex) > 0.5;
  }

  function initializeTool(tool) {
    if (tool.dataset.wcagInitialized === 'true') return;
    tool.dataset.wcagInitialized = 'true';

    // Elements
    const gridEl = tool.querySelector('#viewGrid');
    const tableBodyEl = tool.querySelector('#tableBody');
    const searchInput = tool.querySelector('#globalSearch');
    const themeToggleBtn = tool.querySelector('#themeToggle');
    const resultsTitle = tool.querySelector('#resultsTitle');
    const resultsMeta = tool.querySelector('#resultsMeta');
    const resultsCount = tool.querySelector('#resultsCount');
    const containerAccordion = tool.querySelector('#colorFamiliesAccordion');

    function buildSidebar() {
      const categories = [
        { id: 'reds', label: 'Reds' },
        { id: 'oranges', label: 'Oranges' },
        { id: 'yellows', label: 'Yellows' },
        { id: 'greens', label: 'Greens' },
        { id: 'turquoises', label: 'Turquoises' },
        { id: 'blues', label: 'Blues' },
        { id: 'purples', label: 'Purples' },
        { id: 'greyscales', label: 'Greyscales' },
        { id: 'neutrals', label: 'Neutrals & Backgrounds' }
      ];

      categories.forEach(cat => {
        const group = document.createElement('div');
        group.className = 'v-family-group';
        
        const header = document.createElement('button');
        header.className = 'v-family-header';
        header.innerHTML = `<span>${cat.label} <span class="material-icons-outlined" style="font-size:16px">expand_more`;
        
        const list = document.createElement('ul');
        list.className = 'v-family-list';
        
        colorFamilies[cat.id].forEach(color => {
          const li = document.createElement('li');
          li.className = 'v-color-item';
          li.dataset.hex = color.hex.toUpperCase();
          li.innerHTML = `<div class="v-swatch-mini" style="background:${color.hex}">

 ${color.name}`; li.addEventListener('click', () =&gt; { tool.querySelectorAll('.v-color-item').forEach(el =&gt; el.classList.remove('selected')); li.classList.add('selected'); tool.querySelectorAll('#contextFilters .v-nav-btn').forEach(b =&gt; b.classList.remove('active')); state.activeBg = color.hex.toUpperCase(); renderResults(); }); list.appendChild(li); }); header.addEventListener('click', () =&gt; { list.classList.toggle('open'); header.querySelector('.material-icons-outlined').textContent = list.classList.contains('open') ? 'expand_less' : 'expand_more'; }); // Default Open for Blue &amp; Greyscales if(cat.id === 'blues' || cat.id === 'greyscales') list.classList.add('open'); group.appendChild(header); group.appendChild(list); containerAccordion.appendChild(group); }); } function attachListeners() { // Context Filters (All, Light backgrounds, Dark backgrounds) tool.querySelectorAll('#contextFilters .v-nav-btn').forEach(btn =&gt; { btn.addEventListener('click', (e) =&gt; { tool.querySelectorAll('#contextFilters .v-nav-btn').forEach(b =&gt; b.classList.remove('active')); btn.classList.add('active'); tool.querySelectorAll('.v-color-item').forEach(el =&gt; el.classList.remove('selected')); state.activeBg = null; state.contextFilter = btn.dataset.filter; renderResults(); }); }); // Search Input searchInput.addEventListener('input', (e) =&gt; { state.searchQuery = e.target.value.toLowerCase().trim(); renderResults(); }); // View Switcher tool.querySelectorAll('.v-icon-btn[data-view]').forEach(btn =&gt; { btn.addEventListener('click', () =&gt; { tool.querySelectorAll('.v-icon-btn[data-view]').forEach(b =&gt; b.classList.remove('active')); btn.classList.add('active'); const view = btn.dataset.view; tool.querySelector('#viewGrid').style.display = view === 'grid' ? 'grid' : 'none'; tool.querySelector('#viewTable').style.display = view === 'table' ? 'block' : 'none'; }); }); // Theme Toggle if(themeToggleBtn) { themeToggleBtn.addEventListener('click', () =&gt; { if (tool.getAttribute('data-theme') === 'dark') { tool.removeAttribute('data-theme'); } else { tool.setAttribute('data-theme', 'dark'); } }); } } function renderResults() { let validPairs = []; // Determine target backgrounds let bgsToProcess = allColorsHex; if (state.activeBg) { bgsToProcess = [state.activeBg]; resultsTitle.textContent = `Background: ${allColorsObj[state.activeBg]} (${state.activeBg})`; resultsMeta.textContent = `Text colors compatible with this background.`; } else { if (state.contextFilter === 'light') { bgsToProcess = allColorsHex.filter(hex =&gt; isLight(hex)); resultsTitle.textContent = `Light Backgrounds`; } else if (state.contextFilter === 'dark') { bgsToProcess = allColorsHex.filter(hex =&gt; !isLight(hex)); resultsTitle.textContent = `Dark Backgrounds`; } else { resultsTitle.textContent = `All combinations`; } resultsMeta.textContent = `Explore text colors depending on background.`; } // Process combinations bgsToProcess.forEach(bg =&gt; { allColorsHex.forEach(fg =&gt; { const ratio = contrastRatio(bg, fg); if (ratio &gt;= CONTRAST_THRESHOLD_AA) { const fgName = allColorsObj[fg]; const bgName = allColorsObj[bg]; if (state.searchQuery === '' || fg.toLowerCase().includes(state.searchQuery) || fgName.toLowerCase().includes(state.searchQuery) || bg.toLowerCase().includes(state.searchQuery) || bgName.toLowerCase().includes(state.searchQuery)) { validPairs.push({ bg, fg, ratio, fgName, bgName }); } } }); }); // Sort Descending validPairs.sort((a, b) =&gt; { if (a.bg === b.bg) return b.ratio - a.ratio; return b.ratio - a.ratio; }); // Update Counter resultsCount.textContent = `${validPairs.length} combination(s) found`; // Render Grid &amp; Table gridEl.innerHTML = ''; tableBodyEl.innerHTML = ''; if (validPairs.length === 0) { gridEl.innerHTML = `No color meets the criteria.

`; tableBodyEl.innerHTML = `| No results |
|---|
`; return; } validPairs.forEach(pair =&gt; { const isAAA = pair.ratio &gt;= 7.0; // GRID ITEM const card = document.createElement('div'); card.className = 'v-card'; card.innerHTML = `  ${isAAA ? 'WCAG AAA' : 'WCAG AA'}Example Text

Normal readability

 

 

 ${state.activeBg ? 'Text color' : 'Bg: ' + pair.bgName + ' | Text'} 

${pair.fgName}

${pair.fg}

 

${pair.ratio.toFixed(2)}

 

 

 

 `; gridEl.appendChild(card); // TABLE ROW const tr = document.createElement('tr'); tr.innerHTML = ` | ${pair.fgName}  ${pair.fg}  | Visual Preview  | ${pair.ratio.toFixed(2)}:1  | check\_circle Compliant  `; tableBodyEl.appendChild(tr); }); } buildSidebar(); attachListeners(); renderResults(); } function init(context) { const scope = context || document; scope.querySelectorAll('[data-v-wcag-tool]').forEach(tool =&gt; initializeTool(tool)); } if (typeof Drupal !== 'undefined' &amp;&amp; Drupal.behaviors) { Drupal.behaviors.vWcagTool = { attach: function (context) { init(context); } }; } else { document.addEventListener('DOMContentLoaded', () =&gt; init(document)); } })();