// Finds a hue painted as TEXT on a tint of itself (#3150). The tint sits close // to the surface under it, so the raw hue on it measures 1.6–2.4:1 against // AA's 4.5. The fix is the hue's -fg partner (tokens.json colors.fg), mixed // toward parchment: accent-fg on an accent tint measures 5.03. // // Two spellings are checked: // Tailwind one class string holding both `bg-X-tint` / `bg-X/NN` and `text-X` // CSS one rule block holding both a `color-mix(… var(--fs-X) N%, // transparent)` background and `color: var(--fs-X)` // // A pair split across two strings (a tint in one class: directive, the text // in another) is not seen. The lookbehind on `color:` keeps border-color and // outline-color out: a border is a graphic with a 3:1 floor, not text. import { readFileSync, readdirSync, statSync } from 'node:fs'; import { join, relative } from 'node:path'; export const HUES = { accent: 'accent-fg', warning: 'warning-fg', error: 'error-fg', info: 'info-fg', moss: 'success-fg' }; // The quoted string around index i. Read outward from the tint class rather // than pairing every quote in the file, which an apostrophe in prose // ("Couldn't") throws out of step. function enclosingString(source, i) { const start = Math.max(...['"', "'", '`'].map((q) => source.lastIndexOf(q, i))); if (start < 0) return ''; const end = source.indexOf(source[start], i); return end < 0 ? '' : source.slice(start, end + 1); } /** Tailwind class strings that put text-X on a bg-X tint. */ export function findTailwind(source) { const found = []; for (const hue of Object.keys(HUES)) { const tint = new RegExp(`(?<=^|[\\s'"\`])bg-${hue}(?:-tint|/\\d+)(?=$|[\\s'"\`])`, 'g'); const text = new RegExp(`(?:^|[\\s'"\`])text-${hue}(?=$|[\\s'"\`])`); for (const m of source.matchAll(tint)) { if (text.test(enclosingString(source, m.index))) { found.push({ hue, index: m.index, fix: `text-${hue === 'moss' ? 'success' : hue}-fg` }); } } } return found; } /** CSS rule blocks that put color: var(--fs-X) on a color-mix tint of X. */ export function findCss(source) { const found = []; for (const m of source.matchAll(/\{([^{}]*)\}/g)) { for (const hue of Object.keys(HUES)) { const tint = new RegExp( `background(?:-color)?\\s*:\\s*color-mix\\(\\s*in srgb\\s*,\\s*var\\(--fs-${hue}\\)\\s*\\d+%\\s*,\\s*transparent\\s*\\)` ); const text = new RegExp(`(?