mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(site): add legacy auto sync helper and tritan cleanup (#25180)
## Summary - Add `legacyAutoToSync` to map legacy auto-family theme preferences to light and dark sync pairs. - Expand colorblind theme tests for legacy auto-family handling. - Move tritan success and git-added surfaces onto the sky-blue palette in theme roles and CSS variables. ## Dependencies - None. This can merge independently before the dropdown work. ## Validation - `pnpm -C site exec vitest run --project=unit src/theme/colorblind.test.ts` - `pnpm -C site lint:types` - Pre-commit hook passed on the branch commit.
This commit is contained in:
+26
-3
@@ -199,9 +199,15 @@
|
||||
success+error axis onto sky-blue (success) + vermilion/orange
|
||||
(destructive). Warning shifts to fuchsia so it does not collide
|
||||
with destructive states on the orange axis.
|
||||
- dark-tritan / light-tritan: keep the red/green success+error
|
||||
axis intact and move warning from amber to fuchsia because
|
||||
amber and sky-blue blur together under tritanopia.
|
||||
- dark-tritan / light-tritan: keep destructive states on the
|
||||
red/orange axis and shift `success` (and the green-tinted
|
||||
diff/git surfaces) onto sky-blue. This matches GitHub
|
||||
Primer's tritanopia preset, where diff additions render in
|
||||
blue rather than green so the success+destructive pair stays
|
||||
consistent with the protan-deuter palette and with other
|
||||
tritan-aware tools. Warning still moves from amber to
|
||||
fuchsia because amber and sky-blue blur together under
|
||||
tritanopia.
|
||||
*/
|
||||
.light-protan-deuter {
|
||||
--content-success: 199 89% 48%;
|
||||
@@ -252,24 +258,41 @@
|
||||
--surface-git-deleted: 17 75% 15%;
|
||||
}
|
||||
.light-tritan {
|
||||
--content-success: 199 89% 48%;
|
||||
--content-warning: 322 81% 43%;
|
||||
--surface-green: 201 94% 86%;
|
||||
--surface-orange: 289 100% 98%;
|
||||
--border-success: 199 89% 48%;
|
||||
--border-green: 201 94% 86%;
|
||||
--border-warning: 322 81% 60%;
|
||||
--highlight-green: 201 94% 36%;
|
||||
--highlight-orange: 322 81% 43%;
|
||||
--highlight-magenta: 322, 81%, 43%;
|
||||
--syntax-boolean: 322 81% 43%;
|
||||
--syntax-number: 199 89% 38%;
|
||||
--git-added: 199 89% 48%;
|
||||
--git-modified: 322 81% 43%;
|
||||
--git-added-bright: 199 89% 48%;
|
||||
--surface-git-added: 204 94% 94%;
|
||||
}
|
||||
.dark-tritan {
|
||||
--content-success: 199 82% 67%;
|
||||
--content-warning: 322 81% 67%;
|
||||
--surface-green: 201 80% 14%;
|
||||
--surface-orange: 322 70% 15%;
|
||||
--surface-magenta: 322 70% 15%;
|
||||
--border-success: 199 82% 67%;
|
||||
--border-green: 201 94% 86%;
|
||||
--border-magenta: 322 81% 72%;
|
||||
--border-warning: 322 81% 67%;
|
||||
--highlight-green: 201 94% 86%;
|
||||
--highlight-orange: 322 81% 67%;
|
||||
--highlight-magenta: 322 81% 72%;
|
||||
--syntax-boolean: 322 81% 67%;
|
||||
--git-added: 199 82% 67%;
|
||||
--git-modified: 322 81% 72%;
|
||||
--git-added-bright: 199 89% 48%;
|
||||
--surface-git-added: 201 80% 14%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import themes, {
|
||||
baseModeFor,
|
||||
CONCRETE_THEMES,
|
||||
isConcreteThemeName,
|
||||
legacyAutoToSync,
|
||||
resolveThemeName,
|
||||
} from ".";
|
||||
|
||||
@@ -71,8 +72,10 @@ describe("isConcreteThemeName", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects the auto preference (embeds require a concrete theme)", () => {
|
||||
it("rejects legacy auto-family preferences", () => {
|
||||
expect(isConcreteThemeName("auto")).toBe(false);
|
||||
expect(isConcreteThemeName("auto-protan-deuter")).toBe(false);
|
||||
expect(isConcreteThemeName("auto-tritan")).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects non-string and empty values", () => {
|
||||
@@ -84,6 +87,34 @@ describe("isConcreteThemeName", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("legacyAutoToSync", () => {
|
||||
it("maps each legacy auto value to its sync pair", () => {
|
||||
expect(legacyAutoToSync("auto")).toEqual({
|
||||
mode: "sync",
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
});
|
||||
expect(legacyAutoToSync("auto-protan-deuter")).toEqual({
|
||||
mode: "sync",
|
||||
light: "light-protan-deuter",
|
||||
dark: "dark-protan-deuter",
|
||||
});
|
||||
expect(legacyAutoToSync("auto-tritan")).toEqual({
|
||||
mode: "sync",
|
||||
light: "light-tritan",
|
||||
dark: "dark-tritan",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null for concrete theme names and unrelated values", () => {
|
||||
expect(legacyAutoToSync("dark")).toBeNull();
|
||||
expect(legacyAutoToSync("dark-tritan")).toBeNull();
|
||||
expect(legacyAutoToSync("")).toBeNull();
|
||||
expect(legacyAutoToSync(undefined)).toBeNull();
|
||||
expect(legacyAutoToSync("garbage")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("baseModeFor", () => {
|
||||
it("maps every concrete theme to its base mode", () => {
|
||||
for (const name of CONCRETE_THEMES) {
|
||||
@@ -120,4 +151,19 @@ describe("colorblind role palettes", () => {
|
||||
themes.dark.roles.danger,
|
||||
);
|
||||
});
|
||||
|
||||
it("shifts tritan success off the base green role onto sky-blue", () => {
|
||||
expect(themes["light-tritan"].roles.success).not.toEqual(
|
||||
themes.light.roles.success,
|
||||
);
|
||||
expect(themes["dark-tritan"].roles.success).not.toEqual(
|
||||
themes.dark.roles.success,
|
||||
);
|
||||
expect(themes["light-tritan"].roles.success).toEqual(
|
||||
themes["light-protan-deuter"].roles.success,
|
||||
);
|
||||
expect(themes["dark-tritan"].roles.success).toEqual(
|
||||
themes["dark-protan-deuter"].roles.success,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { Roles } from "../roles";
|
||||
import colors from "../tailwindColors";
|
||||
|
||||
// Tritanopia reduces blue/yellow discrimination, so the standard amber
|
||||
// warning can blur into the sky-blue active/notice accents. Under
|
||||
// tritanopia, red vs green remains intact, so we keep `success` on green,
|
||||
// `error` on red, and `danger` on the base orange. Only `warning` shifts
|
||||
// to a magenta/pink that stays distinct from blue and red states.
|
||||
const roles: Roles = {
|
||||
danger: {
|
||||
background: colors.orange[950],
|
||||
@@ -80,31 +75,31 @@ const roles: Roles = {
|
||||
},
|
||||
},
|
||||
success: {
|
||||
background: colors.green[950],
|
||||
outline: colors.green[500],
|
||||
text: colors.green[50],
|
||||
background: colors.sky[950],
|
||||
outline: colors.sky[500],
|
||||
text: colors.sky[50],
|
||||
fill: {
|
||||
solid: colors.green[600],
|
||||
outline: colors.green[600],
|
||||
solid: colors.sky[600],
|
||||
outline: colors.sky[600],
|
||||
text: colors.white,
|
||||
},
|
||||
disabled: {
|
||||
background: colors.green[950],
|
||||
outline: colors.green[800],
|
||||
text: colors.green[200],
|
||||
background: colors.sky[950],
|
||||
outline: colors.sky[800],
|
||||
text: colors.sky[200],
|
||||
fill: {
|
||||
solid: colors.green[800],
|
||||
outline: colors.green[800],
|
||||
solid: colors.sky[800],
|
||||
outline: colors.sky[800],
|
||||
text: colors.white,
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
background: colors.green[900],
|
||||
outline: colors.green[500],
|
||||
background: colors.sky[900],
|
||||
outline: colors.sky[500],
|
||||
text: colors.white,
|
||||
fill: {
|
||||
solid: colors.green[500],
|
||||
outline: colors.green[500],
|
||||
solid: colors.sky[500],
|
||||
outline: colors.sky[500],
|
||||
text: colors.white,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -53,6 +53,35 @@ export const isConcreteThemeName = (
|
||||
return typeof value === "string" && concreteThemeSet.has(value);
|
||||
};
|
||||
|
||||
type LegacyAutoSync = {
|
||||
mode: "sync";
|
||||
light: ConcreteThemeName;
|
||||
dark: ConcreteThemeName;
|
||||
};
|
||||
|
||||
const LEGACY_AUTO_SYNC: Record<string, LegacyAutoSync> = {
|
||||
auto: { mode: "sync", light: "light", dark: "dark" },
|
||||
"auto-protan-deuter": {
|
||||
mode: "sync",
|
||||
light: "light-protan-deuter",
|
||||
dark: "dark-protan-deuter",
|
||||
},
|
||||
"auto-tritan": {
|
||||
mode: "sync",
|
||||
light: "light-tritan",
|
||||
dark: "dark-tritan",
|
||||
},
|
||||
};
|
||||
|
||||
export const legacyAutoToSync = (
|
||||
preference: string | undefined,
|
||||
): LegacyAutoSync | null => {
|
||||
if (!preference) {
|
||||
return null;
|
||||
}
|
||||
return LEGACY_AUTO_SYNC[preference] ?? null;
|
||||
};
|
||||
|
||||
export const resolveThemeName = (
|
||||
preference: string | undefined,
|
||||
osScheme: "dark" | "light",
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type { Roles } from "../roles";
|
||||
import colors from "../tailwindColors";
|
||||
|
||||
// Tritanopia reduces blue/yellow discrimination. Red vs green remains
|
||||
// intact, so we keep `success` on green, `error` on red, and `danger`
|
||||
// on the base orange. Only `warning` shifts to a magenta/fuchsia that
|
||||
// stays distinct from the blue accents and red destructive states.
|
||||
const roles: Roles = {
|
||||
danger: {
|
||||
background: colors.orange[50],
|
||||
@@ -79,31 +75,31 @@ const roles: Roles = {
|
||||
},
|
||||
},
|
||||
success: {
|
||||
background: colors.green[50],
|
||||
outline: colors.green[500],
|
||||
text: colors.green[950],
|
||||
background: colors.sky[100],
|
||||
outline: colors.sky[500],
|
||||
text: colors.sky[950],
|
||||
fill: {
|
||||
solid: colors.green[600],
|
||||
outline: colors.green[600],
|
||||
solid: colors.sky[600],
|
||||
outline: colors.sky[600],
|
||||
text: colors.white,
|
||||
},
|
||||
disabled: {
|
||||
background: colors.green[50],
|
||||
outline: colors.green[800],
|
||||
text: colors.green[800],
|
||||
background: colors.sky[50],
|
||||
outline: colors.sky[800],
|
||||
text: colors.sky[800],
|
||||
fill: {
|
||||
solid: colors.green[800],
|
||||
outline: colors.green[800],
|
||||
solid: colors.sky[800],
|
||||
outline: colors.sky[800],
|
||||
text: colors.white,
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
background: colors.green[100],
|
||||
outline: colors.green[500],
|
||||
background: colors.sky[200],
|
||||
outline: colors.sky[500],
|
||||
text: colors.black,
|
||||
fill: {
|
||||
solid: colors.green[500],
|
||||
outline: colors.green[500],
|
||||
solid: colors.sky[500],
|
||||
outline: colors.sky[500],
|
||||
text: colors.white,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user