Skip to content

Easy Colors

py_simple.easy_colors

Beginner-friendly helpers for hex and RGB color handling.

contrast_ratio(hex1, hex2)

Calculates WCAG contrast ratio between two hex colors.

Parameters:

Name Type Description Default
hex1 str

First hex color string (e.g., "#000000").

required
hex2 str

Second hex color string (e.g., "#FFFFFF").

required

Returns:

Name Type Description
float float

Contrast ratio between the two colors.

Raises:

Type Description
ValueError

If either hex color is not valid.

Example
from py_simple import contrast_ratio

contrast_ratio("#000000", "#FFFFFF")  # -> 21.0
contrast_ratio("#777777", "#FFFFFF")  # -> 4.48
def contrast(l1, l2):
    lighter = max(l1, l2)
    darker = min(l1, l2)
    return (lighter + 0.05) / (darker + 0.05)

l_black = 0.0
l_white = 1.0
ratio = contrast(l_black, l_white)  # -> 21.0

hex_to_rgb(hex_code)

Converts a hex color code string to an (R, G, B) integer tuple.

Supports 3-digit and 6-digit hex color codes with or without '#'.

Parameters:

Name Type Description Default
hex_code str

Hex color string to convert (e.g., "#FFFFFF" or "fff").

required

Returns:

Type Description
tuple[int, int, int]

tuple[int, int, int]: RGB values as (r, g, b) integers in range 0-255.

Raises:

Type Description
ValueError

If hex_code is not a valid hex color.

Example
from py_simple import hex_to_rgb

hex_to_rgb("#FFFFFF")  # -> (255, 255, 255)
hex_to_rgb("00ff00")  # -> (0, 255, 0)
hex_code = "#FFFFFF".lstrip("#")
if len(hex_code) == 3:
    hex_code = "".join(c * 2 for c in hex_code)
r, g, b = tuple(int(hex_code[i : i + 2], 16) for i in (0, 2, 4))

hex_to_rgba(hex_code, alpha)

Converts a hex color and alpha value into an (R, G, B, A) tuple.

Parameters:

Name Type Description Default
hex_code str

Hex color string to convert (e.g., "#FFFFFF" or "fff").

required
alpha float

Alpha channel value in range 0.0 to 1.0.

required

Returns:

Type Description
tuple[int, int, int, float]

tuple[int, int, int, float]: RGBA values as (r, g, b, alpha).

Raises:

Type Description
ValueError

If hex_code is not a valid hex color.

ValueError

If alpha is outside the 0.0-1.0 range.

Example
from py_simple import hex_to_rgba

hex_to_rgba("#FF0000", 0.5)  # -> (255, 0, 0, 0.5)
hex_to_rgba("00ff00", 1.0)  # -> (0, 255, 0, 1.0)
hex_code = "#FF0000".lstrip("#")
if len(hex_code) == 3:
    hex_code = "".join(c * 2 for c in hex_code)

r = int(hex_code[0:2], 16)
g = int(hex_code[2:4], 16)
b = int(hex_code[4:6], 16)
alpha = 0.5
rgba = (r, g, b, alpha)

hsl_to_rgb(h, s, lightness)

Converts an (H, S, L) color to an (R, G, B) integer tuple.

Hue is measured in degrees (0-360), while Saturation and Lightness are measured as percentages (0-100).

Parameters:

Name Type Description Default
h float

Hue in degrees (0 to 360).

required
s float

Saturation as a percentage (0 to 100).

required
lightness float

Lightness as a percentage (0 to 100).

required

Returns:

Type Description
tuple[int, int, int]

tuple[int, int, int]: RGB values as (r, g, b) integers in range 0-255.

Raises:

Type Description
ValueError

If hue is outside the 0-360 range or saturation/lightness are outside the 0-100 range.

TypeError

If any value is not an int or float.

Example
from py_simple import hsl_to_rgb

hsl_to_rgb(120, 100, 50)  # -> (0, 255, 0)
hsl_to_rgb(0, 0, 100)  # -> (255, 255, 255)
h, s, lightness = 120, 100, 50
s, lightness = s / 100, lightness / 100
chroma = (1 - abs(2 * lightness - 1)) * s
secondary = chroma * (1 - abs((h / 60) % 2 - 1))
match = lightness - chroma / 2
if h < 60:
    red, green, blue = chroma, secondary, 0
elif h < 120:
    red, green, blue = secondary, chroma, 0
elif h < 180:
    red, green, blue = 0, chroma, secondary
elif h < 240:
    red, green, blue = 0, secondary, chroma
elif h < 300:
    red, green, blue = secondary, 0, chroma
else:
    red, green, blue = chroma, 0, secondary
