Skip to content
Twilight React Playground
ثيم رائدaren

color

functionBeginnerserverbrowserlive demo

Reads a six-digit hex color and returns a readable text color, a dark flag, and darker, lighter or inverted shades.

import { color } from '@salla.sa/twilight-theme-engine/utils';

In plain words

Merchants choose their own brand color, so a theme cannot know in advance whether white or black text is readable on it. color('#ed1c24') works it out: .text is '#FFFFFF' on a dark color and '#000000' on a light one, .isDark says which, and .darken(0.2) or .lighten(0.2) give shades for borders and hover states.

It is plain arithmetic on the color's red, green and blue values. It reads nothing from the store: pass it the store's color yourself (from useTheme()).

Signature

function color(primary: string): {
  primary: string;                      // your input, unchanged
  text: string;                         // '#FFFFFF' or '#000000'
  isDark: boolean;
  darken: (amount: number) => string;   // amount from 0 to 1
  lighten: (amount: number) => string;  // amount from 0 to 1
  invert: () => string;
  rgb: () => { r: number; g: number; b: number };
}

Try it live

color() reads a hex color once and gives you a readable text color and lighter or darker shades of it.Try this: type #fff (three digits) or red: color() cannot read them and treats them as black. Then switch on the store primary color.
Storefront canvas · ar · RTL
primary#3b82f6
darken(0.2)#2f68c5
lighten(0.2)#629bf8
invert()#c47d09

color("#3b82f6") → isDark: true, text: #FFFFFF, rgb(): { r: 59, g: 130, b: 246 }

Controls
Six hex digits, with or without #.
Use the store's primary color
What a theme writes
import { color } from '@salla.sa/twilight-theme-engine/utils';

const c = color('#3b82f6');

export function PromoButton({ children }: { children: React.ReactNode }) {
  return (
    <button type="button" style={{ background: c.primary, color: c.text, borderColor: c.darken(0.2) }}>
      {children}
    </button>
  );
}

Example

app/components/PromoButton.tsx
import { useTheme } from '@salla.sa/twilight-theme-engine/hooks/useTheme';
import { color } from '@salla.sa/twilight-theme-engine/utils';

export function PromoButton({ children }: { children: React.ReactNode }) {
  const { color: themeColor } = useTheme();
  const c = color(themeColor.primary);

  return (
    <button
      type="button"
      style={{ background: c.primary, color: c.text, border: `1px solid ${c.darken(0.2)}` }}
    >
      {children}
    </button>
  );
}

How it behaves

  • isDark is luminance < 0.5, where luminance is (0.299 × r + 0.587 × g + 0.114 × b) / 255. text follows it, in upper case.

  • darken(a) multiplies each channel by 1 - a; lighten(a) moves each channel a of the way to 255. a is clamped to 0–1, and the result is a lower-case #rrggbb.

  • The color is parsed once, when color() is called; the returned functions reuse it. It is cheap enough to call during render.

  • useTheme().color also carries text, reverse_primary, reverse_text and is_dark, sent by Salla. They are separate values, not this calculation: the demo store sends is_dark: true with text: '#000000' for #ed1c24, where color('#ed1c24').text is '#FFFFFF'.

Gotchas

  • Only six hex digits parse (#rrggbb or rrggbb). A three-digit #fff, a name like red, rgb(…) or an eight-digit #rrggbbaa is silently read as black: isDark is true, text is #FFFFFF and lighten(0.2) is #333333, while primary still returns your input. Normalise the value first if it can arrive in another form.

  • The JSDoc of color() and docs/18-hooks-api.md say color('#3b82f6').isDark is false with text '#000000'. The code returns true and '#FFFFFF': that blue's luminance is 0.478.

  • docs/02-theme-engine-core.md shows a createThemeColor() helper. The engine exports no such function; color() is the one that exists.

Related

Source and docs