feat(appearance): add display preference models
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
import type { ThemeScheme } from "./themes"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color-vision assistance modes.
|
||||||
|
*
|
||||||
|
* These modes select palettes that are easier to distinguish. They are not
|
||||||
|
* simulations of how another person sees the interface and intentionally do
|
||||||
|
* not apply a filter to images or the complete document.
|
||||||
|
*/
|
||||||
|
export type ColorVisionMode = "standard" | "protan" | "deutan" | "tritan"
|
||||||
|
|
||||||
|
export const colorVisionModes = [
|
||||||
|
"standard",
|
||||||
|
"protan",
|
||||||
|
"deutan",
|
||||||
|
"tritan",
|
||||||
|
] as const satisfies readonly ColorVisionMode[]
|
||||||
|
|
||||||
|
export const COLOR_VISION_ATTRIBUTE = "data-ui-color-vision"
|
||||||
|
|
||||||
|
type ColorVisionVariables = Readonly<
|
||||||
|
Record<
|
||||||
|
"destructive" | "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5",
|
||||||
|
string
|
||||||
|
>
|
||||||
|
>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Palettes for semantic data colors that commonly need to be distinguished.
|
||||||
|
* Components must still pair color with text, icons, shapes, or patterns.
|
||||||
|
*/
|
||||||
|
export const colorVisionPalettes: Readonly<
|
||||||
|
Record<
|
||||||
|
Exclude<ColorVisionMode, "standard">,
|
||||||
|
Record<ThemeScheme, ColorVisionVariables>
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
protan: {
|
||||||
|
light: {
|
||||||
|
destructive: "#b54a00",
|
||||||
|
"chart-1": "#0072b2",
|
||||||
|
"chart-2": "#e69f00",
|
||||||
|
"chart-3": "#009e73",
|
||||||
|
"chart-4": "#cc79a7",
|
||||||
|
"chart-5": "#6b5ca5",
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
destructive: "#ff9d66",
|
||||||
|
"chart-1": "#56b4e9",
|
||||||
|
"chart-2": "#f0e442",
|
||||||
|
"chart-3": "#65d5b3",
|
||||||
|
"chart-4": "#e8a4ce",
|
||||||
|
"chart-5": "#afa4e8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
deutan: {
|
||||||
|
light: {
|
||||||
|
destructive: "#a64b00",
|
||||||
|
"chart-1": "#005f9e",
|
||||||
|
"chart-2": "#d88c00",
|
||||||
|
"chart-3": "#8a6f00",
|
||||||
|
"chart-4": "#bd63a0",
|
||||||
|
"chart-5": "#168c9e",
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
destructive: "#ff9d66",
|
||||||
|
"chart-1": "#65bff0",
|
||||||
|
"chart-2": "#f3c969",
|
||||||
|
"chart-3": "#d7c85d",
|
||||||
|
"chart-4": "#e5a0cf",
|
||||||
|
"chart-5": "#63c7d5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tritan: {
|
||||||
|
light: {
|
||||||
|
destructive: "#b83261",
|
||||||
|
"chart-1": "#b83280",
|
||||||
|
"chart-2": "#008f72",
|
||||||
|
"chart-3": "#d55e00",
|
||||||
|
"chart-4": "#6b5ca5",
|
||||||
|
"chart-5": "#53606d",
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
destructive: "#f27ca5",
|
||||||
|
"chart-1": "#ed8bc9",
|
||||||
|
"chart-2": "#62d1b3",
|
||||||
|
"chart-3": "#ff9d66",
|
||||||
|
"chart-4": "#afa4e8",
|
||||||
|
"chart-5": "#b8c2cc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isColorVisionMode(value: unknown): value is ColorVisionMode {
|
||||||
|
return colorVisionModes.some((mode) => mode === value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getColorVisionCssVariables(
|
||||||
|
mode: ColorVisionMode,
|
||||||
|
scheme: ThemeScheme
|
||||||
|
): ColorVisionVariables | undefined {
|
||||||
|
return mode === "standard" ? undefined : colorVisionPalettes[mode][scheme]
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import type { ThemeScheme } from "./themes"
|
||||||
|
|
||||||
|
export type Brightness = "dim" | "normal" | "bright"
|
||||||
|
export type MotionMode = "system" | "reduce" | "full"
|
||||||
|
export type ResolvedMotionMode = Exclude<MotionMode, "system">
|
||||||
|
export type TextScale = "100" | "125" | "150" | "200"
|
||||||
|
|
||||||
|
export const brightnessModes = [
|
||||||
|
"dim",
|
||||||
|
"normal",
|
||||||
|
"bright",
|
||||||
|
] as const satisfies readonly Brightness[]
|
||||||
|
export const motionModes = [
|
||||||
|
"system",
|
||||||
|
"reduce",
|
||||||
|
"full",
|
||||||
|
] as const satisfies readonly MotionMode[]
|
||||||
|
export const textScales = [
|
||||||
|
"100",
|
||||||
|
"125",
|
||||||
|
"150",
|
||||||
|
"200",
|
||||||
|
] as const satisfies readonly TextScale[]
|
||||||
|
|
||||||
|
export const BRIGHTNESS_ATTRIBUTE = "data-ui-brightness"
|
||||||
|
export const MOTION_ATTRIBUTE = "data-ui-motion"
|
||||||
|
export const TEXT_SCALE_ATTRIBUTE = "data-ui-text-scale"
|
||||||
|
|
||||||
|
const surfaceVariableNames = [
|
||||||
|
"background",
|
||||||
|
"card",
|
||||||
|
"popover",
|
||||||
|
"secondary",
|
||||||
|
"muted",
|
||||||
|
"highlight",
|
||||||
|
"sidebar",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export function isBrightness(value: unknown): value is Brightness {
|
||||||
|
return brightnessModes.some((mode) => mode === value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMotionMode(value: unknown): value is MotionMode {
|
||||||
|
return motionModes.some((mode) => mode === value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTextScale(value: unknown): value is TextScale {
|
||||||
|
return textScales.some((scale) => scale === value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts interface surfaces without filtering media, illustrations, or brand
|
||||||
|
* colors. Foreground colors are preserved so dimming does not reduce contrast.
|
||||||
|
*/
|
||||||
|
export function applyBrightnessToTheme(
|
||||||
|
variables: Record<string, string>,
|
||||||
|
brightness: Brightness,
|
||||||
|
scheme: ThemeScheme
|
||||||
|
): Record<string, string> {
|
||||||
|
if (brightness === "normal") return variables
|
||||||
|
|
||||||
|
const target = brightness === "dim" ? "black" : "white"
|
||||||
|
const amount = {
|
||||||
|
bright: scheme === "light" ? 12 : 8,
|
||||||
|
dim: scheme === "light" ? 5 : 12,
|
||||||
|
}[brightness]
|
||||||
|
const adjusted = { ...variables }
|
||||||
|
|
||||||
|
for (const name of surfaceVariableNames) {
|
||||||
|
const value = variables[name]
|
||||||
|
if (value) {
|
||||||
|
adjusted[name] = `color-mix(in oklch,${value},${target} ${amount}%)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjusted
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user