<color-contrast 
  foreground="AliceBlue, AntiqueWhite, Aqua, Aquamarine, Azure, Beige, Bisque, Black, BlanchedAlmond, Blue, BlueViolet, Brown, BurlyWood, CadetBlue, Chartreuse, Chocolate, Coral, CornflowerBlue, Cornsilk, Crimson, Cyan, DarkBlue, DarkCyan, DarkGoldenRod, DarkGray, DarkGreen, DarkKhaki, DarkMagenta, DarkOliveGreen, DarkOrange, DarkOrchid, DarkRed, DarkSalmon, DarkSeaGreen, DarkSlateBlue, DarkSlateGray, DarkTurquoise, DarkViolet, DeepPink, DeepSkyBlue, DimGray, DodgerBlue, FireBrick, FloralWhite, ForestGreen, Fuchsia, Gainsboro, GhostWhite, Gold, GoldenRod, Gray, Green, GreenYellow, HoneyDew, HotPink, IndianRed, Indigo, Ivory, Khaki, Lavender, LavenderBlush, LawnGreen, LemonChiffon, LightBlue, LightCoral, LightCyan, LightGoldenRodYellow, LightGray, LightGreen, LightPink, LightSalmon, LightSeaGreen, LightSkyBlue, LightSlateGray, LightSteelBlue, LightYellow, Lime, LimeGreen, Linen, Magenta, Maroon, MediumAquaMarine, MediumBlue, MediumOrchid, MediumPurple, MediumSeaGreen, MediumSlateBlue, MediumSpringGreen, MediumTurquoise, MediumVioletRed, MidnightBlue, MintCream, MistyRose, Moccasin, NavajoWhite, Navy, OldLace, Olive, OliveDrab, Orange, OrangeRed, Orchid, PaleGoldenRod, PaleGreen, PaleTurquoise, PaleVioletRed, PapayaWhip, PeachPuff, Peru, Pink, Plum, PowderBlue, Purple, RebeccaPurple, Red, RosyBrown, RoyalBlue, SaddleBrown, Salmon, SandyBrown, SeaGreen, SeaShell, Sienna, Silver, SkyBlue, SlateBlue, SlateGray, Snow, SpringGreen, SteelBlue, Tan, Teal, Thistle, Tomato, Turquoise, Violet, Wheat, White, WhiteSmoke, Yellow, YellowGreen" 
  background="AliceBlue, AntiqueWhite, Aqua, Aquamarine, Azure, Beige, Bisque, Black, BlanchedAlmond, Blue, BlueViolet, Brown, BurlyWood, CadetBlue, Chartreuse, Chocolate, Coral, CornflowerBlue, Cornsilk, Crimson, Cyan, DarkBlue, DarkCyan, DarkGoldenRod, DarkGray, DarkGreen, DarkKhaki, DarkMagenta, DarkOliveGreen, DarkOrange, DarkOrchid, DarkRed, DarkSalmon, DarkSeaGreen, DarkSlateBlue, DarkSlateGray, DarkTurquoise, DarkViolet, DeepPink, DeepSkyBlue, DimGray, DodgerBlue, FireBrick, FloralWhite, ForestGreen, Fuchsia, Gainsboro, GhostWhite, Gold, GoldenRod, Gray, Green, GreenYellow, HoneyDew, HotPink, IndianRed, Indigo, Ivory, Khaki, Lavender, LavenderBlush, LawnGreen, LemonChiffon, LightBlue, LightCoral, LightCyan, LightGoldenRodYellow, LightGray, LightGreen, LightPink, LightSalmon, LightSeaGreen, LightSkyBlue, LightSlateGray, LightSteelBlue, LightYellow, Lime, LimeGreen, Linen, Magenta, Maroon, MediumAquaMarine, MediumBlue, MediumOrchid, MediumPurple, MediumSeaGreen, MediumSlateBlue, MediumSpringGreen, MediumTurquoise, MediumVioletRed, MidnightBlue, MintCream, MistyRose, Moccasin, NavajoWhite, Navy, OldLace, Olive, OliveDrab, Orange, OrangeRed, Orchid, PaleGoldenRod, PaleGreen, PaleTurquoise, PaleVioletRed, PapayaWhip, PeachPuff, Peru, Pink, Plum, PowderBlue, Purple, RebeccaPurple, Red, RosyBrown, RoyalBlue, SaddleBrown, Salmon, SandyBrown, SeaGreen, SeaShell, Sienna, Silver, SkyBlue, SlateBlue, SlateGray, Snow, SpringGreen, SteelBlue, Tan, Teal, Thistle, Tomato, Turquoise, Violet, Wheat, White, WhiteSmoke, Yellow, YellowGreen" 
  algorithm="wcag21"
  min="4.5"
></color-contrast>
body {
  padding: 2rem;
}
import { attr, html, css, FASTElement, repeat, observable } from "https://unpkg.com/@microsoft/fast-element@2.0.0/dist/fast-element.min.js"
import Color from "https://colorjs.io/dist/color.js";

const styles = css` 
      table {
        font-family: system-ui;
        font-feature-setting: tnums;
        border: 1px solid #ccc;
      }
      thead th {
        border-bottom: 1px solid #ccc;
        background: canvas;
        position: sticky; 
        top: 0;
        z-index: 2;
      }
      th, td {
        text-align: start;
        padding: 0.5rem 1rem;
      }
      td {
        white-space: nowrap;
      }
      td.pass::before {
        content: '✅ '
      }
      td.fail::before {
        content: '❌ '
      }
      tbody th {
        border-right: 1px solid #ccc;
        background: canvas;
        position: sticky; 
        left: 0;
      }
      `

const trTemplate = (rootEl) => {
  return html`${el => { 
    let fg = new Color(el)
    
    return html`
              <tr style="color: ${el}">
                <th>${el}</th>
                ${repeat(rootEl.backgroundColors, bgColor => tdTemplate(rootEl, fg) )}
              </tr>
            `
  }}`
}

const tdTemplate = (rootEl, fgColor) => {
  return html`${bgColor => {
    let bg = new Color(bgColor)
    let contrastRatio = fgColor.contrast(bg, rootEl.algorithm).toFixed(2)
    let className = contrastRatio >= parseInt(rootEl.min, 10) ? 'pass': 'fail'

    return html`
      <td class="${className}" 
          style="background-color: ${bgColor}; opacity: ${rootEl.hideFails && className === 'fail'? 0 : 1 }"
      >
          ${contrastRatio}
      </td>`
  }}`
} 

const template = html`
    <table>
      <thead>
        <tr>
         <th>&nbsp;</th>
         ${repeat((el) => el.backgroundColors, html`<th>${x => x}</th>`)}
        </tr>
      </thead>
      <tbody>
          ${repeat((el) => el.foregroundColors, (rootEl) => trTemplate(rootEl))}
      </tbody>
    </table>
`
       
class ColorContrast extends FASTElement {
  
  @attr
  public foreground = ''
  
  @attr
  public background = ''
  
  @attr
  public algorithm = ''
  
  @attr
  public min = 4.5
  
  @attr({ mode: 'boolean', attribute: 'hide-fails' })
  public hideFails: false
  
  @observable
  foregroundColors = []

  @observable
  backgroundColors = []
    
  connectedCallback() {
    super.connectedCallback()
    this.foregroundColors = this.foreground?.split(",").map(c => c.trim())
    this.backgroundColors = this.background?.split(",").map(c => c.trim())
  }
}

/* Roll it up to get it on the page */
const definition = ColorContrast.compose({
  name: 'color-contrast',
  styles,
  template
})

definition.define(globalThis.customElements)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.