fix(renderer): use consistent wheel label colors

This commit is contained in:
tiennm99 committed 2026-07-22 14:34:01 +07:00
1 parent 7a0163ec01
commit 8de459b131
5 files changed
+39 -32

No files matched your search

+3 -2
View File
@@ -9,7 +9,7 @@ import {
getWheelRotationDegrees,
rightPointerClipPath,
} from './wheel-layout.js';
import {getContrastingTextColor, getRadialLabelLayout} from './wheel-label-layout.js';
import {getRadialLabelLayout} from './wheel-label-layout.js';
import {getTheme} from './themes.js';
const baseFont =
@@ -124,7 +124,7 @@ export const WheelComposition = (props) => {
style={{
alignItems: 'center',
boxSizing: 'border-box',
color: getContrastingTextColor(color),
color: theme.text,
display: 'flex',
fontSize: label.fontSize,
flexDirection: 'column',
@@ -138,6 +138,7 @@ export const WheelComposition = (props) => {
padding: `0 ${label.horizontalPadding}px`,
position: 'absolute',
textAlign: 'center',
textShadow: `0 1px 0 ${theme.textHalo}, 0 -1px 0 ${theme.textHalo}, 1px 0 0 ${theme.textHalo}, -1px 0 0 ${theme.textHalo}`,
top: label.y,
transform: `translate(-50%, -50%) rotate(${label.rotation}deg)`,
transformOrigin: '50% 50%',
+2 -2
View File
@@ -21,7 +21,7 @@ export const themes = {
text: '#1f2937',
textHalo: 'rgba(255, 251, 235, 0.92)',
winnerGlow: 'rgba(251, 146, 60, 0.5)',
slices: ['#f43f5e', '#f59e0b', '#84cc16', '#06b6d4', '#8b5cf6', '#ec4899', '#22c55e', '#fb923c'],
slices: ['#fb7185', '#f59e0b', '#84cc16', '#06b6d4', '#a78bfa', '#f472b6', '#22c55e', '#fb923c'],
},
mono: {
background: '#f4f4f5',
@@ -33,7 +33,7 @@ export const themes = {
text: '#18181b',
textHalo: 'rgba(250, 250, 250, 0.9)',
winnerGlow: 'rgba(161, 161, 170, 0.45)',
slices: ['#e4e4e7', '#a1a1aa', '#d4d4d8', '#71717a', '#f4f4f5', '#52525b'],
slices: ['#e4e4e7', '#a1a1aa', '#d4d4d8', '#b8b8bf', '#f4f4f5', '#c4c4ca'],
},
};
@@ -43,7 +43,6 @@ export const estimateTextWidth = (text, fontSize) => {
return (widthUnits + 2) * fontSize;
};
/**
* @param {string[]} units
* @param {number} lineCount
@@ -211,24 +210,3 @@ export const getRadialLabelLayout = ({center, radius, hubRadius, centerDegrees,
lines,
};
};
/**
* @param {string} color
* @returns {'#111827' | '#ffffff'}
*/
export const getContrastingTextColor = (color) => {
const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/iu.exec(color);
if (!match) {
return '#111827';
}
const redHex = match[1] ?? '00';
const greenHex = match[2] ?? '00';
const blueHex = match[3] ?? '00';
const red = Number.parseInt(redHex, 16) / 255;
const green = Number.parseInt(greenHex, 16) / 255;
const blue = Number.parseInt(blueHex, 16) / 255;
const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
return luminance > 0.56 ? '#111827' : '#ffffff';
};
-6
View File
@@ -1,7 +1,6 @@
import {describe, expect, test} from 'vitest';
import {
estimateTextWidth,
getContrastingTextColor,
getLabelFontSize,
getLabelLines,
getLabelTrack,
@@ -242,9 +241,4 @@ describe('wheel label layout', () => {
return !(firstCodeUnit >= 0xdc00 && firstCodeUnit <= 0xdfff) && !(lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff);
})).toBe(true);
});
test('uses contrast text colors for light and dark slices', () => {
expect(getContrastingTextColor('#facc15')).toBe('#111827');
expect(getContrastingTextColor('#7f1d1d')).toBe('#ffffff');
});
});
+34
View File
@@ -0,0 +1,34 @@
import {describe, expect, test} from 'vitest';
import {themes} from '../src/remotion/themes.js';
/**
* @param {string} color
*/
const getRelativeLuminance = (color) => {
const channelHexValues = color.slice(1).match(/.{2}/gu) ?? [];
const channels = channelHexValues
.map((channel) => Number.parseInt(channel, 16) / 255)
.map((channel) => (channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4));
const [red = 0, green = 0, blue = 0] = channels;
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
};
/**
* @param {string} foreground
* @param {string} background
*/
const getContrastRatio = (foreground, background) => {
const luminances = [getRelativeLuminance(foreground), getRelativeLuminance(background)].sort(
(left, right) => right - left,
);
const [lighter = 0, darker = 0] = luminances;
return (lighter + 0.05) / (darker + 0.05);
};
describe('wheel themes', () => {
test.each(Object.entries(themes))('%s uses one readable label color across every slice', (_name, theme) => {
expect(theme.slices.every((slice) => getContrastRatio(theme.text, slice) >= 4.5)).toBe(true);
});
});