diff --git a/packages/uui-color-swatch/lib/uui-color-swatch.element.ts b/packages/uui-color-swatch/lib/uui-color-swatch.element.ts index f14668652..b0e81c1c6 100644 --- a/packages/uui-color-swatch/lib/uui-color-swatch.element.ts +++ b/packages/uui-color-swatch/lib/uui-color-swatch.element.ts @@ -1,7 +1,8 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; -import { property } from 'lit/decorators.js'; +import { property, state } from 'lit/decorators.js'; import { css, html, LitElement, nothing } from 'lit'; import { ref } from 'lit/directives/ref.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; import { iconCheck } from '@umbraco-ui/uui-icon-registry-essential/lib/svgs'; import { ActiveMixin, @@ -23,6 +24,9 @@ export class UUIColorSwatchElement extends LabelMixin( 'label', SelectableMixin(ActiveMixin(LitElement)), ) { + @state() + private _contrast: 'dark' | 'light' | undefined = undefined; + /** * Value of the swatch. This will become the color value if color is left undefined, see the property `color` for more details. */ @@ -90,6 +94,21 @@ export class UUIColorSwatchElement extends LabelMixin( firstUpdated() { this._setAriaAttributes(); + + const color = this.color ?? this.value; + if (color.startsWith('#')) { + this._contrast = this.#contrast(color) === 'light' ? 'light' : 'dark'; + } else if (color.startsWith('rgb')) { + const [r, g, b, a] = color.match(/[.\d]+/g)?.map(Number) ?? [0, 0, 0]; + if (a <= 0.5) { + this._contrast = 'light'; + } else { + this._contrast = + this.#contrast(this.#rgbToHex(r, g, b)) === 'light' + ? 'light' + : 'dark'; + } + } } willUpdate(changedProperties: Map) { @@ -118,6 +137,30 @@ export class UUIColorSwatchElement extends LabelMixin( this.selectableTarget = button || this; } + #contrast(hex: string): string { + const rgb = this.#hexToRgb(hex); + const o = Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000); + + return o <= 180 ? 'dark' : 'light'; + } + + #hexToRgb(hex: string): number[] { + hex = hex.startsWith('#') ? hex.slice(1) : hex; + if (hex.length === 3) { + hex = Array.from(hex).reduce((str, x) => str + x + x, ''); // 123 -> 112233 + } + + const bigint = parseInt(hex, 16); + const r = (bigint >> 16) & 255; + const g = (bigint >> 8) & 255; + const b = bigint & 255; + + return [r, g, b]; + } + + #rgbToHex = (r: number, g: number, b: number, hash: '#' | '' = ''): string => + hash + ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); + render() { return html`