File size: 2,671 Bytes
68778bc
6c3e735
 
3e87437
6c3e735
3e87437
6c3e735
68778bc
 
3e87437
 
 
 
 
 
 
 
 
7213dbb
 
 
 
3e87437
 
 
 
 
 
 
 
 
 
 
68778bc
 
 
 
6c3e735
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import re
import random
from colorsys import hls_to_rgb
from typing import NamedTuple

import streamlit as st
import wcag_contrast_ratio as contrast


class ThemeColor(NamedTuple):
    primaryColor: str
    backgroundColor: str
    secondaryBackgroundColor: str
    textColor: str


@st.cache_resource
def get_config_theme_color():
    config_theme_primaryColor = st._config.get_option('theme.primaryColor')
    config_theme_backgroundColor = st._config.get_option('theme.backgroundColor')
    config_theme_secondaryBackgroundColor = st._config.get_option('theme.secondaryBackgroundColor')
    config_theme_textColor = st._config.get_option('theme.textColor')
    if config_theme_primaryColor and config_theme_backgroundColor and config_theme_secondaryBackgroundColor and config_theme_textColor:
        return ThemeColor(
            primaryColor=config_theme_primaryColor,
            backgroundColor=config_theme_backgroundColor,
            secondaryBackgroundColor=config_theme_secondaryBackgroundColor,
            textColor=config_theme_textColor,
        )

    return None


def parse_hex(rgb_hex_str: str) -> tuple[float, float, float]:
    if not re.match(r"^#[0-9a-fA-F]{6}$", rgb_hex_str):
        raise ValueError("Invalid hex color")
    return tuple(int(rgb_hex_str[i:i+2], 16) / 255 for i in (1, 3, 5))


def random_hls():
    h = random.random()
    l = random.random()
    s = random.random()

    # Make sure the color is light or dark enough.
    MAX_LIGHTNESS = 0.3
    if l < 0.5:
        # Make the color darker.
        l = l * (MAX_LIGHTNESS / 0.5)
    else:
        # Make the color lighter.
        l = 1 - l
        l = l * (MAX_LIGHTNESS / 0.5)
        l = 1 - l
    return (h, l, s)


def high_contrast_color(color):
    h, l, s = color
    l = 1 - l
    return (h, l, s)


def hls_to_hex(color):
    r, g, b = hls_to_rgb(*color)
    return "#{:02x}{:02x}{:02x}".format(round(r * 255), round(g * 255), round(b * 255))


def find_color_with_contrast(base_color, min_contrast_ratio, max_attempts):
    for _ in range(max_attempts):
        candidate_color = random_hls()
        if contrast.rgb(hls_to_rgb(*candidate_color), hls_to_rgb(*base_color)) > min_contrast_ratio:
            return candidate_color
    return high_contrast_color(base_color)


def generate_color_scheme():
    primary_color = random_hls()
    basic_background = high_contrast_color(primary_color)

    text_color = find_color_with_contrast(basic_background, 7, 100)
    secondary_background = find_color_with_contrast(primary_color, 7, 100)

    return hls_to_hex(primary_color), hls_to_hex(text_color), hls_to_hex(basic_background), hls_to_hex(secondary_background)