color_tools.harmony

Generate color harmonies in CIE LCH space.

class color_tools.harmony.HarmonyColor(hue_offset, ideal_lch, was_in_gamut, mapped_lch, rgb, hex, gamut_delta_e)[source]

Bases: object

One ideal harmony color and its optional displayable sRGB realization.

Parameters:
hue_offset: float
ideal_lch: tuple[float, float, float]
was_in_gamut: bool
mapped_lch: tuple[float, float, float] | None
rgb: tuple[int, int, int] | None
hex: str | None
gamut_delta_e: float | None
class color_tools.harmony.HarmonyResult(scheme, base_lch, colors, mood=None, tone='normal', grade_base=False)[source]

Bases: object

Immutable result of generating an LCH harmony.

Parameters:
  • scheme (Literal['analogous', 'complementary', 'full-spectrum', 'monochromatic', 'rainbow', 'split-complementary', 'triadic', 'square', 'tetradic'])

  • base_lch (tuple[float, float, float])

  • colors (tuple[HarmonyColor, ...])

  • mood (Literal['warm', 'cool', 'happy', 'calm', 'intense', 'sad', 'energetic'] | None)

  • tone (Literal['normal', 'dark', 'light'])

  • grade_base (bool)

scheme: Literal['analogous', 'complementary', 'full-spectrum', 'monochromatic', 'rainbow', 'split-complementary', 'triadic', 'square', 'tetradic']
base_lch: tuple[float, float, float]
colors: tuple[HarmonyColor, ...]
mood: Literal['warm', 'cool', 'happy', 'calm', 'intense', 'sad', 'energetic'] | None = None
tone: Literal['normal', 'dark', 'light'] = 'normal'
grade_base: bool = False
color_tools.harmony.generate_harmony_lch(lch, scheme, *, map_to_gamut=True, mood=None, tone='normal', grade_base=False)[source]

Generate a styled harmony directly from CIE LCH coordinates.

Hue-based schemes preserve the base lightness and chroma while rotating hue. Monochromatic harmonies preserve hue while varying lightness and chroma. Opinionated mood profiles adjust lightness and chroma without changing hue, while tone independently produces a darker or lighter variation. The base color remains unchanged unless grade_base is true. Displayable colors reduce chroma as needed while preserving lightness and hue. Set map_to_gamut=False to leave out-of-gamut colors without RGB values.

Parameters:
  • lch (tuple[float, float, float]) – Base color as (lightness, chroma, hue_degrees).

  • scheme (Literal['analogous', 'complementary', 'full-spectrum', 'monochromatic', 'rainbow', 'split-complementary', 'triadic', 'square', 'tetradic']) – Harmony relationship to generate.

  • map_to_gamut (bool) – Map out-of-gamut colors into sRGB when true.

  • mood (Optional[Literal['warm', 'cool', 'happy', 'calm', 'intense', 'sad', 'energetic']]) – Optional opinionated mood profile applied before gamut mapping.

  • tone (Literal['normal', 'dark', 'light']) – Independent normal, dark, or light palette variation.

  • grade_base (bool) – Apply mood and tone to the base color when true.

Return type:

HarmonyResult

Returns:

The ideal and displayable colors in the requested harmony.

Raises:

ValueError – If the color or scheme is invalid, or the color is achromatic.

color_tools.harmony.generate_harmony(rgb, scheme, *, map_to_gamut=True, mood=None, tone='normal', grade_base=False)[source]

Generate a styled LCH-based harmony from an sRGB color.

Parameters:
  • rgb (tuple[int, int, int]) – Base color as integer sRGB channels from 0 through 255.

  • scheme (Literal['analogous', 'complementary', 'full-spectrum', 'monochromatic', 'rainbow', 'split-complementary', 'triadic', 'square', 'tetradic']) – Harmony relationship to generate.

  • map_to_gamut (bool) – Map out-of-gamut colors into sRGB when true.

  • mood (Optional[Literal['warm', 'cool', 'happy', 'calm', 'intense', 'sad', 'energetic']]) – Optional opinionated mood profile applied before gamut mapping.

  • tone (Literal['normal', 'dark', 'light']) – Independent normal, dark, or light palette variation.

  • grade_base (bool) – Apply mood and tone to the base color when true.

Return type:

HarmonyResult

Returns:

The ideal and displayable colors in the requested harmony.

Raises:

ValueError – If the color or scheme is invalid, or the color is achromatic.