r = round((red + match) * 255)
g = round((green + match) * 255)
b = round((blue + match) * 255)

is_light_color(hex_code, threshold=LUMINANCE_LIGHT_THRESHOLD)

Takes a hex color and returns whether it's "light" based on perceived luminance. Uses the sRGB relative luminance formula (ITU-R BT.709 weights). Threshold ~0.179 is commonly used for WCAG contrast calculations.

Parameters:

Name Type Description Default
hex_code str

Hex color string to convert (e.g., "#FFFFFF" or "fff").

required
threshold float

Luminance threshold for "light" classification (default 0.179).

LUMINANCE_LIGHT_THRESHOLD

Returns:

Name Type Description
bool bool

True if it's "light" based on perceived luminance.

Raises:

Type Description
ValueError

If hex_code is not a valid hex color.

Example
from py_simple import is_light_color

is_light_color("#FFFFFF")  # -> True
is_light_color("#000000")  # -> False
hex_code = "#FFFFFF".lstrip("#")
if len(hex_code) == 3:
    hex_code = "".join(c * 2 for c in hex_code)
r, g, b = (int(hex_code[i:i + 2], 16) / 255 for i in (0, 2, 4))

def linearize(c):
    return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4

luminance = sum(w * linearize(v)
                for w, v in zip((0.2126, 0.7152, 0.0722), (r, g, b)))
is_light = luminance > 0.179  # -> True

is_valid_hex(hex_code)

Checks whether a string is a valid hex color code.

Supports 3-digit and 6-digit hex color formats, with or without a leading '#' prefix.

Parameters:

Name Type Description Default
hex_code str

Color string to check.

required

Returns:

Name Type Description
bool bool

True if hex_code is a valid hex color, False otherwise.

Example
from py_simple import is_valid_hex

is_valid_hex("#FFFFFF")  # -> True
is_valid_hex("fff")  # -> True
is_valid_hex("invalid")  # -> False
import re

hex_code = "#FFFFFF"
cleaned = hex_code.lstrip("#")
is_valid = len(cleaned) in (3, 6) and all(
    c in "0123456789abcdefABCDEF" for c in cleaned
)

random_hex_color()

Returns a random valid hex color string in uppercase #RRGGBB format.

Returns:

Name Type Description
str str

Uppercase hex color string (e.g., "#FFFFFF").

Example
from py_simple import random_hex_color

color = random_hex_color()  # -> e.g. "#A1B2C3"
import random

color = f"#{random.randint(0, 0xFFFFFF):06X}"

rgb_to_hex(r, g, b)

Converts (R, G, B) color values to a hex color string.

Parameters:

Name Type Description Default
r int

Red channel value (0 to 255).

required
g int

Green channel value (0 to 255).

required
b int

Blue channel value (0 to 255).

required

Returns:

Name Type Description
str str

Uppercase hex color string (e.g., "#FFFFFF").

Raises:

Type Description
ValueError

If any channel is outside the 0-255 range.

TypeError

If any channel is not an integer.

Example
from py_simple import rgb_to_hex

rgb_to_hex(255, 255, 255)  # -> "#FFFFFF"
rgb_to_hex(0, 128, 64)  # -> "#008040"
r, g, b = 255, 255, 255
hex_code = f"#{r:02X}{g:02X}{b:02X}"

rgb_to_hsl(r, g, b)

Converts (R, G, B) color values to an (H, S, L) tuple.

Hue is measured in degrees (0-360), while Saturation and Lightness are measured as percentages (0-100).

Parameters:

Name Type Description Default
r int

Red channel value (0 to 255).

required
g int

Green channel value (0 to 255).

required
b int

Blue channel value (0 to 255).

required

Returns:

Type Description
float

tuple[float, float, float]: HSL values as (h, s, l), where h is in

float

the 0-360 range and s/l are percentages in the 0-100 range.

Raises:

Type Description
ValueError

If any channel is outside the 0-255 range.

TypeError

If any channel is not an integer.

Example
from py_simple import rgb_to_hsl

rgb_to_hsl(255, 0, 0)  # -> (0.0, 100.0, 50.0)
rgb_to_hsl(18, 52, 86)  # -> (210.0, 65.38, 20.39)
r, g, b = 18, 52, 86
r, g, b = r / 255, g / 255, b / 255
color_max, color_min = max(r, g, b), min(r, g, b)
delta = color_max - color_min
lightness = (color_max + color_min) / 2
if delta == 0:
    hue = saturation = 0
else:
    saturation = delta / (1 - abs(2 * lightness - 1))
    if color_max == r:
        hue = (60 * ((g - b) / delta)) % 360
    elif color_max == g:
        hue = 60 * ((b - r) / delta) + 120
    else:
        hue = 60 * ((r - g) / delta) + 240