Skip to content

Commit

Permalink
Simplify CSS variables for light and dark mode (#2106)
Browse files Browse the repository at this point in the history
* Simplify CSS variables for light and dark mode

* Simplify code

* Use strings for colors

* Remove extra type

* Update colors.ts

* Update utilities.ts

* Update src/lib/theme/utilities.ts

Co-authored-by: Laura Whitaker <laura.whitaker@temporal.io>

* Update src/lib/theme/variables.ts

Co-authored-by: Laura Whitaker <laura.whitaker@temporal.io>

* Format changes

* Update labs.svelte

---------

Co-authored-by: Laura Whitaker <laura.whitaker@temporal.io>
  • Loading branch information
stevekinney and laurakwhit authored May 13, 2024
1 parent 65a05f8 commit 285beb8
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 146 deletions.
1 change: 0 additions & 1 deletion src/lib/holocene/icon/svg/labs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
</script>

<Svg {...$$props}>
{#if active}{/if}
<path
d="M14.25 10.0734V2.25H9.75V10.0734V10.7109L9.41719 11.2547L7.11094 15H16.8891L14.5828 11.2547L14.25 10.7109V10.0734ZM16.5 2.25V10.0734L22.5 19.8234V21.75V24H20.25H3.75H1.5V21.75V19.8234L7.5 10.0734V2.25H7.125H6V0H7.125H7.5H9.75H14.25H16.5H16.875H18V2.25H16.875H16.5Z"
fill="currentColor"
Expand Down
34 changes: 13 additions & 21 deletions src/lib/theme/colors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const palette = {
export type Palette = typeof palette;
export type PaletteColor = keyof Palette;

export const palette = {
blue: {
50: '#EFF5FF',
100: '#DBE8FE',
Expand Down Expand Up @@ -139,33 +142,22 @@ const palette = {
950: '#461B02',
DEFAULT: '#F8A208',
},
} as const satisfies Readonly<Record<string, PaletteColor>>;

type Palette = typeof palette;
} as const satisfies Record<string, Shades>;

export const getColor = (
color: keyof Palette,
shade: Shade = 'DEFAULT',
): HexColor => {
return palette[color][shade];
};

const colors = {
export const colors = {
// primary, secondary, danger, and success should be removed at some point
primary: '#141414',
secondary: getColor('slate', 500),
danger: getColor('red', 700),
success: getColor('green', 200),
white: '#ffffff',
secondary: palette['slate'][500],
danger: palette['red'][700],
success: palette['green'][200],
white: '#FFFFFF',
offWhite: '#F8FAFC',
black: '#141414',
offBlack: '#0F172A',
ultraviolet: '#444CE7',
current: 'currentColor',
transparent: 'transparent',
...palette,
} satisfies Readonly<
Record<string, HexColor | 'transparent' | 'currentColor' | PaletteColor>
} as const satisfies Record<
string,
HexColor | 'transparent' | 'currentColor' | Shades
>;

export default colors;
117 changes: 4 additions & 113 deletions src/lib/theme/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,122 +1,13 @@
import plugin from 'tailwindcss/plugin';

import colors, { getColor } from './colors';
import { css, rgb } from './utilities';

export const variables = {
'--color-primary': rgb(colors.primary),
'--color-secondary': rgb(colors.secondary),
'--color-inverse': rgb(colors.black),
'--color-subtle': rgb(getColor('slate', 950)),
'--color-brand': rgb(getColor('indigo', 800)),
'--color-interactive': rgb(getColor('indigo', 600)),
'--color-interactive-hover': rgb(getColor('indigo', 700)),
'--color-interactive-error': rgb(getColor('red', 600)),
'--color-interactive-active': rgb(getColor('indigo', 600)),
'--color-warning': rgb(getColor('yellow', 100)),

'--color-text-black': rgb(colors.black),

'--color-text-primary': rgb(colors.primary),
'--color-text-secondary': rgb(colors.secondary),
'--color-text-inverse': rgb(colors.offWhite),
'--color-text-subtle': rgb(getColor('slate', 300)),
'--color-text-disabled': rgb(getColor('slate', 500)),
'--color-text-error': rgb(getColor('red', 700)),
'--color-text-information': rgb(getColor('blue', 700)),
'--color-text-success': rgb(getColor('green', 800)),
'--color-text-warning': rgb(getColor('yellow', 800)),
'--color-text-active': rgb(getColor('indigo', 600)),

'--color-surface-background': rgb(colors.offWhite),
'--color-surface-primary': rgb(colors.white),
'--color-surface-secondary': rgb(colors.offWhite),
'--color-surface-interactive': rgb(getColor('indigo', 600)),
'--color-surface-interactive-secondary': rgb(getColor('slate', 100)),
'--color-surface-secondary-active': rgb(getColor('slate', 200)),
'--color-surface-disabled': rgb(getColor('slate', 50)),
'--color-surface-inverse': rgb(colors.primary),
'--color-surface-subtle': rgb(getColor('slate', 100)),
'--color-surface-table': rgb(colors.black),
'--color-surface-badge': rgb(getColor('slate', 100)),
'--color-surface-error': rgb(getColor('red', 50)),
'--color-surface-information': rgb(getColor('blue', 50)),
'--color-surface-success': rgb(getColor('green', 50)),
'--color-surface-warning': rgb(getColor('yellow', 100)),
'--color-surface-danger': rgb(getColor('red', 300)),

'--color-border-primary': rgb(colors.black),
'--color-border-secondary': rgb(getColor('slate', 200)),
'--color-border-subtle': rgb(getColor('slate', 300)),
'--color-border-table': rgb(colors.black),
'--color-border-inverse': rgb(colors.offWhite),
'--color-border-disabled': rgb(getColor('slate', 300)),
'--color-border-error': rgb(getColor('red', 500)),
'--color-border-information': rgb(getColor('blue', 800)),
'--color-border-success': rgb(getColor('green', 800)),
'--color-border-warning': rgb(getColor('yellow', 400)),
'--color-border-danger': rgb(getColor('red', 300)),
'--color-border-interactive': rgb(getColor('indigo', 600)),
'--color-border-interactive-secondary': rgb(getColor('slate', 100)),

'--color-shadow-primary': rgb(getColor('indigo', 600)),
'--color-shadow-secondary': rgb(getColor('indigo', 500)),
'--color-shadow-danger': rgb(getColor('red', 200)),
} as const satisfies Variables;

const dark: Partial<Variables<keyof typeof variables>> = {
'--color-primary': rgb(colors.offWhite),
'--color-secondary': rgb(colors.offBlack),
'--color-inverse': rgb(colors.primary),
'--color-brand': rgb(getColor('indigo', 500)),
'--color-interactive-error': rgb(getColor('red', 400)),
'--color-interactive-active': rgb(getColor('indigo', 500)),
'--color-warning': rgb(getColor('yellow', 950)),

'--color-text-primary': rgb(colors.offWhite),
'--color-text-secondary': rgb(getColor('slate', 200)),
'--color-text-subtle': rgb(getColor('slate', 200)),
'--color-text-disabled': rgb(getColor('slate', 100)),
'--color-text-information': rgb(getColor('blue', 300)),
'--color-text-warning': rgb(getColor('yellow', 300)),
'--color-text-error': rgb(getColor('red', 100)),
'--color-text-success': rgb(getColor('green', 100)),
'--color-text-active': rgb(getColor('indigo', 500)),

'--color-surface-background': rgb(colors.offBlack),
'--color-surface-primary': rgb(colors.black),
'--color-surface-secondary': rgb(colors.offBlack),
'--color-surface-interactive-secondary': rgb(getColor('slate', 800)),
'--color-surface-subtle': rgb(getColor('slate', 900)),
'--color-surface-table': rgb(getColor('slate', 900)),
'--color-surface-badge': rgb(getColor('slate', 700)),
'--color-surface-information': rgb(getColor('indigo', 950)),
'--color-surface-warning': rgb(getColor('yellow', 950)),
'--color-surface-danger': rgb(getColor('red', 300)),
'--color-surface-error': rgb(getColor('red', 950)),
'--color-surface-success': rgb(getColor('green', 950)),

'--color-border-primary': rgb(getColor('slate', 600)),
'--color-border-secondary': rgb(getColor('slate', 700)),
'--color-border-subtle': rgb(getColor('slate', 800)),
'--color-border-table': rgb(getColor('slate', 900)),
'--color-border-inverse': rgb(colors.black),
'--color-border-disabled': rgb(getColor('slate', 100)),
'--color-border-information': rgb(getColor('blue', 700)),
'--color-border-interactive': rgb(getColor('indigo', 600)),
'--color-border-warning': rgb(getColor('yellow', 700)),
'--color-border-danger': rgb(getColor('red', 300)),
'--color-border-error': rgb(getColor('red', 700)),
'--color-border-success': rgb(getColor('green', 700)),
'--color-border-interactive-secondary': rgb(getColor('slate', 800)),

'--color-shadow-danger': rgb(getColor('red', 600)),
} as const;
import { colors } from './colors';
import { css } from './utilities';
import { dark, light } from './variables';

const temporal = plugin(
({ addComponents, addBase }) => {
addBase({
':root': variables,
':root': light,
'[data-theme="dark"]': dark,
});

Expand Down
19 changes: 13 additions & 6 deletions src/lib/theme/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
type CSSVariable = `--${string}`;
type PaletteColor = import('./colors').PaletteColor;
type Palette = import('./colors').Palette;

type RGB = `${number} ${number} ${number}`;
type HexColor = `#${string}`;

type Variables<K extends string = CSSVariable> = Readonly<
Record<K, RGB | `var(${CSSVariable})`>
type CSSVariable = `--${string}`;
type ColorVariables = Readonly<
Record<CSSVariable, { light: ColorName; dark: ColorName }>
>;

type HexColor = `#${string}`;

type Shade =
| 50
| 100
Expand All @@ -22,4 +23,10 @@ type Shade =
| 950
| 'DEFAULT';

type PaletteColor = Record<Shade, HexColor>;
type Shades = Record<Shade, HexColor>;

type Color = [PaletteColor, Shade | undefined] | HexColor;

type ColorName =
| Exclude<keyof typeof import('./colors').colors, PaletteColor>
| `${PaletteColor}.${Shade}`;
34 changes: 29 additions & 5 deletions src/lib/theme/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { variables } from './plugin';
import { colors, palette } from './colors';
import type { Variable } from './variables';

const removeHexPrefix = (hex: `#${string}`) => hex.replace('#', '');
const removeHexPrefix = (hex: HexColor) => hex.replace('#', '');

export const rgb = (hexColor: `#${string}`): RGB => {
export const rgb = (hexColor: HexColor): RGB => {
let hex = removeHexPrefix(hexColor);
hex = hex.length === 3 ? hex.replace(/./g, '$&$&') : hex;
const r = parseInt(hex.substring(0, 2), 16);
Expand All @@ -11,5 +12,28 @@ export const rgb = (hexColor: `#${string}`): RGB => {
return `${r} ${g} ${b}`;
};

export const css = (variable: keyof typeof variables) =>
`rgb(var(${variable}))`;
export const css = (variable: Variable) => `rgb(var(${variable}))`;

export const toColor = (name: ColorName): RGB => {
const [paletteColor, shade] = name.split('.') as [PaletteColor, Shade];
const color = colors[paletteColor];
if (isHexColor(color)) return rgb(color);
if (isPaletteColor(paletteColor)) {
const color = palette[paletteColor];
if (isShade(shade)) return rgb(color[shade]);
return rgb(color.DEFAULT);
}
};

export const isHexColor = (color: unknown): color is HexColor => {
if (typeof color !== 'string') return false;
return /^#[0-9A-F]{6}$/i.test(color);
};

export const isPaletteColor = (color: string): color is PaletteColor =>
color in palette;

export const isShade = (shade: unknown): shade is Shade =>
typeof shade === 'number' ||
typeof Number(shade) === 'number' ||
shade === 'DEFAULT';
Loading

0 comments on commit 285beb8

Please sign in to comment.