Skip to content

Colors - generateColorPalette - avoid reversing on dark mode #2757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/style/__tests__/colors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ describe('style/Colors', () => {
expect(uut.getColorTint('#F1BE0B', 3)).toEqual('#8D7006');
expect(uut.getColorTint('#F1BE0B', 95)).toEqual('#FFFEFA');
});
});

describe('generateColorPalette', () => {
it('should memoize calls for generateColorPalette', () => {
uut.getColorTint('#3F88C5', 20);
uut.getColorTint('#3F88C5', 50);
Expand All @@ -126,6 +128,16 @@ describe('style/Colors', () => {
});
});

it('should generateColorPalette with avoidReverseOnDark option false not reverse on light mode', () => {
const palette = uut.generateColorPalette('#3F88C5', {avoidReverseOnDark: false});
expect(palette).toEqual(['#193852', '#255379', '#316EA1', '#3F88C5', '#66A0D1', '#8DB9DD', '#B5D1E9', '#DCE9F4']);
});

it('should generateColorPalette with avoidReverseOnDark option true not reverse on light mode', () => {
const palette = uut.generateColorPalette('#3F88C5', {avoidReverseOnDark: true});
expect(palette).toEqual(['#193852', '#255379', '#316EA1', '#3F88C5', '#66A0D1', '#8DB9DD', '#B5D1E9', '#DCE9F4']);
});

describe('generateDesignTokens(...)', () => {
it('should generate design tokens from dark color for light theme', () => {
const primaryColor = '#860D86';
Expand Down
30 changes: 16 additions & 14 deletions src/style/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export type GeneratePaletteOptions = {
adjustSaturation?: boolean;
/** Whether to add two extra dark colors usually used for dark mode (generating a palette of 10 instead of 8 colors) */
addDarkestTints?: boolean;
/** Whether to reverse the color palette to generate dark mode palette (pass 'true' to generate the same palette for both light and dark modes) */
avoidReverseOnDark?: boolean;
}
export class Colors {
[key: string]: any;
Expand Down Expand Up @@ -173,6 +175,8 @@ export class Colors {
return validColors ? undefined : results[0];
}

shouldReverseOnDark = (avoidReverseOnDark?: boolean) => !avoidReverseOnDark && this.shouldSupportDarkMode && Scheme.getSchemeType() === 'dark';

getColorTint(colorValue: string | OpaqueColorValue, tintKey: string | number, options: GetColorTintOptions = {}) {
if (_.isUndefined(tintKey) || isNaN(tintKey as number) || _.isUndefined(colorValue)) {
// console.error('"Colors.getColorTint" must accept a color and tintKey params');
Expand All @@ -190,10 +194,8 @@ export class Colors {
if (colorKey) {
const colorKeys = [1, 5, 10, 20, 30, 40, 50, 60, 70, 80];
const keyIndex = _.indexOf(colorKeys, Number(tintKey));
const shouldReverseOnDark =
!options?.avoidReverseOnDark && this.shouldSupportDarkMode && Scheme.getSchemeType() === 'dark';
const key = shouldReverseOnDark ? colorKeys[colorKeys.length - 1 - keyIndex] : tintKey;

const key =
this.shouldReverseOnDark(options?.avoidReverseOnDark) ? colorKeys[colorKeys.length - 1 - keyIndex] : tintKey;
const requiredColorKey = `${colorKey.slice(0, -2)}${key}`;
const requiredColorKey1 = `${colorKey.slice(0, -1)}${key}`;
const requiredColor = this[requiredColorKey] || this[requiredColorKey1];
Expand All @@ -215,17 +217,14 @@ export class Colors {
return colorsPalette[tintLevel - 1];
}

private generatePalette = _.memoize((color: string, options?: GeneratePaletteOptions): string[] => {
const defaultOptions = {adjustLightness: true, adjustSaturation: true, addDarkestTints: false};
const _options = {...defaultOptions, ...options};

private generatePalette = _.memoize((color: string, options?: GeneratePaletteOptions): string[] => {
const hsl = Color(color).hsl();
const lightness = Math.round(hsl.color[2]);
const lightColorsThreshold = _options.adjustLightness && this.shouldGenerateDarkerPalette(color) ? 5 : 0;
const lightColorsThreshold = options?.adjustLightness && this.shouldGenerateDarkerPalette(color) ? 5 : 0;
const ls = [hsl.color[2]];

let l = lightness - 10;
const lightnessLevel = _options.addDarkestTints ? 0 : 20;
const lightnessLevel = options?.addDarkestTints ? 0 : 20;
while (l >= lightnessLevel - lightColorsThreshold) { // darker tints
ls.unshift(l);
l -= 10;
Expand All @@ -243,15 +242,18 @@ export class Colors {
tints.push(tint);
});

const size = _options.addDarkestTints ? 10 : 8;
const size = options?.addDarkestTints ? 10 : 8;
const sliced = tints.slice(0, size);
const adjusted = _options.adjustSaturation && adjustSaturation(sliced, color);
const adjusted = options?.adjustSaturation && adjustSaturation(sliced, color);
return adjusted || sliced;
});

defaultOptions = {adjustLightness: true, adjustSaturation: true, addDarkestTints: false, avoidReverseOnDark: false};

generateColorPalette = _.memoize((color: string, options?: GeneratePaletteOptions): string[] => {
const palette = this.generatePalette(color, options);
return this.shouldSupportDarkMode && Scheme.getSchemeType() === 'dark' ? _.reverse(palette) : palette;
const _options = {...this.defaultOptions, ...options};
const palette = this.generatePalette(color, _options);
return this.shouldReverseOnDark(_options?.avoidReverseOnDark) ? _.reverse(palette) : palette;
});

private generateDesignTokens(primaryColor: string, dark?: boolean) {
Expand Down