feat(design): Matrix toned down to moss, text on an accent fill held to --accent-fg
The lime accent was the loudest of the four (OKLCH L 0.85, C 0.21) and read as neon; the owner chose the moss variant at Midnight's lightness with near-neutral greys, and the Matrix backdrops follow it. Fixed white/black parts on a solid accent fill (two switch knobs, three play buttons, the Up next pill) now use --accent-fg, and theme.contract.test.ts refuses a fixed white or black next to a solid bg-accent.
This commit is contained in:
+2
-1
@@ -70,7 +70,7 @@ style guide.
|
|||||||
|
|
||||||
- **Colour:** only semantic tokens (`--bg --surface --card --fg --muted --accent --accent-fg --danger
|
- **Colour:** only semantic tokens (`--bg --surface --card --fg --muted --accent --accent-fg --danger
|
||||||
--warn --success --info --border`, each with a `-fg` partner where text sits on it). Four schemes × dark
|
--warn --success --info --border`, each with a `-fg` partner where text sits on it). Four schemes × dark
|
||||||
and light (B2): **Midnight** (blue), **Forest** (cyan), **Slate** (fuchsia), **Matrix** (lime). Each earns its
|
and light (B2): **Midnight** (blue), **Forest** (cyan), **Slate** (fuchsia), **Matrix** (moss). Each earns its
|
||||||
place with its own hue family. Normal text ≥ 4.5:1 and a control's edge ≥ 3:1 in **every** scheme and mode.
|
place with its own hue family. Normal text ≥ 4.5:1 and a control's edge ≥ 3:1 in **every** scheme and mode.
|
||||||
[WCAG 1.4.3, 1.4.11] The input edge token is set under Controls (A4).
|
[WCAG 1.4.3, 1.4.11] The input edge token is set under Controls (A4).
|
||||||
- **The accent never looks like a status.** A scheme's accent is at least 30° of OKLCH hue away from
|
- **The accent never looks like a status.** A scheme's accent is at least 30° of OKLCH hue away from
|
||||||
@@ -302,3 +302,4 @@ Wherever there is text, the user can select it and copy it, and no click action
|
|||||||
| 2026-09-24 | C4 Dialogs | Focus by purpose; a pinned footer | Focus on Close; the footer scrolls | A form starts in its first field, and Save stays on screen with the keyboard up |
|
| 2026-09-24 | C4 Dialogs | Focus by purpose; a pinned footer | Focus on Close; the footer scrolls | A form starts in its first field, and Save stays on screen with the keyboard up |
|
||||||
| 2026-09-24 | C5 Plex player controls | The owner's lean set: no Stop / Restart / ±10 s buttons, Back by position, Next only with a next item, Resume + From the start on the card, touch double-tap ±10 s | Every control in one row (it ran off-screen below 1024); Back by a press counter; ±10 s keyboard-only | The row fits a portrait tablet; the decision to restart is made on the card; a tablet keeps the 10 s skip |
|
| 2026-09-24 | C5 Plex player controls | The owner's lean set: no Stop / Restart / ±10 s buttons, Back by position, Next only with a next item, Resume + From the start on the card, touch double-tap ±10 s | Every control in one row (it ran off-screen below 1024); Back by a press counter; ±10 s keyboard-only | The row fits a portrait tablet; the decision to restart is made on the card; a tablet keeps the 10 s skip |
|
||||||
| 2026-09-24 | Glass tuner | Retired; the schemes are the only colour choice | Keep it with ranges clamped to the rules | Free tuning is the drift the constitution exists to stop (owner) |
|
| 2026-09-24 | Glass tuner | Retired; the schemes are the only colour choice | Keep it with ranges clamped to the rules | Free tuning is the drift the constitution exists to stop (owner) |
|
||||||
|
| 2026-09-24 | Matrix toned down | Moss #86a05a at Midnight's lightness, near-neutral greys; every white-on-accent part → `--accent-fg` | The B2 lime #a3e635 | The owner found the lime neon and tiring; it was the loudest accent (L 0.85, C 0.21), and fixed white parts sat on it at 1.5:1 |
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1000" preserveAspectRatio="xMidYMid slice">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1000" preserveAspectRatio="xMidYMid slice">
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient id="a" cx="20%" cy="18%" r="55%"><stop offset="0%" stop-color="#cfe8a0" stop-opacity=".9"/><stop offset="100%" stop-color="#cfe8a0" stop-opacity="0"/></radialGradient>
|
<radialGradient id="a" cx="20%" cy="18%" r="55%"><stop offset="0%" stop-color="#d9e2c4" stop-opacity=".9"/><stop offset="100%" stop-color="#d9e2c4" stop-opacity="0"/></radialGradient>
|
||||||
<radialGradient id="b" cx="86%" cy="26%" r="52%"><stop offset="0%" stop-color="#a3e635" stop-opacity=".45"/><stop offset="100%" stop-color="#a3e635" stop-opacity="0"/></radialGradient>
|
<radialGradient id="b" cx="86%" cy="26%" r="52%"><stop offset="0%" stop-color="#86a05a" stop-opacity=".35"/><stop offset="100%" stop-color="#86a05a" stop-opacity="0"/></radialGradient>
|
||||||
<radialGradient id="c" cx="68%" cy="92%" r="60%"><stop offset="0%" stop-color="#b8dca8" stop-opacity=".85"/><stop offset="100%" stop-color="#b8dca8" stop-opacity="0"/></radialGradient>
|
<radialGradient id="c" cx="68%" cy="92%" r="60%"><stop offset="0%" stop-color="#cdd6bf" stop-opacity=".85"/><stop offset="100%" stop-color="#cdd6bf" stop-opacity="0"/></radialGradient>
|
||||||
<radialGradient id="d" cx="8%" cy="88%" r="55%"><stop offset="0%" stop-color="#96d69c" stop-opacity=".8"/><stop offset="100%" stop-color="#96d69c" stop-opacity="0"/></radialGradient>
|
<radialGradient id="d" cx="8%" cy="88%" r="55%"><stop offset="0%" stop-color="#c3cfb3" stop-opacity=".8"/><stop offset="100%" stop-color="#c3cfb3" stop-opacity="0"/></radialGradient>
|
||||||
<filter id="s"><feGaussianBlur stdDeviation="60"/></filter>
|
<filter id="s"><feGaussianBlur stdDeviation="60"/></filter>
|
||||||
<filter id="g"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter>
|
<filter id="g"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter>
|
||||||
</defs>
|
</defs>
|
||||||
<rect width="1600" height="1000" fill="#eaf5ec"/>
|
<rect width="1600" height="1000" fill="#eef0e8"/>
|
||||||
<g filter="url(#s)">
|
<g filter="url(#s)">
|
||||||
<rect width="1600" height="1000" fill="url(#a)"/>
|
<rect width="1600" height="1000" fill="url(#a)"/>
|
||||||
<rect width="1600" height="1000" fill="url(#c)"/>
|
<rect width="1600" height="1000" fill="url(#c)"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,13 +1,13 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1000" preserveAspectRatio="xMidYMid slice">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1000" preserveAspectRatio="xMidYMid slice">
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient id="a" cx="20%" cy="18%" r="55%"><stop offset="0%" stop-color="#3a5a0f" stop-opacity=".95"/><stop offset="100%" stop-color="#3a5a0f" stop-opacity="0"/></radialGradient>
|
<radialGradient id="a" cx="20%" cy="18%" r="55%"><stop offset="0%" stop-color="#34431f" stop-opacity=".95"/><stop offset="100%" stop-color="#34431f" stop-opacity="0"/></radialGradient>
|
||||||
<radialGradient id="b" cx="86%" cy="26%" r="52%"><stop offset="0%" stop-color="#a3e635" stop-opacity=".45"/><stop offset="100%" stop-color="#a3e635" stop-opacity="0"/></radialGradient>
|
<radialGradient id="b" cx="86%" cy="26%" r="52%"><stop offset="0%" stop-color="#86a05a" stop-opacity=".35"/><stop offset="100%" stop-color="#86a05a" stop-opacity="0"/></radialGradient>
|
||||||
<radialGradient id="c" cx="68%" cy="92%" r="60%"><stop offset="0%" stop-color="#1a3a14" stop-opacity=".9"/><stop offset="100%" stop-color="#1a3a14" stop-opacity="0"/></radialGradient>
|
<radialGradient id="c" cx="68%" cy="92%" r="60%"><stop offset="0%" stop-color="#1d2a18" stop-opacity=".9"/><stop offset="100%" stop-color="#1d2a18" stop-opacity="0"/></radialGradient>
|
||||||
<radialGradient id="d" cx="8%" cy="88%" r="55%"><stop offset="0%" stop-color="#164a26" stop-opacity=".85"/><stop offset="100%" stop-color="#164a26" stop-opacity="0"/></radialGradient>
|
<radialGradient id="d" cx="8%" cy="88%" r="55%"><stop offset="0%" stop-color="#22301c" stop-opacity=".85"/><stop offset="100%" stop-color="#22301c" stop-opacity="0"/></radialGradient>
|
||||||
<filter id="s"><feGaussianBlur stdDeviation="60"/></filter>
|
<filter id="s"><feGaussianBlur stdDeviation="60"/></filter>
|
||||||
<filter id="g"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter>
|
<filter id="g"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter>
|
||||||
</defs>
|
</defs>
|
||||||
<rect width="1600" height="1000" fill="#020805"/>
|
<rect width="1600" height="1000" fill="#0a0c09"/>
|
||||||
<g filter="url(#s)">
|
<g filter="url(#s)">
|
||||||
<rect width="1600" height="1000" fill="url(#a)"/>
|
<rect width="1600" height="1000" fill="url(#a)"/>
|
||||||
<rect width="1600" height="1000" fill="url(#c)"/>
|
<rect width="1600" height="1000" fill="url(#c)"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -594,7 +594,7 @@ export default function PlexInfo({
|
|||||||
{onPlay && (
|
{onPlay && (
|
||||||
<button
|
<button
|
||||||
onClick={onPlay}
|
onClick={onPlay}
|
||||||
className="inline-flex items-center gap-2 rounded-xl bg-accent px-4 py-2 font-medium text-white hover:opacity-90"
|
className="inline-flex items-center gap-2 rounded-xl bg-accent px-4 py-2 font-medium text-accent-fg hover:opacity-90"
|
||||||
>
|
>
|
||||||
<Play className="w-5 h-5 fill-current" />
|
<Play className="w-5 h-5 fill-current" />
|
||||||
{inProgress ? t("plex.resume") : t("plex.play")}
|
{inProgress ? t("plex.resume") : t("plex.play")}
|
||||||
|
|||||||
@@ -569,7 +569,7 @@ export default function PlexPlaylistView({
|
|||||||
{items.length > 0 && (
|
{items.length > 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={() => onPlay(order[0]!, order)}
|
onClick={() => onPlay(order[0]!, order)}
|
||||||
className="inline-flex items-center gap-2 rounded-xl bg-accent px-4 py-2 font-medium text-white hover:opacity-90"
|
className="inline-flex items-center gap-2 rounded-xl bg-accent px-4 py-2 font-medium text-accent-fg hover:opacity-90"
|
||||||
>
|
>
|
||||||
<Play className="h-5 w-5 fill-current" />
|
<Play className="h-5 w-5 fill-current" />
|
||||||
{t("plex.playlist.playAll")}
|
{t("plex.playlist.playAll")}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export function UpNextCard({
|
|||||||
<div className="mt-auto flex items-center gap-2 pt-2.5">
|
<div className="mt-auto flex items-center gap-2 pt-2.5">
|
||||||
<button
|
<button
|
||||||
onClick={onAdvance}
|
onClick={onAdvance}
|
||||||
className="flex items-center gap-1.5 rounded-full bg-accent px-3 py-1.5 text-xs font-semibold text-black hover:brightness-110"
|
className="flex items-center gap-1.5 rounded-full bg-accent px-3 py-1.5 text-xs font-semibold text-accent-fg hover:brightness-110"
|
||||||
>
|
>
|
||||||
<span className="relative grid h-5.5 w-5.5 flex-none place-items-center">
|
<span className="relative grid h-5.5 w-5.5 flex-none place-items-center">
|
||||||
<svg viewBox="0 0 24 24" className="absolute inset-0 -rotate-90">
|
<svg viewBox="0 0 24 24" className="absolute inset-0 -rotate-90">
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export function PlexPosterCard({
|
|||||||
<div className="absolute inset-0 bg-black/25" />
|
<div className="absolute inset-0 bg-black/25" />
|
||||||
<div className="absolute inset-x-0 top-[30%] flex justify-center">
|
<div className="absolute inset-x-0 top-[30%] flex justify-center">
|
||||||
<div
|
<div
|
||||||
className="grid h-12 w-12 scale-90 place-items-center rounded-full text-white shadow-xl transition-transform duration-200 group-hover:scale-100"
|
className="grid h-12 w-12 scale-90 place-items-center rounded-full text-accent-fg shadow-xl transition-transform duration-200 group-hover:scale-100"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
"radial-gradient(circle at 38% 32%, color-mix(in srgb, var(--accent) 72%, white), var(--accent))",
|
"radial-gradient(circle at 38% 32%, color-mix(in srgb, var(--accent) 72%, white), var(--accent))",
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export function PrefToggle({
|
|||||||
className={`relative h-4 w-7 rounded-full transition ${on ? "bg-accent" : "bg-border"}`}
|
className={`relative h-4 w-7 rounded-full transition ${on ? "bg-accent" : "bg-border"}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`absolute top-0.5 h-3 w-3 rounded-full bg-white transition-all ${on ? "left-3.5" : "left-0.5"}`}
|
className={`absolute top-0.5 h-3 w-3 rounded-full transition-all ${on ? "left-3.5 bg-accent-fg" : "left-0.5 bg-white"}`}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ export function Switch({
|
|||||||
className={`w-9 h-5 rounded-full transition relative shrink-0 disabled:opacity-50 disabled:cursor-not-allowed ${checked ? "bg-accent" : "bg-border"}`}
|
className={`w-9 h-5 rounded-full transition relative shrink-0 disabled:opacity-50 disabled:cursor-not-allowed ${checked ? "bg-accent" : "bg-border"}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-all ${
|
className={`absolute top-0.5 w-4 h-4 rounded-full shadow transition-all ${
|
||||||
checked ? "left-4.5" : "left-0.5"
|
checked ? "left-4.5 bg-accent-fg" : "left-0.5 bg-white"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</FocusSafeButton>
|
</FocusSafeButton>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function TouchRow() {
|
|||||||
<div className="flex min-h-11 items-center justify-between rounded-lg bg-card/60 px-3">
|
<div className="flex min-h-11 items-center justify-between rounded-lg bg-card/60 px-3">
|
||||||
<span className="text-sm">Dark mode</span>
|
<span className="text-sm">Dark mode</span>
|
||||||
<span className="relative h-5 w-9 rounded-full bg-accent">
|
<span className="relative h-5 w-9 rounded-full bg-accent">
|
||||||
<span className="absolute right-0.5 top-0.5 h-4 w-4 rounded-full bg-white" />
|
<span className="absolute right-0.5 top-0.5 h-4 w-4 rounded-full bg-accent-fg" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,7 +136,7 @@ function NativeSet() {
|
|||||||
<label className={clsx(row, "justify-between")}>
|
<label className={clsx(row, "justify-between")}>
|
||||||
Hide Shorts
|
Hide Shorts
|
||||||
<span className="relative h-5 w-9 rounded-full bg-accent">
|
<span className="relative h-5 w-9 rounded-full bg-accent">
|
||||||
<span className="absolute right-0.5 top-0.5 h-4 w-4 rounded-full bg-white" />
|
<span className="absolute right-0.5 top-0.5 h-4 w-4 rounded-full bg-accent-fg" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex flex-col gap-1 px-2 text-sm">
|
<label className="flex flex-col gap-1 px-2 text-sm">
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ const SCHEMES: SchemeDef[] = [
|
|||||||
light: pal("#f6f7f9", "#ffffff", "#e3e6eb", "#1c1f26", "#5d636e", "#a21caf", "#ffffff"),
|
light: pal("#f6f7f9", "#ffffff", "#e3e6eb", "#1c1f26", "#5d636e", "#a21caf", "#ffffff"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Matrix · lime",
|
name: "Matrix · moss",
|
||||||
dark: pal("#030a06", "#08130d", "#163d27", "#cfe8d6", "#6fae86", "#a3e635", "#04170c"),
|
dark: pal("#0b0e0b", "#111510", "#2a3226", "#d9ded6", "#8f9a8a", "#86a05a", "#0c1208"),
|
||||||
light: pal("#f0f7f1", "#ffffff", "#cfe6d6", "#08130b", "#4c6b56", "#4d7c0f", "#ffffff"),
|
light: pal("#f4f5f0", "#ffffff", "#dfe3d8", "#161a14", "#5a6352", "#556b2f", "#ffffff"),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -472,7 +472,7 @@ export function BatchB() {
|
|||||||
id="b2"
|
id="b2"
|
||||||
code="B2"
|
code="B2"
|
||||||
title="The scheme set"
|
title="The scheme set"
|
||||||
rule="Four schemes, four hue families: Midnight blue, Forest cyan, Slate fuchsia, Matrix lime. YouTube and Starship are gone (a user on them lands on Midnight). Rule: an accent is ≥ 30° of OKLCH hue from danger, warn and success, and ≥ 4.5:1 as text and under its button text, dark and light."
|
rule="Four schemes, four hue families: Midnight blue, Forest cyan, Slate fuchsia, Matrix moss. YouTube and Starship are gone (a user on them lands on Midnight). Rule: an accent is ≥ 30° of OKLCH hue from danger, warn and success, and ≥ 4.5:1 as text and under its button text, dark and light."
|
||||||
>
|
>
|
||||||
<div className="grid gap-2 grid-cols-[repeat(auto-fill,minmax(9.5rem,1fr))]">
|
<div className="grid gap-2 grid-cols-[repeat(auto-fill,minmax(9.5rem,1fr))]">
|
||||||
{SCHEMES.flatMap((s) =>
|
{SCHEMES.flatMap((s) =>
|
||||||
|
|||||||
+15
-13
@@ -960,24 +960,26 @@ html[data-scheme="slate"][data-theme="light"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Matrix — near-black with a lime accent (E28 B2: the terminal green read as success) */
|
/* Matrix — near-black with a lime accent (E28 B2: the terminal green read as success) */
|
||||||
|
/* Matrix is moss, not neon (owner, 2026-09-24): the accent sits at Midnight's lightness (OKLCH L 0.67)
|
||||||
|
with half the old lime's chroma, and the neutrals are barely tinted, so the page doesn't glow green. */
|
||||||
html[data-scheme="matrix"][data-theme="dark"] {
|
html[data-scheme="matrix"][data-theme="dark"] {
|
||||||
--bg: #030a06;
|
--bg: #0b0e0b;
|
||||||
--surface: #08130d;
|
--surface: #111510;
|
||||||
--card: #0b1c12;
|
--card: #161b15;
|
||||||
--border: #163d27;
|
--border: #2a3226;
|
||||||
--fg: #cfe8d6;
|
--fg: #d9ded6;
|
||||||
--muted: #6fae86;
|
--muted: #8f9a8a;
|
||||||
--accent: #a3e635;
|
--accent: #86a05a;
|
||||||
--accent-fg: #04170c;
|
--accent-fg: #0c1208;
|
||||||
}
|
}
|
||||||
html[data-scheme="matrix"][data-theme="light"] {
|
html[data-scheme="matrix"][data-theme="light"] {
|
||||||
--bg: #f0f7f1;
|
--bg: #f4f5f0;
|
||||||
--surface: #ffffff;
|
--surface: #ffffff;
|
||||||
--card: #ffffff;
|
--card: #ffffff;
|
||||||
--border: #cfe6d6;
|
--border: #dfe3d8;
|
||||||
--fg: #08130b;
|
--fg: #161a14;
|
||||||
--muted: #4c6b56;
|
--muted: #5a6352;
|
||||||
--accent: #4d7c0f;
|
--accent: #556b2f;
|
||||||
--accent-fg: #ffffff;
|
--accent-fg: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { existsSync, readFileSync } from "node:fs";
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { hexContrast, hueGap, mixHex } from "./colorMath";
|
import { hexContrast, hueGap, mixHex } from "./colorMath";
|
||||||
import { SCHEMES } from "./theme";
|
import { SCHEMES } from "./theme";
|
||||||
@@ -40,6 +40,23 @@ it("styles exactly the schemes the app offers", () => {
|
|||||||
expect(css).toMatch(/--edge-strong:\s*color-mix\(in srgb, var\(--fg\) 55%, var\(--bg\)\)/);
|
expect(css).toMatch(/--edge-strong:\s*color-mix\(in srgb, var\(--fg\) 55%, var\(--bg\)\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The 4.5:1 above only holds for text that USES --accent-fg: a fixed white or black on a solid accent
|
||||||
|
// fill passes on one scheme and fails on the next (white on the old Matrix lime was 1.5:1).
|
||||||
|
it("text on a solid accent fill uses --accent-fg", () => {
|
||||||
|
const root = new URL("../", import.meta.url);
|
||||||
|
const offenders: string[] = [];
|
||||||
|
for (const f of readdirSync(root, { recursive: true, encoding: "utf8" })) {
|
||||||
|
if (!f.endsWith(".tsx")) continue;
|
||||||
|
readFileSync(new URL(f.replace(/\\/g, "/"), root), "utf8")
|
||||||
|
.split("\n")
|
||||||
|
.forEach((line, i) => {
|
||||||
|
if (/\bbg-accent(?![\w/-])/.test(line) && /\b(text|bg)-(white|black)\b/.test(line))
|
||||||
|
offenders.push(`${f}:${i + 1}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect(offenders).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
describe.each(SCHEMES.map((s) => s.id))("scheme %s", (id) => {
|
describe.each(SCHEMES.map((s) => s.id))("scheme %s", (id) => {
|
||||||
it.each(MODES)("%s: backdrop rule and file", (mode) => {
|
it.each(MODES)("%s: backdrop rule and file", (mode) => {
|
||||||
const file = `backdrops/${mode === "light" ? "light/" : ""}${id}.svg`;
|
const file = `backdrops/${mode === "light" ? "light/" : ""}${id}.svg`;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const SCHEMES: { id: Scheme; name: string; swatch: string }[] = [
|
|||||||
{ id: "midnight", name: "Midnight", swatch: "#6d8cff" },
|
{ id: "midnight", name: "Midnight", swatch: "#6d8cff" },
|
||||||
{ id: "forest", name: "Forest", swatch: "#22d3ee" },
|
{ id: "forest", name: "Forest", swatch: "#22d3ee" },
|
||||||
{ id: "slate", name: "Slate", swatch: "#e879f9" },
|
{ id: "slate", name: "Slate", swatch: "#e879f9" },
|
||||||
{ id: "matrix", name: "Matrix", swatch: "#a3e635" },
|
{ id: "matrix", name: "Matrix", swatch: "#86a05a" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface ThemePrefs {
|
export interface ThemePrefs {
|
||||||
|
|||||||
Reference in New Issue
Block a user