From 710854510b3268c14f65555d376077ffcb960423 Mon Sep 17 00:00:00 2001 From: Aurealibe <220354117+Aurealibe@users.noreply.github.com> Date: Fri, 10 Jul 2026 04:31:15 +0100 Subject: [PATCH] Brand tile colors as generated stylesheet (CSP blocks inline styles), footer credit link --- .prettierignore | 1 + scripts/gen-icons.mjs | 30 +++++ src/components/Footer.astro | 3 + src/components/ToolCard.astro | 22 +--- src/data/domains-allowlist.json | 1 + src/styles/brands.generated.css | 220 ++++++++++++++++++++++++++++++++ src/styles/global.css | 1 + 7 files changed, 260 insertions(+), 18 deletions(-) create mode 100644 src/styles/brands.generated.css diff --git a/.prettierignore b/.prettierignore index 3727a0c..257b025 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,4 @@ node_modules/ pnpm-lock.yaml index.html src/icons.generated.ts +src/styles/brands.generated.css diff --git a/scripts/gen-icons.mjs b/scripts/gen-icons.mjs index 24cbfeb..77250ca 100644 --- a/scripts/gen-icons.mjs +++ b/scripts/gen-icons.mjs @@ -40,3 +40,33 @@ writeFileSync(new URL('../src/icons.generated.ts', import.meta.url), out) console.log( `icons: ${found.length} embedded, ${missing.length} missing${missing.length ? ` (${missing.join(', ')})` : ''}`, ) + +// Brand tile colors as a GENERATED STYLESHEET (not inline style attributes: +// the production CSP is style-src 'self', which blocks style= attributes). +// Tile ink is picked by WCAG contrast against the brand color — white ink +// fails on several light brands. +function luminance(hex) { + const n = hex.replace('#', '') + const [r, g, b] = [0, 2, 4].map((i) => { + const c = parseInt(n.slice(i, i + 2), 16) / 255 + return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4 + }) + return 0.2126 * r + 0.7152 * g + 0.0722 * b +} +function inkFor(hex) { + const L = luminance(hex) + return 1.05 / (L + 0.05) >= (L + 0.05) / 0.05 ? '#faf7ee' : '#14161a' +} + +const tools = JSON.parse(readFileSync(new URL('../src/data/tools.json', import.meta.url), 'utf8')) +const rules = tools + .filter((t) => t.color) + .map((t) => `#${t.id} .tool-logo {\n background: ${t.color};\n color: ${inkFor(t.color)};\n}`) +const css = `/* AUTO-GENERATED by scripts/gen-icons.mjs — do not edit. + Brand tile background + WCAG-picked ink per tool (from tools.json). + A stylesheet, not style= attributes: the CSP (style-src 'self') + blocks inline styles in production. */ +${rules.join('\n')} +` +writeFileSync(new URL('../src/styles/brands.generated.css', import.meta.url), css) +console.log(`brands: ${rules.length} tile rules → src/styles/brands.generated.css`) diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 3d1f676..70a2574 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -114,5 +114,8 @@ const localeRoot = `${localePath(locale)}/`.replace('//', '/') {t('footer.factsUpdated')} exitchatcontrol.org +

+ by Aurealibe +

diff --git a/src/components/ToolCard.astro b/src/components/ToolCard.astro index 092c201..8f394de 100644 --- a/src/components/ToolCard.astro +++ b/src/components/ToolCard.astro @@ -26,23 +26,9 @@ if (!tool) { throw new Error(`ToolCard: unknown tool id "${toolId}" (not in src/data/tools.json)`) } -// Tile ink picked by WCAG contrast against the brand color — white ink fails -// on several light brands (the axe lesson from the reviewed PR). -function luminance(hex: string): number { - const n = hex.replace('#', '') - const [r, g, b] = [0, 2, 4].map((i) => { - const c = parseInt(n.slice(i, i + 2), 16) / 255 - return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4 - }) - return 0.2126 * r! + 0.7152 * g! + 0.0722 * b! -} -const brand = tool.color ?? '#7d5a10' -const ink = (() => { - const L = luminance(brand) - const white = 1.05 / (L + 0.05) - const dark = (L + 0.05) / 0.05 - return white >= dark ? '#faf7ee' : '#14161a' -})() +// Tile colors live in src/styles/brands.generated.css (generated from +// tools.json by scripts/gen-icons.mjs) — a stylesheet, because the +// production CSP (style-src 'self') blocks style= attributes. const iconPath = tool.iconSlug ? BRAND_ICONS[tool.iconSlug] : undefined const locale = (Astro.currentLocale ?? defaultLocale) as Locale @@ -72,7 +58,7 @@ const labels = LABELS[locale]
-