From 154740f48166a93c9a8ea2890a65438dd5c20ff1 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Tue, 24 Feb 2026 12:36:38 +0100 Subject: [PATCH] feat(settings/options/2048): per-color scheme --- .../BottomScreen/Menus/Options/2048.vue | 78 ++++++++++++------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/app/components/Settings/BottomScreen/Menus/Options/2048.vue b/app/components/Settings/BottomScreen/Menus/Options/2048.vue index 14635d8..2c9867a 100644 --- a/app/components/Settings/BottomScreen/Menus/Options/2048.vue +++ b/app/components/Settings/BottomScreen/Menus/Options/2048.vue @@ -42,28 +42,55 @@ const handleActivateA = () => { }); }; -// TODO: one color scheme per app color -const TILE_COLORS: Record = { - [0]: { bg: "#f7f7f7", fg: "#776e65" }, - [2]: { bg: "#ebebf3", fg: "#292929" }, - [4]: { bg: "#d3dbe3", fg: "#292929" }, - [8]: { bg: "#bacbd3", fg: "#292929" }, - [16]: { bg: "#a2bac3", fg: "#f9f6f2" }, - [32]: { bg: "#8aa2b2", fg: "#f9f6f2" }, - [64]: { bg: "#7192a2", fg: "#f9f6f2" }, - [128]: { bg: "#698aa2", fg: "#f9f6f2" }, - [256]: { bg: "#61829a", fg: "#f9f6f2" }, - [512]: { bg: "#5c7b92", fg: "#f9f6f2" }, - [1024]: { bg: "#57758a", fg: "#f9f6f2" }, // -2.5L, -0.8C - [2048]: { bg: "#476277", fg: "#f9f6f2" }, - [4046]: { bg: "#173446", fg: "#f9f6f2" }, +const app = useAppStore(); + +const TILE_VALUES = [0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]; +const APP_COLOR_INDEX = TILE_VALUES.indexOf(2048); + +const buildTileColors = ( + base: string, +): Record => { + const result: Record = { + [0]: { bg: "#f7f7f7", fg: "#776e65" }, + }; + + for (let i = 1; i < TILE_VALUES.length; i += 1) { + const value = TILE_VALUES[i]!; + // 0 -> 1 = tile 2 -> 2048 + const progress = (i - 1) / (APP_COLOR_INDEX - 1); + + let lightnessBoost: number; + let chromaFactor: number; + + if (i <= APP_COLOR_INDEX) { + lightnessBoost = (1 - progress) * 0.35; + chromaFactor = 0.05 + progress * 0.95; + } else { + const stepsAbove2048 = i - APP_COLOR_INDEX; + lightnessBoost = -stepsAbove2048 * 0.07; + chromaFactor = 1 + stepsAbove2048 * 0.15; + } + + const bg = `oklch(from ${base} calc(l + ${lightnessBoost}) calc(c * ${chromaFactor}) h)`; + // change text color based on background lightness + const fg = `oklch(from ${bg} clamp(0, (0.6 - l) * 999, 1) 0 h)`; + result[value] = { bg, fg }; + } + + return result; }; -const LAST_TILE_COLOR = - Object.values(TILE_COLORS)[Object.values(TILE_COLORS).length - 1]!; + +const TILE_COLORS = computed(() => buildTileColors(app.color.hex)); +const LAST_TILE_COLOR = computed(() => { + const values = Object.values(TILE_COLORS.value); + return values[values.length - 1]!; +}); const TILE_SIZE = 28; const ANIM_DURATION = 0.1; -const BORDER_COLOR = "#d7d7d7"; +const BORDER_COLOR = computed( + () => `oklch(from ${app.color.hex} 0.88 0.015 h)`, +); const BORDER_SIZE = 3; const BOARD_X = 64; @@ -215,7 +242,7 @@ onRender((ctx) => { assets.images.settings.bottomScreen.options._2048.frame.draw(ctx, -3, -3); - ctx.fillStyle = BORDER_COLOR; + ctx.fillStyle = BORDER_COLOR.value; ctx.fillRect( 0, 0, @@ -225,7 +252,7 @@ onRender((ctx) => { for (let row = 0; row < BOARD_SIZE; row += 1) { for (let col = 0; col < BOARD_SIZE; col += 1) { - ctx.fillStyle = TILE_COLORS[0]!.bg; + ctx.fillStyle = TILE_COLORS.value[0]!.bg; ctx.fillRect(cellX(col), cellY(row), TILE_SIZE, TILE_SIZE); } } @@ -236,7 +263,7 @@ onRender((ctx) => { } for (const tile of tiles) { - const color = TILE_COLORS[tile.value] ?? LAST_TILE_COLOR; + const color = TILE_COLORS.value[tile.value] ?? LAST_TILE_COLOR.value; ctx.save(); const cx = tile.x + TILE_SIZE / 2; @@ -567,15 +594,6 @@ const slide = (rowDir: number, colDir: number) => { useKeyDown(({ key }) => { if (isAnimating.value) return; switch (key) { - // TODO: remove this, testing only - case "n": - savedState.value.board = [ - [0, 0, 2, 4], - [8, 16, 32, 64], - [128, 256, 512, 1024], - [2048, 4096, 8192, 16384], - ]; - break; case "NDS_UP": case "NDS_SWIPE_UP": slide(-1, 0);