Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c394eaaf2 | |||
| d3125b707d | |||
| 52c5b6a484 | |||
| 71c0083194 | |||
| 3c9418f0c0 | |||
| a10453d752 | |||
| fb9a4088ed | |||
| a875817fd0 | |||
| 9c312943de | |||
| f61cef96d5 |
4
.gitignore
vendored
@@ -19,3 +19,7 @@ docs/_build/
|
||||
|
||||
data/
|
||||
archive/
|
||||
|
||||
notebooks/color_spaces_manyview.ipynb
|
||||
notebooks/oklch_srgb_spherical.ipynb
|
||||
notebooks/v1.4.0/
|
||||
|
||||
@@ -142,6 +142,8 @@ dark `tundra` variant with the soft harshness level.
|
||||
each of which is generated using the [Firefox `manifest.json`
|
||||
template](templates/apps/firefox/templates/none-dark.manifest.json).
|
||||
|
||||
Static [light][4] and [dark][5] are additionally available.
|
||||
|
||||

|
||||
|
||||
# Switching themes
|
||||
@@ -204,3 +206,5 @@ biome/harshness/mode. This can be done for any app config file.
|
||||
[1]: https://github.com/isa/TextMate-Themes/blob/master/monoindustrial.tmTheme
|
||||
[2]: https://addons.mozilla.org/en-US/firefox/collections/18495484/monobiome/
|
||||
[3]: https://github.com/ologio/symconf
|
||||
[4]: https://addons.mozilla.org/en-US/firefox/collections/18495484/monobiome-light/
|
||||
[5]: https://addons.mozilla.org/en-US/firefox/collections/18495484/monobiome-dark/
|
||||
|
||||
68
examples/class.py
Normal file
@@ -0,0 +1,68 @@
|
||||
class WLBPosteriorEstimator(PosteriorEstimatorTrainer):
|
||||
"""
|
||||
Weighted likelihood bootstrap (WLB) estimator.
|
||||
|
||||
Trains models to approximate draws from the *weight posterior* (under
|
||||
Jeffrey's prior).
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
assert not self.use_non_atomic_loss
|
||||
|
||||
def get_dataloaders(
|
||||
self,
|
||||
starting_round: int = 0,
|
||||
training_batch_size: int = 200,
|
||||
validation_fraction: float = 0.1,
|
||||
resume_training: bool = False,
|
||||
dataloader_kwargs: dict | None = None,
|
||||
) -> tuple[data.DataLoader, data.DataLoader]:
|
||||
"""
|
||||
Add logic for generating session-specific WLB weights.
|
||||
|
||||
This is probably the easiest place to stick some fixed weights on a
|
||||
point-wise basis for a given training run, and we load them later where
|
||||
we need them in the ``train()`` loop.
|
||||
"""
|
||||
theta, x, prior_masks = self.get_simulations(starting_round)
|
||||
|
||||
# generate session specific WLB weights to attach point-wise
|
||||
N = theta.shape[0]
|
||||
wlb_z = Exponential(1.0).sample((N,))
|
||||
wlb_w = (wlb_z / wlb_z.sum()) * N
|
||||
|
||||
dataset = data.TensorDataset(theta, x, prior_masks, wlb_w)
|
||||
|
||||
num_examples = theta.size(0)
|
||||
num_training_examples = int((1 - validation_fraction) * num_examples)
|
||||
num_validation_examples = num_examples - num_training_examples
|
||||
|
||||
if not resume_training:
|
||||
permuted_indices = torch.randperm(num_examples)
|
||||
self.train_indices, self.val_indices = (
|
||||
permuted_indices[:num_training_examples],
|
||||
permuted_indices[num_training_examples:],
|
||||
)
|
||||
|
||||
train_loader_kwargs = {
|
||||
"batch_size": min(training_batch_size, num_training_examples),
|
||||
"drop_last": True,
|
||||
"sampler": SubsetRandomSampler(self.train_indices.tolist()),
|
||||
}
|
||||
val_loader_kwargs = {
|
||||
"batch_size": min(training_batch_size, num_validation_examples),
|
||||
"shuffle": False,
|
||||
"drop_last": True,
|
||||
"sampler": SubsetRandomSampler(self.val_indices.tolist()),
|
||||
}
|
||||
if dataloader_kwargs is not None:
|
||||
train_loader_kwargs = dict(train_loader_kwargs, **dataloader_kwargs)
|
||||
val_loader_kwargs = dict(val_loader_kwargs, **dataloader_kwargs)
|
||||
|
||||
train_loader = data.DataLoader(dataset, **train_loader_kwargs)
|
||||
val_loader = data.DataLoader(dataset, **val_loader_kwargs)
|
||||
|
||||
return train_loader, val_loader
|
||||
|
||||
BIN
images/color_stats/chroma-curves-v130.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
images/render/v110-alpine-dark.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
images/render/v120-alpine-dark.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
images/render/v130-alpine-dark.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
3
monobiome/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from importlib.metadata import version
|
||||
|
||||
__version__ = version("monobiome")
|
||||
19
monobiome/__main__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from monobiome.cli import create_parser, configure_logging
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = create_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
# skim off log level to handle higher-level option
|
||||
if hasattr(args, "log_level") and args.log_level is not None:
|
||||
configure_logging(args.log_level)
|
||||
|
||||
if "func" in args:
|
||||
args.func(args)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
32
monobiome/cli/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import logging
|
||||
import argparse
|
||||
|
||||
from monobiome.cli import scheme, palette
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
def configure_logging(log_level: int) -> None:
|
||||
"""
|
||||
Configure logger's logging level.
|
||||
"""
|
||||
|
||||
logger.setLevel(log_level)
|
||||
|
||||
def create_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Accent modeling CLI",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
type=int,
|
||||
metavar="int",
|
||||
choices=[10, 20, 30, 40, 50],
|
||||
help="Log level: 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL",
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(help="subcommand help")
|
||||
|
||||
palette.register_parser(subparsers)
|
||||
scheme.register_parser(subparsers)
|
||||
|
||||
return parser
|
||||
51
monobiome/cli/palette.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from monobiome.util import _SubparserType
|
||||
from monobiome.palette import generate_palette
|
||||
|
||||
|
||||
def register_parser(subparsers: _SubparserType) -> None:
|
||||
parser = subparsers.add_parser(
|
||||
"palette",
|
||||
help="generate primary palette"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--notation",
|
||||
type=str,
|
||||
default="hex",
|
||||
choices=["hex", "oklch"],
|
||||
help="Color notation to export (either hex or oklch)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--format",
|
||||
type=str,
|
||||
default="toml",
|
||||
choices=["json", "toml"],
|
||||
help="Format of palette file (either JSON or TOML)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=str,
|
||||
help="Output file to write palette content",
|
||||
)
|
||||
|
||||
parser.set_defaults(func=handle_palette)
|
||||
|
||||
|
||||
def handle_palette(args: argparse.Namespace) -> None:
|
||||
notation = args.notation
|
||||
file_format = args.format
|
||||
output = args.output
|
||||
|
||||
palette_text = generate_palette(notation, file_format)
|
||||
|
||||
if output is None:
|
||||
print(palette_text)
|
||||
else:
|
||||
with Path(output).open("w") as f:
|
||||
f.write(palette_text)
|
||||
155
monobiome/cli/scheme.py
Normal file
@@ -0,0 +1,155 @@
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from monobiome.util import _SubparserType
|
||||
from monobiome.scheme import generate_scheme
|
||||
from monobiome.constants import monotone_h_map
|
||||
|
||||
|
||||
def register_parser(subparsers: _SubparserType) -> None:
|
||||
parser = subparsers.add_parser(
|
||||
"scheme",
|
||||
help="create scheme variants"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"mode",
|
||||
type=str,
|
||||
choices=["dark", "light"],
|
||||
help="Scheme mode (light or dark)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"biome",
|
||||
type=str,
|
||||
choices=list(monotone_h_map.keys()),
|
||||
help="Biome setting for scheme."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--metric",
|
||||
type=str,
|
||||
default="oklch",
|
||||
choices=["wcag", "oklch", "lightness"],
|
||||
help="Metric to use for measuring swatch distances."
|
||||
)
|
||||
|
||||
# e.g., wcag=4.5; oklch=0.40; lightness=40
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--distance",
|
||||
type=float,
|
||||
default=0.40,
|
||||
help="Distance threshold for specified metric",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=str,
|
||||
help="Output file to write scheme content",
|
||||
)
|
||||
|
||||
# these params remain rooted in lightness; no need to accommodate metric
|
||||
# given these are monotone adjustments. You *could* consider rooting these
|
||||
# in metric units, but along monotones, distance=lightness and WCAG isn't a
|
||||
# particularly good measure of perceptual distinction, so we'd prefer the
|
||||
# former.
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--l-base",
|
||||
type=int,
|
||||
default=20,
|
||||
help="Minimum lightness level (default: 20)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--l-step",
|
||||
type=int,
|
||||
default=5,
|
||||
help="Lightness step size (default: 5)",
|
||||
)
|
||||
|
||||
# gaps
|
||||
parser.add_argument(
|
||||
"--fg-gap",
|
||||
type=int,
|
||||
default=50,
|
||||
help="Foreground lightness gap (default: 50)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--grey-gap",
|
||||
type=int,
|
||||
default=30,
|
||||
help="Grey lightness gap (default: 30)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--term-fg-gap",
|
||||
type=int,
|
||||
default=65,
|
||||
help="Terminal foreground lightness gap (default: 60)",
|
||||
)
|
||||
|
||||
parser.set_defaults(func=handle_scheme)
|
||||
|
||||
|
||||
def handle_scheme(args: argparse.Namespace) -> None:
|
||||
output = args.output
|
||||
|
||||
mode = args.mode
|
||||
biome = args.biome
|
||||
metric = args.metric
|
||||
distance = args.distance
|
||||
l_base = args.l_base
|
||||
l_step = args.l_step
|
||||
fg_gap = args.fg_gap
|
||||
grey_gap = args.grey_gap
|
||||
term_fg_gap = args.term_fg_gap
|
||||
|
||||
full_color_map = {
|
||||
"red": "red",
|
||||
"orange": "orange",
|
||||
"yellow": "yellow",
|
||||
"green": "green",
|
||||
"cyan": "cyan",
|
||||
"blue": "blue",
|
||||
"violet": "violet",
|
||||
"magenta": "orange",
|
||||
}
|
||||
term_color_map = {
|
||||
"red": "red",
|
||||
"yellow": "yellow",
|
||||
"green": "green",
|
||||
"cyan": "blue",
|
||||
"blue": "blue",
|
||||
"magenta": "orange",
|
||||
}
|
||||
vim_color_map = {
|
||||
"red": "red",
|
||||
"orange": "orange",
|
||||
"yellow": "yellow",
|
||||
"green": "green",
|
||||
"cyan": "green",
|
||||
"blue": "blue",
|
||||
"violet": "blue",
|
||||
"magenta": "red",
|
||||
}
|
||||
# vim_color_map = full_color_map
|
||||
|
||||
scheme_text = generate_scheme(
|
||||
mode,
|
||||
biome,
|
||||
metric,
|
||||
distance,
|
||||
l_base,
|
||||
l_step,
|
||||
fg_gap,
|
||||
grey_gap,
|
||||
term_fg_gap,
|
||||
full_color_map,
|
||||
term_color_map,
|
||||
vim_color_map,
|
||||
)
|
||||
|
||||
if output is None:
|
||||
print(scheme_text)
|
||||
else:
|
||||
with Path(output).open("w") as f:
|
||||
f.write(scheme_text)
|
||||
123
monobiome/constants.py
Normal file
@@ -0,0 +1,123 @@
|
||||
import tomllib
|
||||
from importlib.resources import files
|
||||
|
||||
import numpy as np
|
||||
|
||||
from monobiome.curve import (
|
||||
l_maxC_h,
|
||||
bezier_y_at_x,
|
||||
)
|
||||
|
||||
parameters_file = files("monobiome.data") / "parameters.toml"
|
||||
parameters = tomllib.load(parameters_file.open("rb"))
|
||||
|
||||
L_min: int = parameters.get("L_min", 10)
|
||||
L_max: int = parameters.get("L_max", 98)
|
||||
L_step: int = parameters.get("L_step", 5)
|
||||
|
||||
L_points: list[int] = list(range(L_min, L_max+1))
|
||||
|
||||
# L-space just affects accuracy of chroma max
|
||||
L_space = np.arange(0, 100 + L_step, L_step)
|
||||
|
||||
monotone_C_map = parameters.get("monotone_C_map", {})
|
||||
h_weights = parameters.get("h_weights", {})
|
||||
h_L_offsets = parameters.get("h_L_offsets", {})
|
||||
h_C_offsets = parameters.get("h_C_offsets", {})
|
||||
monotone_h_map = parameters.get("monotone_h_map", {})
|
||||
accent_h_map = parameters.get("accent_h_map", {})
|
||||
h_map = {**monotone_h_map, **accent_h_map}
|
||||
|
||||
"""
|
||||
Compute chroma maxima at provided lightness levels across hues.
|
||||
|
||||
A map with max chroma values for each hue across lightness space
|
||||
|
||||
{
|
||||
"red": [ Cmax@L=10, Cmax@L=11, Cmax@L=12, ... ],
|
||||
"orange": [ Cmax@L=10, Cmax@L=11, Cmax@L=12, ... ],
|
||||
...
|
||||
}
|
||||
"""
|
||||
Lspace_Cmax_Hmap = {
|
||||
h_str: [l_maxC_h(_L, _h) for _L in L_space]
|
||||
for h_str, _h in h_map.items()
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
Set QBR curves, *unbounded* chroma curves for all hues
|
||||
|
||||
1. Raw bezier chroma values for each hue across the lightness space
|
||||
|
||||
Lpoints_Cqbr_Hmap = {
|
||||
"red": [ Bezier@L=10, Bezier@L=11, Bezier@L=12, ... ],
|
||||
...
|
||||
}
|
||||
|
||||
2. Three bezier control points for each hue's chroma curve
|
||||
|
||||
QBR_ctrl_Hmap = {
|
||||
"red": np.array([
|
||||
[ x1, y1 ],
|
||||
[ x2, y2 ],
|
||||
[ x3, y3 ]
|
||||
]),
|
||||
...
|
||||
}
|
||||
"""
|
||||
Lpoints_Cqbr_Hmap = {}
|
||||
QBR_ctrl_Hmap = {}
|
||||
|
||||
for h_str, _h in monotone_h_map.items():
|
||||
Lpoints_Cqbr_Hmap[h_str] = np.array(
|
||||
[monotone_C_map[h_str]]*len(L_points)
|
||||
)
|
||||
|
||||
for h_str, _h in accent_h_map.items():
|
||||
Lspace_Cmax = Lspace_Cmax_Hmap[h_str]
|
||||
|
||||
# get L value of max chroma; will be a bezier control
|
||||
L_Cmax_idx = np.argmax(Lspace_Cmax)
|
||||
L_Cmax = L_space[L_Cmax_idx]
|
||||
|
||||
# offset control point by any preset x-shift
|
||||
L_Cmax += h_L_offsets[h_str]
|
||||
|
||||
# and get max C at the L offset
|
||||
Cmax = l_maxC_h(L_Cmax, _h)
|
||||
|
||||
# set 3 control points; shift by any global linear offest
|
||||
C_offset = h_C_offsets.get(h_str, 0)
|
||||
|
||||
p_0 = np.array([0, 0])
|
||||
p_Cmax = np.array([L_Cmax, Cmax + C_offset])
|
||||
p_100 = np.array([100, 0])
|
||||
|
||||
B_L_points = bezier_y_at_x(
|
||||
p_0, p_Cmax, p_100,
|
||||
h_weights.get(h_str, 1),
|
||||
L_points
|
||||
)
|
||||
Lpoints_Cqbr_Hmap[h_str] = B_L_points
|
||||
QBR_ctrl_Hmap[h_str] = np.vstack([p_0, p_Cmax, p_100])
|
||||
|
||||
|
||||
"""
|
||||
Bezier chroma values, but bounded to attainable gamut colors (bezier fit
|
||||
can produce invalid chroma values)
|
||||
|
||||
h_L_points_Cstar = {
|
||||
"red": [ bounded-bezier@L=10, bounded-bezier@L=11, ... ],
|
||||
...
|
||||
}
|
||||
"""
|
||||
Lpoints_Cstar_Hmap = {}
|
||||
|
||||
for h_str, L_points_C in Lpoints_Cqbr_Hmap.items():
|
||||
_h = h_map[h_str]
|
||||
|
||||
Lpoints_Cstar_Hmap[h_str] = [
|
||||
max(0, min(_C, l_maxC_h(_L, _h)))
|
||||
for _L, _C in zip(L_points, L_points_C, strict=True)
|
||||
]
|
||||
77
monobiome/curve.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from functools import cache
|
||||
|
||||
import numpy as np
|
||||
from coloraide import Color
|
||||
|
||||
|
||||
def quad_bezier_rational(
|
||||
P0: float,
|
||||
P1: float,
|
||||
P2: float,
|
||||
w: float,
|
||||
t: np.array,
|
||||
) -> np.array:
|
||||
"""
|
||||
Compute the point values of a quadratic rational Bezier curve.
|
||||
|
||||
Uses `P0`, `P1`, and `P2` as the three control points of the curve. `w`
|
||||
controls the weight toward the middle control point ("sharpness" of the
|
||||
curve"), and `t` is the number of sample points used along the curve.
|
||||
"""
|
||||
|
||||
t = np.asarray(t)[:, None]
|
||||
num = (1-t)**2*P0 + 2*w*(1-t)*t*P1 + t**2*P2
|
||||
den = (1-t)**2 + 2*w*(1-t)*t + t**2
|
||||
|
||||
return num / den
|
||||
|
||||
def bezier_y_at_x(
|
||||
P0: float,
|
||||
P1: float,
|
||||
P2: float,
|
||||
w: float,
|
||||
x: float,
|
||||
n: int = 400,
|
||||
) -> np.array:
|
||||
"""
|
||||
For the provided QBR parameters, provide the curve value at the given
|
||||
input.
|
||||
"""
|
||||
|
||||
t = np.linspace(0, 1, n)
|
||||
B = quad_bezier_rational(P0, P1, P2, w, t)
|
||||
x_vals, y_vals = B[:, 0], B[:, 1]
|
||||
|
||||
return np.interp(x, x_vals, y_vals)
|
||||
|
||||
@cache
|
||||
def l_maxC_h(
|
||||
_l: float,
|
||||
_h: float,
|
||||
space: str = 'srgb',
|
||||
eps: float = 1e-6,
|
||||
tol: float = 1e-9
|
||||
) -> float:
|
||||
"""
|
||||
Binary search for max attainable OKLCH chroma at fixed lightness and hue.
|
||||
|
||||
Parameters:
|
||||
_l: lightness
|
||||
_h: hue
|
||||
|
||||
Returns:
|
||||
Max in-gamut chroma at provided lightness and hue
|
||||
"""
|
||||
|
||||
def chroma_in_gamut(_c: float) -> bool:
|
||||
color = Color('oklch', [_l/100, _c, _h])
|
||||
return color.convert(space).in_gamut(tolerance=tol)
|
||||
|
||||
lo, hi = 0.0, 0.1
|
||||
while chroma_in_gamut(hi):
|
||||
hi *= 2
|
||||
while hi - lo > eps:
|
||||
m = (lo + hi) / 2
|
||||
lo, hi = (m, hi) if chroma_in_gamut(m) else (lo, m)
|
||||
|
||||
return lo
|
||||
54
monobiome/palette.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import json
|
||||
from functools import cache
|
||||
from importlib.metadata import version
|
||||
|
||||
from coloraide import Color
|
||||
|
||||
from monobiome.constants import (
|
||||
h_map,
|
||||
L_points,
|
||||
Lpoints_Cstar_Hmap,
|
||||
)
|
||||
|
||||
|
||||
@cache
|
||||
def compute_hlc_map(notation: str) -> dict[str, dict[int, str]]:
|
||||
hlc_map = {}
|
||||
|
||||
for h_str, Lpoints_Cstar in Lpoints_Cstar_Hmap.items():
|
||||
_h = h_map[h_str]
|
||||
hlc_map[h_str] = {}
|
||||
|
||||
for _l, _c in zip(L_points, Lpoints_Cstar, strict=True):
|
||||
oklch = Color('oklch', [_l/100, _c, _h])
|
||||
|
||||
if notation == "hex":
|
||||
srgb = oklch.convert('srgb')
|
||||
c_str = srgb.to_string(hex=True)
|
||||
elif notation == "oklch":
|
||||
ol, oc, oh = oklch.convert('oklch').coords()
|
||||
c_str = f"oklch({ol*100:.1f}% {oc:.4f} {oh:.1f})"
|
||||
|
||||
hlc_map[h_str][_l] = c_str
|
||||
|
||||
return hlc_map
|
||||
|
||||
def generate_palette(
|
||||
notation: str,
|
||||
file_format: str,
|
||||
) -> str:
|
||||
mb_version = version("monobiome")
|
||||
hlc_map = compute_hlc_map(notation)
|
||||
|
||||
if file_format == "json":
|
||||
hlc_map["version"] = mb_version
|
||||
return json.dumps(hlc_map, indent=4)
|
||||
else:
|
||||
toml_lines = [f"version = {mb_version}", ""]
|
||||
for _h, _lc_map in hlc_map.items():
|
||||
toml_lines.append(f"[{_h}]")
|
||||
for _l, _c in _lc_map.items():
|
||||
toml_lines.append(f'l{_l} = "{_c}"')
|
||||
toml_lines.append("")
|
||||
|
||||
return "\n".join(toml_lines)
|
||||
176
monobiome/plotting.py
Normal file
@@ -0,0 +1,176 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from monobiome.constants import (
|
||||
h_map,
|
||||
L_space,
|
||||
L_points,
|
||||
accent_h_map,
|
||||
monotone_h_map,
|
||||
Lspace_Cmax_Hmap,
|
||||
Lpoints_Cstar_Hmap,
|
||||
)
|
||||
|
||||
|
||||
def plot_hue_chroma_bounds() -> None:
|
||||
name_h_map = {}
|
||||
ax_h_map = {}
|
||||
fig, axes = plt.subplots(
|
||||
len(monotone_h_map),
|
||||
1,
|
||||
sharex=True,
|
||||
sharey=True,
|
||||
figsize=(4, 10)
|
||||
)
|
||||
|
||||
for i, h_str in enumerate(Lpoints_Cstar_Hmap):
|
||||
_h = h_map[h_str]
|
||||
|
||||
l_space_Cmax = Lspace_Cmax_Hmap[h_str]
|
||||
l_points_Cstar = Lpoints_Cstar_Hmap[h_str]
|
||||
|
||||
if _h not in ax_h_map:
|
||||
ax_h_map[_h] = axes[i]
|
||||
ax = ax_h_map[_h]
|
||||
|
||||
if _h not in name_h_map:
|
||||
name_h_map[_h] = []
|
||||
name_h_map[_h].append(h_str)
|
||||
|
||||
# plot Cmax and Cstar
|
||||
ax.plot(L_space, l_space_Cmax, c="g", alpha=0.3, label="Cmax")
|
||||
|
||||
cstar_label = f"{'accent' if h_str in accent_h_map else 'monotone'} C*"
|
||||
ax.plot(L_points, l_points_Cstar, alpha=0.7, label=cstar_label)
|
||||
|
||||
ax.title.set_text(f"Hue [${_h}$] - {'|'.join(name_h_map[_h])}")
|
||||
|
||||
axes[-1].set_xlabel("Lightness (%)")
|
||||
axes[-1].set_xticks([L_points[0], L_points[-1]])
|
||||
|
||||
fig.tight_layout()
|
||||
fig.subplots_adjust(top=0.9)
|
||||
|
||||
handles, labels = axes[-1].get_legend_handles_labels()
|
||||
unique = dict(zip(labels, handles))
|
||||
fig.legend(unique.values(), unique.keys(), loc='lower center', bbox_to_anchor=(0.5, -0.06), ncol=3)
|
||||
|
||||
plt.suptitle("$C^*$ curves for hue groups")
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_hue_chroma_star() -> None:
|
||||
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
|
||||
|
||||
# uncomment to preview 5 core term colors
|
||||
colors = accent_h_map.keys()
|
||||
#colors = set(["red", "orange", "yellow", "green", "blue"])
|
||||
|
||||
for h_str in Lpoints_Cstar_Hmap:
|
||||
if h_str not in accent_h_map or h_str not in colors:
|
||||
continue
|
||||
ax.fill_between(
|
||||
L_points,
|
||||
Lpoints_Cstar_Hmap[h_str],
|
||||
alpha=0.2,
|
||||
color='grey',
|
||||
label=h_str
|
||||
)
|
||||
|
||||
x, y = L_points, Lpoints_Cstar_Hmap[h_str]
|
||||
n = int(0.45*len(x))
|
||||
ax.text(x[n], y[n]-0.01, h_str, rotation=10, va='center', ha='left')
|
||||
|
||||
ax.set_xlabel("Lightness (%)")
|
||||
ax.set_xticks([L_points[0], 45, 50, 55, 60, 65, 70, L_points[-1]])
|
||||
plt.suptitle("$C^*$ curves (v1.4.0)")
|
||||
fig.show()
|
||||
|
||||
|
||||
def palette_image(palette, cell_size=40, keys=None):
|
||||
if keys is None:
|
||||
names = list(palette.keys())
|
||||
else:
|
||||
names = keys
|
||||
|
||||
row_count = len(names)
|
||||
col_counts = [len(palette[n]) for n in names]
|
||||
max_cols = max(col_counts)
|
||||
|
||||
h = row_count * cell_size
|
||||
w = max_cols * cell_size
|
||||
img = np.ones((h, w, 3), float)
|
||||
|
||||
lightness_keys_per_row = []
|
||||
|
||||
for r, name in enumerate(names):
|
||||
shades = palette[name]
|
||||
keys = sorted(shades.keys())
|
||||
lightness_keys_per_row.append(keys)
|
||||
for c, k in enumerate(keys):
|
||||
col = Color(shades[k]).convert("srgb").fit(method="clip")
|
||||
rgb = [col["r"], col["g"], col["b"]]
|
||||
r0, r1 = r * cell_size, (r + 1) * cell_size
|
||||
c0, c1 = c * cell_size, (c + 1) * cell_size
|
||||
img[r0:r1, c0:c1, :] = rgb
|
||||
|
||||
return img, names, lightness_keys_per_row, cell_size, max_cols
|
||||
|
||||
|
||||
def show_palette(palette, cell_size=40, keys=None):
|
||||
img, names, keys, cell_size, max_cols = palette_image(palette, cell_size, keys=keys)
|
||||
|
||||
fig_w = img.shape[1] / 100
|
||||
fig_h = img.shape[0] / 100
|
||||
fig, ax = plt.subplots(figsize=(fig_w, fig_h))
|
||||
|
||||
ax.imshow(img, interpolation="none", origin="upper")
|
||||
ax.set_xticks([])
|
||||
|
||||
ytick_pos = [(i + 0.5) * cell_size for i in range(len(names))]
|
||||
ax.set_yticks(ytick_pos)
|
||||
ax.set_yticklabels(names)
|
||||
|
||||
ax.set_ylim(img.shape[0], 0) # ensures rows render correctly without half-cells
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from monobiome.constants import OKLCH_hL_dict
|
||||
|
||||
keys = [
|
||||
"alpine",
|
||||
"badlands",
|
||||
"chaparral",
|
||||
"savanna",
|
||||
"grassland",
|
||||
"reef",
|
||||
"tundra",
|
||||
"heathland",
|
||||
"moorland",
|
||||
"orange",
|
||||
"yellow",
|
||||
"green",
|
||||
"cyan",
|
||||
"blue",
|
||||
"violet",
|
||||
"magenta",
|
||||
"red",
|
||||
]
|
||||
term_keys = [
|
||||
"alpine",
|
||||
"badlands",
|
||||
"chaparral",
|
||||
"savanna",
|
||||
"grassland",
|
||||
"tundra",
|
||||
"red",
|
||||
"orange",
|
||||
"yellow",
|
||||
"green",
|
||||
"blue",
|
||||
]
|
||||
|
||||
show_palette(OKLCH_hL_dict, cell_size=25, keys=keys)
|
||||
# show_palette(OKLCH_hL_dict, cell_size=1, keys=term_keys)
|
||||
254
monobiome/scheme.py
Normal file
@@ -0,0 +1,254 @@
|
||||
from functools import cache
|
||||
from collections.abc import Callable
|
||||
|
||||
from coloraide import Color
|
||||
|
||||
from monobiome.util import oklch_distance
|
||||
from monobiome.palette import compute_hlc_map
|
||||
from monobiome.constants import (
|
||||
accent_h_map,
|
||||
monotone_h_map,
|
||||
)
|
||||
|
||||
|
||||
@cache
|
||||
def compute_dma_map(
|
||||
dT: float,
|
||||
metric: Callable | None = None
|
||||
) -> dict[str, dict]:
|
||||
"""
|
||||
For threshold `dT`, compute the nearest accent shades that exceed that
|
||||
threshold for every monotone shade.
|
||||
|
||||
Returns: map of minimum constraint satisfying accent colors for monotone
|
||||
spectra
|
||||
|
||||
{
|
||||
"alpine": {
|
||||
"oklch( ... )": {
|
||||
"red": *nearest oklch >= dT from M base*,
|
||||
...
|
||||
},
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
"""
|
||||
|
||||
if metric is None:
|
||||
metric = oklch_distance
|
||||
|
||||
oklch_hlc_map = compute_hlc_map("oklch")
|
||||
oklch_color_map = {
|
||||
c_name: [Color(c_str) for c_str in c_str_dict.values()]
|
||||
for c_name, c_str_dict in oklch_hlc_map.items()
|
||||
}
|
||||
|
||||
dT_mL_acol_map = {}
|
||||
for m_name in monotone_h_map:
|
||||
mL_acol_map = {}
|
||||
m_colors = oklch_color_map[m_name]
|
||||
|
||||
for m_color in m_colors:
|
||||
acol_min_map = {}
|
||||
|
||||
for a_name in accent_h_map:
|
||||
a_colors = oklch_color_map[a_name]
|
||||
oklch_dists = filter(
|
||||
lambda d: (d[1] - dT) >= 0,
|
||||
[
|
||||
(ac, metric(m_color, ac))
|
||||
for ac in a_colors
|
||||
]
|
||||
)
|
||||
oklch_dists = list(oklch_dists)
|
||||
if oklch_dists:
|
||||
min_a_color = min(oklch_dists, key=lambda t: t[1])[0]
|
||||
acol_min_map[a_name] = min_a_color
|
||||
|
||||
# make sure the current monotone level has *all* accents; o/w
|
||||
# ignore
|
||||
if len(acol_min_map) < len(accent_h_map):
|
||||
continue
|
||||
|
||||
mL = m_color.coords()[0]
|
||||
mL_acol_map[int(mL*100)] = acol_min_map
|
||||
dT_mL_acol_map[m_name] = mL_acol_map
|
||||
|
||||
return dT_mL_acol_map
|
||||
|
||||
def generate_scheme_groups(
|
||||
mode: str,
|
||||
biome: str,
|
||||
metric: str,
|
||||
distance: float,
|
||||
l_base: int,
|
||||
l_step: int,
|
||||
fg_gap: int,
|
||||
grey_gap: int,
|
||||
term_fg_gap: int,
|
||||
accent_color_map: dict[str, str],
|
||||
) -> tuple[dict[str, str], ...]:
|
||||
"""
|
||||
Parameters:
|
||||
mode: one of ["dark", "light"]
|
||||
biome: biome setting
|
||||
metric: one of ["wcag", "oklch", "lightness"]
|
||||
"""
|
||||
|
||||
metric_map = {
|
||||
"wcag": lambda mc,ac: ac.contrast(mc, method='wcag21'),
|
||||
"oklch": oklch_distance,
|
||||
"lightness": lambda mc,ac: abs(mc.coords()[0]-ac.coords()[0])*100,
|
||||
}
|
||||
|
||||
metric_func = metric_map[metric]
|
||||
dT_mL_acol_map = compute_dma_map(distance, metric=metric_func)
|
||||
Lma_map = {
|
||||
m_name: mL_acol_dict[l_base]
|
||||
for m_name, mL_acol_dict in dT_mL_acol_map.items()
|
||||
if l_base in mL_acol_dict
|
||||
}
|
||||
|
||||
# the `mL_acol_dict` only includes lightnesses where all accent colors were
|
||||
# within threshold. Coverage here will be partial if, at the `mL`, there is
|
||||
# some monotone base that doesn't have all accents within threshold. This
|
||||
# can happen at the edge, e.g., alpine@L15 has all accents w/in the
|
||||
# distance, but the red accent was too far under tundra@L15, so there's no
|
||||
# entry. This particular case is fairly rare; it's more likely that *all*
|
||||
# monotones are undefined. Either way, both such cases lead to partial
|
||||
# scheme coverage.
|
||||
if len(Lma_map) < len(monotone_h_map):
|
||||
print(f"Warning: partial scheme coverage for {l_base=}@{distance=}")
|
||||
if biome not in Lma_map:
|
||||
print(f"Biome {biome} unable to meet {metric} constraints")
|
||||
accent_colors = Lma_map.get(biome, {})
|
||||
|
||||
meta_pairs = [
|
||||
("mode", mode),
|
||||
("biome", biome),
|
||||
("metric", metric),
|
||||
("distance", distance),
|
||||
("l_base", l_base),
|
||||
("l_step", l_step),
|
||||
("fg_gap", fg_gap),
|
||||
("grey_gap", grey_gap),
|
||||
("term_fg_gap", term_fg_gap),
|
||||
]
|
||||
|
||||
# note how selection_bg steps up by `l_step`, selection_fg steps down by
|
||||
# `l_step` (from their respective bases)
|
||||
term_pairs = [
|
||||
("background", f"f{{{{{biome}.l{l_base}}}}}"),
|
||||
("selection_bg", f"f{{{{{biome}.l{l_base+l_step}}}}}"),
|
||||
("selection_fg", f"f{{{{{biome}.l{l_base+term_fg_gap-l_step}}}}}"),
|
||||
("foreground", f"f{{{{{biome}.l{l_base+term_fg_gap}}}}}"),
|
||||
("cursor", f"f{{{{{biome}.l{l_base+term_fg_gap-l_step}}}}}"),
|
||||
("cursor_text", f"f{{{{{biome}.l{l_base+l_step}}}}}"),
|
||||
]
|
||||
|
||||
monotone_pairs = []
|
||||
monotone_pairs += [
|
||||
(f"bg{i}", f"f{{{{{biome}.l{l_base+i*l_step}}}}}")
|
||||
for i in range(4)
|
||||
]
|
||||
monotone_pairs += [
|
||||
(f"fg{3-i}", f"f{{{{{biome}.l{fg_gap+l_base+i*l_step}}}}}")
|
||||
for i in range(4)
|
||||
]
|
||||
|
||||
accent_pairs = [
|
||||
("black", f"f{{{{{biome}.l{l_base}}}}}"),
|
||||
("grey", f"f{{{{{biome}.l{l_base+grey_gap}}}}}"),
|
||||
("white", f"f{{{{{biome}.l{l_base+term_fg_gap-2*l_step}}}}}"),
|
||||
]
|
||||
for color_name, mb_accent in accent_color_map.items():
|
||||
aL = int(100*accent_colors[mb_accent].coords()[0])
|
||||
accent_pairs.append(
|
||||
(
|
||||
color_name,
|
||||
f"f{{{{{mb_accent}.l{aL}}}}}"
|
||||
)
|
||||
)
|
||||
|
||||
return meta_pairs, term_pairs, monotone_pairs, accent_pairs
|
||||
|
||||
def generate_scheme(
|
||||
mode: str,
|
||||
biome: str,
|
||||
metric: str,
|
||||
distance: float,
|
||||
l_base: int,
|
||||
l_step: int,
|
||||
fg_gap: int,
|
||||
grey_gap: int,
|
||||
term_fg_gap: int,
|
||||
full_color_map: dict[str, str],
|
||||
term_color_map: dict[str, str],
|
||||
vim_color_map: dict[str, str],
|
||||
) -> str:
|
||||
l_sys = l_base
|
||||
l_app = l_base + l_step
|
||||
|
||||
term_bright_offset = 10
|
||||
|
||||
# negate gaps if mode is light
|
||||
if mode == "light":
|
||||
l_step *= -1
|
||||
fg_gap *= -1
|
||||
grey_gap *= -1
|
||||
term_fg_gap *= -1
|
||||
term_bright_offset *= -1
|
||||
|
||||
meta, _, mt, ac = generate_scheme_groups(
|
||||
mode, biome, metric, distance,
|
||||
l_sys, l_step,
|
||||
fg_gap, grey_gap, term_fg_gap,
|
||||
full_color_map
|
||||
)
|
||||
|
||||
_, term, _, term_norm_ac = generate_scheme_groups(
|
||||
mode, biome, metric, distance,
|
||||
l_app, l_step,
|
||||
fg_gap, grey_gap, term_fg_gap,
|
||||
term_color_map
|
||||
)
|
||||
_, _, _, term_bright_ac = generate_scheme_groups(
|
||||
mode, biome, metric, distance,
|
||||
l_app + term_bright_offset, l_step,
|
||||
fg_gap, grey_gap, term_fg_gap,
|
||||
term_color_map
|
||||
)
|
||||
|
||||
_, _, vim_mt, vim_ac = generate_scheme_groups(
|
||||
mode, biome, metric, distance,
|
||||
l_app, l_step,
|
||||
fg_gap, grey_gap, term_fg_gap,
|
||||
vim_color_map
|
||||
)
|
||||
|
||||
def pair_strings(pair_list: list[tuple[str, str]]) -> list[str]:
|
||||
return [
|
||||
f"{lhs:<12} = \"{rhs}\""
|
||||
for lhs, rhs in pair_list
|
||||
]
|
||||
|
||||
scheme_pairs = []
|
||||
scheme_pairs += pair_strings(meta)
|
||||
scheme_pairs += pair_strings(mt)
|
||||
scheme_pairs += pair_strings(ac)
|
||||
|
||||
scheme_pairs += ["", "[term]"]
|
||||
scheme_pairs += pair_strings(term)
|
||||
|
||||
scheme_pairs += ["", "[term.normal]"]
|
||||
scheme_pairs += pair_strings(term_norm_ac)
|
||||
|
||||
scheme_pairs += ["", "[term.bright]"]
|
||||
scheme_pairs += pair_strings(term_bright_ac)
|
||||
|
||||
scheme_pairs += ["", "[vim]"]
|
||||
scheme_pairs += pair_strings(vim_mt)
|
||||
scheme_pairs += pair_strings(vim_ac)
|
||||
|
||||
return "\n".join(scheme_pairs)
|
||||
35
monobiome/util.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import math
|
||||
from types import GenericAlias
|
||||
from argparse import ArgumentParser, _SubParsersAction
|
||||
|
||||
from coloraide import Color
|
||||
|
||||
_SubParsersAction.__class_getitem__ = classmethod(GenericAlias)
|
||||
_SubparserType = _SubParsersAction[ArgumentParser]
|
||||
|
||||
def oklch_distance(xc: Color, yc: Color) -> float:
|
||||
"""
|
||||
Compute the distance between two colors in OKLCH space.
|
||||
|
||||
Note: `xc` and `yc` are presumed to be OKLCH colors already, such that
|
||||
`.coords()` yields an `(l, c, h)` triple directly rather than first
|
||||
requiring conversion. When we can make this assumption, we save roughly an
|
||||
order of magnitude in runtime.
|
||||
|
||||
1. `xc.distance(yc, space="oklch")`: 500k evals takes ~2s
|
||||
2. This method: 500k evals takes ~0.2s
|
||||
"""
|
||||
|
||||
l1, c1, h1 = xc.coords()
|
||||
l2, c2, h2 = yc.coords()
|
||||
|
||||
rad1 = h1 / 180 * math.pi
|
||||
rad2 = h2 / 180 * math.pi
|
||||
x1, y1 = c1 * math.cos(rad1), c1 * math.sin(rad1)
|
||||
x2, y2 = c2 * math.cos(rad2), c2 * math.sin(rad2)
|
||||
|
||||
dx = x1 - x2
|
||||
dy = y1 - y2
|
||||
dz = l1 - l2
|
||||
|
||||
return (dx**2 + dy**2 + dz**2)**0.5
|
||||
BIN
notebooks/figures/mb_contrast_r50.gif
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
notebooks/figures/mb_sphere.gif
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
notebooks/figures/mb_trajectories.gif
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
@@ -10,6 +10,7 @@ import math
|
||||
import plotly.io as io
|
||||
import os
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
@@ -1016,6 +1017,440 @@ def main():
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def plot_sphere_in_oklch(
|
||||
fig,
|
||||
center,
|
||||
radius,
|
||||
gmap,
|
||||
filters=(),
|
||||
resolution=24,
|
||||
opacity=0.4,
|
||||
edges=False,
|
||||
faces=False,
|
||||
ecolor=None,
|
||||
fcolor=None,
|
||||
equal_metric=False # <- new flag, see below
|
||||
):
|
||||
space = 'oklch'
|
||||
target = Color.CS_MAP[space]
|
||||
flags = {
|
||||
'is_cyl': target.is_polar(),
|
||||
'is_labish': isinstance(target, Labish),
|
||||
'is_lchish': isinstance(target, LChish),
|
||||
'is_hslish': isinstance(target, HSLish),
|
||||
'is_hwbish': isinstance(target, HWBish),
|
||||
'is_hsvish': isinstance(target, HSVish)
|
||||
}
|
||||
|
||||
# Center in OKLab (Euclidean space behind OKLCH)
|
||||
c_center = Color(center).convert('oklab', in_place=True).normalize(nans=False)
|
||||
L0, a0, b0 = c_center[0], c_center[1], c_center[2]
|
||||
|
||||
# Parametric sphere in (L, a, b)
|
||||
thetas = np.linspace(0.0, np.pi, resolution)
|
||||
phis = np.linspace(0.0, 2.0 * np.pi, 2 * resolution, endpoint=False)
|
||||
TT, PP = np.meshgrid(thetas, phis)
|
||||
TT_flat = TT.ravel()
|
||||
PP_flat = PP.ravel()
|
||||
|
||||
L_vals = L0 + radius * np.cos(TT_flat)
|
||||
a_vals = a0 + radius * np.sin(TT_flat) * np.cos(PP_flat)
|
||||
b_vals = b0 + radius * np.sin(TT_flat) * np.sin(PP_flat)
|
||||
|
||||
x = []
|
||||
y = []
|
||||
z = []
|
||||
cmap = []
|
||||
inside_mask = []
|
||||
|
||||
for Lv, av, bv in zip(L_vals, a_vals, b_vals):
|
||||
c = Color('oklab', [Lv, av, bv]).convert(space, in_place=True).normalize(nans=False)
|
||||
|
||||
# Basic L sanity; out-of-range L is definitely not useful
|
||||
if not (0.0 <= c[0] <= 1.0):
|
||||
inside_mask.append(False)
|
||||
x.append(0.0); y.append(0.0); z.append(0.0)
|
||||
cmap.append('#000000')
|
||||
continue
|
||||
|
||||
store_coords(c, x, y, z, flags)
|
||||
|
||||
s = c.convert('srgb')
|
||||
in_gamut = s.in_gamut()
|
||||
inside_mask.append(in_gamut)
|
||||
|
||||
if in_gamut:
|
||||
if not s.in_gamut():
|
||||
s.fit(**gmap)
|
||||
else:
|
||||
s.clip()
|
||||
if filters:
|
||||
s.filter(filters[0], **filters[1], in_place=True, out_space=s.space()).clip()
|
||||
cmap.append(s.to_string(hex=True, alpha=False))
|
||||
else:
|
||||
cmap.append('#000000')
|
||||
|
||||
inside_mask = np.asarray(inside_mask, dtype=bool)
|
||||
|
||||
# Triangulate parameter space (theta, phi)
|
||||
uv = np.column_stack([TT_flat, PP_flat])
|
||||
tri = Delaunay(uv)
|
||||
simplices = tri.simplices
|
||||
|
||||
# Keep only triangles whose vertices are all in gamut
|
||||
keep = inside_mask[simplices].all(axis=1)
|
||||
simplices = simplices[keep]
|
||||
if simplices.size == 0:
|
||||
return
|
||||
|
||||
# Compact vertices to those actually used in kept simplices
|
||||
used = np.unique(simplices.ravel())
|
||||
idx_map = {old: i for i, old in enumerate(used)}
|
||||
simplices_comp = np.vectorize(idx_map.__getitem__)(simplices)
|
||||
|
||||
x2 = [x[i] for i in used]
|
||||
y2 = [y[i] for i in used]
|
||||
z2 = [z[i] for i in used]
|
||||
cmap2 = [cmap[i] for i in used]
|
||||
|
||||
class _Tri:
|
||||
def __init__(self, simplices):
|
||||
self.simplices = simplices
|
||||
|
||||
tri2 = _Tri(simplices_comp)
|
||||
|
||||
create3d(
|
||||
fig,
|
||||
x2,
|
||||
y2,
|
||||
z2,
|
||||
tri2,
|
||||
cmap2,
|
||||
edges=edges,
|
||||
faces=faces,
|
||||
ecolor=ecolor,
|
||||
fcolor=fcolor,
|
||||
opacity=opacity,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
if equal_metric:
|
||||
set_equal_metric_aspect(fig)
|
||||
|
||||
def plot_sphere_in_oklch2(
|
||||
fig,
|
||||
center, # Color spec, interpreted via Color()
|
||||
radius, # OKLab radius
|
||||
gmap,
|
||||
filters=(),
|
||||
resolution=24,
|
||||
opacity=0.2,
|
||||
edges=False,
|
||||
faces=False,
|
||||
ecolor=None,
|
||||
fcolor=None,
|
||||
outside_color='#000000'
|
||||
):
|
||||
space = 'oklch'
|
||||
target = Color.CS_MAP[space]
|
||||
flags = {
|
||||
'is_cyl': target.is_polar(),
|
||||
'is_labish': isinstance(target, Labish),
|
||||
'is_lchish': isinstance(target, LChish),
|
||||
'is_hslish': isinstance(target, HSLish),
|
||||
'is_hwbish': isinstance(target, HWBish),
|
||||
'is_hsvish': isinstance(target, HSVish)
|
||||
}
|
||||
|
||||
# Center in OKLab (Euclidean)
|
||||
c_center = Color(center).convert('oklab', in_place=True).normalize(nans=False)
|
||||
L0, a0, b0 = c_center[0], c_center[1], c_center[2]
|
||||
|
||||
# Parametric sphere in (L, a, b)
|
||||
thetas = np.linspace(0.0, np.pi, resolution)
|
||||
phis = np.linspace(0.0, 2.0 * np.pi, 2 * resolution, endpoint=False)
|
||||
TT, PP = np.meshgrid(thetas, phis)
|
||||
TT_flat = TT.ravel()
|
||||
PP_flat = PP.ravel()
|
||||
|
||||
L_vals = L0 + radius * np.cos(TT_flat)
|
||||
a_vals = a0 + radius * np.sin(TT_flat) * np.cos(PP_flat)
|
||||
b_vals = b0 + radius * np.sin(TT_flat) * np.sin(PP_flat)
|
||||
|
||||
x, y, z, cmap = [], [], [], []
|
||||
|
||||
for Lv, av, bv in zip(L_vals, a_vals, b_vals):
|
||||
# Build OKLab color, convert to OKLCH for consistency with the space
|
||||
c = Color('oklab', [Lv, av, bv]).convert(space, in_place=True).normalize(nans=False)
|
||||
|
||||
# Map to plotted coordinates (LCh → Lab embedding)
|
||||
store_coords(c, x, y, z, flags)
|
||||
|
||||
# Decide vertex color based on sRGB gamut
|
||||
s = c.convert('srgb')
|
||||
if s.in_gamut():
|
||||
if not s.in_gamut():
|
||||
s.fit(**gmap)
|
||||
else:
|
||||
s.clip()
|
||||
if filters:
|
||||
s.filter(filters[0], **filters[1], in_place=True, out_space=s.space()).clip()
|
||||
cmap.append(s.to_string(hex=True, alpha=False))
|
||||
else:
|
||||
cmap.append(outside_color)
|
||||
|
||||
# Triangulate parameter space (theta, phi).
|
||||
# NOTE: we DO NOT filter simplices here: full sphere is drawn,
|
||||
# just colored differently inside vs outside.
|
||||
uv = np.column_stack([TT_flat, PP_flat])
|
||||
tri = Delaunay(uv)
|
||||
|
||||
create3d(
|
||||
fig,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
tri,
|
||||
cmap,
|
||||
edges=edges,
|
||||
faces=faces,
|
||||
ecolor=ecolor,
|
||||
fcolor=fcolor,
|
||||
opacity=opacity,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
def plot_sphere_in_oklch3(
|
||||
fig,
|
||||
center,
|
||||
radius,
|
||||
gmap,
|
||||
filters=(),
|
||||
resolution=24,
|
||||
opacity=0.4,
|
||||
edges=False,
|
||||
faces=False,
|
||||
ecolor=None,
|
||||
fcolor=None,
|
||||
draw_boundary=True,
|
||||
boundary_color='black',
|
||||
boundary_width=3
|
||||
):
|
||||
space = 'oklch'
|
||||
target = Color.CS_MAP[space]
|
||||
flags = {
|
||||
'is_cyl': target.is_polar(),
|
||||
'is_labish': isinstance(target, Labish),
|
||||
'is_lchish': isinstance(target, LChish),
|
||||
'is_hslish': isinstance(target, HSLish),
|
||||
'is_hwbish': isinstance(target, HWBish),
|
||||
'is_hsvish': isinstance(target, HSVish)
|
||||
}
|
||||
|
||||
# 1) Center in OKLab (Euclidean)
|
||||
c_center = Color(center).convert('oklab', in_place=True).normalize(nans=False)
|
||||
L0, a0, b0 = c_center[0], c_center[1], c_center[2]
|
||||
|
||||
# 2) Parametric sphere in (L, a, b)
|
||||
thetas = np.linspace(0.0, np.pi, resolution)
|
||||
phis = np.linspace(0.0, 2.0 * np.pi, 2 * resolution, endpoint=False)
|
||||
TT, PP = np.meshgrid(thetas, phis)
|
||||
TT_flat = TT.ravel()
|
||||
PP_flat = PP.ravel()
|
||||
|
||||
L_vals = L0 + radius * np.cos(TT_flat)
|
||||
a_vals = a0 + radius * np.sin(TT_flat) * np.cos(PP_flat)
|
||||
b_vals = b0 + radius * np.sin(TT_flat) * np.sin(PP_flat)
|
||||
|
||||
x = []
|
||||
y = []
|
||||
z = []
|
||||
cmap = []
|
||||
inside_mask = []
|
||||
|
||||
for Lv, av, bv in zip(L_vals, a_vals, b_vals):
|
||||
# OKLab -> OKLCH (for consistency with space)
|
||||
c = Color('oklab', [Lv, av, bv]).convert(space, in_place=True).normalize(nans=False)
|
||||
|
||||
# Optionally reject totally crazy L, but not strictly required
|
||||
if not (0.0 <= c[0] <= 1.0):
|
||||
inside_mask.append(False)
|
||||
x.append(0.0); y.append(0.0); z.append(0.0)
|
||||
cmap.append('#000000')
|
||||
continue
|
||||
|
||||
# Map to 3D coords used in gamut plotting
|
||||
store_coords(c, x, y, z, flags)
|
||||
|
||||
# sRGB for display + in-gamut test
|
||||
s = c.convert('srgb')
|
||||
in_gamut = s.in_gamut()
|
||||
inside_mask.append(in_gamut)
|
||||
|
||||
if in_gamut:
|
||||
if not s.in_gamut():
|
||||
s.fit(**gmap)
|
||||
else:
|
||||
s.clip()
|
||||
if filters:
|
||||
s.filter(filters[0], **filters[1], in_place=True, out_space=s.space()).clip()
|
||||
cmap.append(s.to_string(hex=True, alpha=False))
|
||||
else:
|
||||
# color won't be used for clipped mesh, but keep list aligned
|
||||
cmap.append('#000000')
|
||||
|
||||
inside_mask = np.asarray(inside_mask, dtype=bool)
|
||||
|
||||
# 3) Triangulate parameter space
|
||||
uv = np.column_stack([TT_flat, PP_flat])
|
||||
tri_all = Delaunay(uv)
|
||||
simplices_all = tri_all.simplices
|
||||
|
||||
# ----- 3a) Build clipped mesh: only triangles fully inside gamut -----
|
||||
keep = inside_mask[simplices_all].all(axis=1)
|
||||
simplices = simplices_all[keep]
|
||||
if simplices.size == 0:
|
||||
# sphere completely out of gamut; nothing to draw
|
||||
return
|
||||
|
||||
used = np.unique(simplices.ravel())
|
||||
idx_map = {old: i for i, old in enumerate(used)}
|
||||
simplices_comp = np.vectorize(idx_map.__getitem__)(simplices)
|
||||
|
||||
x2 = [x[i] for i in used]
|
||||
y2 = [y[i] for i in used]
|
||||
z2 = [z[i] for i in used]
|
||||
cmap2 = [cmap[i] for i in used]
|
||||
|
||||
class _Tri:
|
||||
def __init__(self, simplices):
|
||||
self.simplices = simplices
|
||||
|
||||
tri2 = _Tri(simplices_comp)
|
||||
|
||||
# Clipped sphere surface
|
||||
create3d(
|
||||
fig,
|
||||
x2,
|
||||
y2,
|
||||
z2,
|
||||
tri2,
|
||||
cmap2,
|
||||
edges=edges,
|
||||
faces=faces,
|
||||
ecolor=ecolor,
|
||||
fcolor=fcolor,
|
||||
opacity=opacity,
|
||||
filters=filters
|
||||
)
|
||||
|
||||
# ----- 3b) Boundary curve: edges where inside/outside differ -----
|
||||
if draw_boundary:
|
||||
boundary_edges = set()
|
||||
|
||||
for vs in simplices_all:
|
||||
m = inside_mask[vs]
|
||||
# If all in or all out, no boundary here
|
||||
if np.all(m) or (not np.any(m)):
|
||||
continue
|
||||
|
||||
# For each triangle edge, if it connects inside<->outside, add it
|
||||
edge_pairs = ((vs[0], vs[1]), (vs[1], vs[2]), (vs[2], vs[0]))
|
||||
for a, b in edge_pairs:
|
||||
if inside_mask[a] != inside_mask[b]:
|
||||
# use sorted so (a,b) and (b,a) are treated the same
|
||||
boundary_edges.add(tuple(sorted((a, b))))
|
||||
|
||||
if boundary_edges:
|
||||
xe, ye, ze = [], [], []
|
||||
for a, b in boundary_edges:
|
||||
xe.extend([x[a], x[b], None])
|
||||
ye.extend([y[a], y[b], None])
|
||||
ze.extend([z[a], z[b], None])
|
||||
|
||||
fig.add_trace(
|
||||
go.Scatter3d(
|
||||
x=xe,
|
||||
y=ye,
|
||||
z=ze,
|
||||
mode='lines',
|
||||
line={'color': boundary_color, 'width': boundary_width},
|
||||
showlegend=False,
|
||||
name=''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def set_equal_metric_aspect(fig):
|
||||
xs = []
|
||||
ys = []
|
||||
zs = []
|
||||
|
||||
for tr in fig.data:
|
||||
# Just be defensive in case some traces don't have full 3D coords
|
||||
tx = getattr(tr, 'x', None)
|
||||
ty = getattr(tr, 'y', None)
|
||||
tz = getattr(tr, 'z', None)
|
||||
if tx is None or ty is None or tz is None:
|
||||
continue
|
||||
xs.extend([v for v in tx if v is not None])
|
||||
ys.extend([v for v in ty if v is not None])
|
||||
zs.extend([v for v in tz if v is not None])
|
||||
|
||||
if not xs or not ys or not zs:
|
||||
return
|
||||
|
||||
xmin, xmax = min(xs), max(xs)
|
||||
ymin, ymax = min(ys), max(ys)
|
||||
zmin, zmax = min(zs), max(zs)
|
||||
|
||||
dx = xmax - xmin or 1.0
|
||||
dy = ymax - ymin or 1.0
|
||||
dz = zmax - zmin or 1.0
|
||||
|
||||
m = max(dx, dy, dz)
|
||||
|
||||
fig.update_layout(
|
||||
scene=dict(
|
||||
aspectmode='manual',
|
||||
aspectratio=dict(
|
||||
x=dx / m,
|
||||
y=dy / m,
|
||||
z=dz / m
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def freeze_scene_axes(fig):
|
||||
xs, ys, zs = [], [], []
|
||||
|
||||
for tr in fig.data:
|
||||
tx = getattr(tr, 'x', None)
|
||||
ty = getattr(tr, 'y', None)
|
||||
tz = getattr(tr, 'z', None)
|
||||
if tx is None or ty is None or tz is None:
|
||||
continue
|
||||
xs.extend([v for v in tx if v is not None])
|
||||
ys.extend([v for v in ty if v is not None])
|
||||
zs.extend([v for v in tz if v is not None])
|
||||
|
||||
if not xs or not ys or not zs:
|
||||
return
|
||||
|
||||
xmin, xmax = min(xs), max(xs)
|
||||
ymin, ymax = min(ys), max(ys)
|
||||
zmin, zmax = min(zs), max(zs)
|
||||
|
||||
fig.update_layout(
|
||||
scene=dict(
|
||||
xaxis=dict(range=[xmin, xmax], autorange=False),
|
||||
yaxis=dict(range=[ymin, ymax], autorange=False),
|
||||
zaxis=dict(range=[zmin, zmax], autorange=False),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
778
notebooks/monobiome_qbr-shapenorm-v130.ipynb
Normal file
1
notebooks/palettes/monobiome-vQBRsn-130-oklch.json
Normal file
1000
notebooks/palettes/monobiome-vQBRsn-130.toml
Normal file
@@ -1,11 +1,16 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "monobiome"
|
||||
version = "1.2.0"
|
||||
description = "Add your description here"
|
||||
version = "1.4.0"
|
||||
description = "Monobiome color palette"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"coloraide>=5.1",
|
||||
"imageio[ffmpeg]>=2.37.2",
|
||||
"ipython>=9.6.0",
|
||||
"kaleido>=1.1.0",
|
||||
"matplotlib>=3.10.7",
|
||||
@@ -20,3 +25,39 @@ dependencies = [
|
||||
dev = [
|
||||
"ipykernel>=7.0.1",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
monobiome = "monobiome.__main__:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://doc.olog.io/monobiome"
|
||||
Documentation = "https://doc.olog.io/monobiome"
|
||||
Repository = "https://git.olog.io/olog/monobiome"
|
||||
Issues = "https://git.olog.io/olog/monobiome/issues"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["monobiome*"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"monobiome" = ["data/*.toml"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 79
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["ANN", "E", "F", "UP", "B", "SIM", "I", "C4", "PERF"]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
length-sort = true
|
||||
order-by-type = false
|
||||
force-sort-within-sections = false
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/**" = ["S101"]
|
||||
"**/__init__.py" = ["F401"]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
docstring-code-format = true
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# note: this script is not portable; to be run in the monobiome scripts/
|
||||
# directory (notice the `rm` invocations)
|
||||
|
||||
rm -rf app-config/*
|
||||
symconf -c templates/ generate -o app-config
|
||||
|
||||
40
scripts/render.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# usage: render.sh width height [extra kitty args...]
|
||||
# useful for reproducible screenshots of terminal/vim setups
|
||||
|
||||
set -euo pipefail
|
||||
w=${1:?width}; h=${2:?height}
|
||||
out=${3:?file};
|
||||
#out="kitty-$(date +%Y%m%d-%H%M%S).png"
|
||||
shift 3 || true
|
||||
|
||||
# spawn a kitty window w/ a mark
|
||||
title="kitty-$(date +%s%N)"
|
||||
sock="$XDG_RUNTIME_DIR/kitty-$title.sock"
|
||||
kitty -o allow_remote_control=yes --listen-on "unix:$sock" --title "$title" "$@" &
|
||||
|
||||
# create a targeted rule for the marked window and resize
|
||||
sleep 2
|
||||
swaymsg "for_window [title=\"$title\"] mark --add $title, floating enable, resize set width ${w} px height ${h} px, move position center" >/dev/null
|
||||
|
||||
# get the spawned window geometry
|
||||
sleep 2
|
||||
geom=$(swaymsg -t get_tree | jq -r --arg t "$title" '.. | select(.name? == $t) | .rect | "\(.x),\(.y) \(.width)x\(.height)"')
|
||||
wgeom=$(swaymsg -t get_tree | jq -r --arg t "$title" '.. | select(.name? == $t) | .window_rect | "\(.x),\(.y) \(.width)x\(.height)"')
|
||||
|
||||
read gx gy _ < <(awk -F'[ ,x]' '{print $1,$2}' <<<"$geom")
|
||||
read wx wy ww wh < <(awk -F'[ ,x]' '{print $1,$2,$3,$4}' <<<"$wgeom")
|
||||
inner_geom="$((gx+wx)),$((gy+wy)) ${ww}x${wh}"
|
||||
|
||||
echo "+ title=$title"
|
||||
echo "+ geom=$geom"
|
||||
echo "+ out=$out"
|
||||
|
||||
# take a screenshot
|
||||
mkdir -p "$(dirname "$out")"
|
||||
grim -g "$inner_geom" "$out"
|
||||
echo "saved: $out"
|
||||
|
||||
# close the kitty window
|
||||
kitty @ --to "unix:$sock" close-window
|
||||
|
||||
33
scripts/screens.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# usage: screens.sh prefix
|
||||
|
||||
set -euo pipefail
|
||||
prefix=${1:-}
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
render_script="$script_dir/render.sh"
|
||||
|
||||
#biomes=(alpine)
|
||||
#modes=(light dark)
|
||||
biomes=(alpine)
|
||||
modes=(dark)
|
||||
|
||||
for biome in "${biomes[@]}"; do
|
||||
for mode in "${modes[@]}"; do
|
||||
echo "Applying $biome-$mode theme"
|
||||
symconf config \
|
||||
-a kitty,nvim \
|
||||
-m "$mode" \
|
||||
-s "default-$biome-monobiome" \
|
||||
-T font=Berkeley
|
||||
sleep 2
|
||||
|
||||
echo "Taking screenshot..."
|
||||
"$render_script" 800 600 "images/render/$prefix-$biome-$mode.png" nvim \
|
||||
+':highlight Cursor blend=100' \
|
||||
+':set guicursor=n:block-Cursor' \
|
||||
+':silent! setlocal nonumber nocursorline signcolumn=no foldcolumn=no' \
|
||||
+':lua vim.diagnostic.config({virtual_text=false,signs=false,underline=false})' \
|
||||
examples/class.py
|
||||
done
|
||||
done
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"version": "1.2.0",
|
||||
"version": "f{{theme.version}}",
|
||||
"name": "monobiome-f{{theme.biome}}",
|
||||
"theme": {
|
||||
"colors": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"version": "1.2.0",
|
||||
"version": "f{{theme.version}}",
|
||||
"name": "monobiome-f{{theme.biome}}-dark",
|
||||
"theme": {
|
||||
"colors": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"version": "1.2.0",
|
||||
"version": "f{{theme.version}}",
|
||||
"name": "monobiome-f{{theme.biome}}-light",
|
||||
"theme": {
|
||||
"colors": {
|
||||
|
||||
@@ -546,76 +546,76 @@
|
||||
"98": "oklch(98.0% 0.0094 262.0)"
|
||||
},
|
||||
"red": {
|
||||
"10": "oklch(10.0% 0.0391 29.0)",
|
||||
"11": "oklch(11.0% 0.0429 29.0)",
|
||||
"12": "oklch(12.0% 0.0468 29.0)",
|
||||
"13": "oklch(13.0% 0.0506 29.0)",
|
||||
"14": "oklch(14.0% 0.0545 29.0)",
|
||||
"15": "oklch(15.0% 0.0583 29.0)",
|
||||
"16": "oklch(16.0% 0.0621 29.0)",
|
||||
"17": "oklch(17.0% 0.0659 29.0)",
|
||||
"18": "oklch(18.0% 0.0697 29.0)",
|
||||
"19": "oklch(19.0% 0.0735 29.0)",
|
||||
"20": "oklch(20.0% 0.0773 29.0)",
|
||||
"21": "oklch(21.0% 0.0810 29.0)",
|
||||
"22": "oklch(22.0% 0.0847 29.0)",
|
||||
"23": "oklch(23.0% 0.0885 29.0)",
|
||||
"24": "oklch(24.0% 0.0922 29.0)",
|
||||
"25": "oklch(25.0% 0.0958 29.0)",
|
||||
"26": "oklch(26.0% 0.0995 29.0)",
|
||||
"27": "oklch(27.0% 0.1031 29.0)",
|
||||
"28": "oklch(28.0% 0.1067 29.0)",
|
||||
"29": "oklch(29.0% 0.1103 29.0)",
|
||||
"30": "oklch(30.0% 0.1139 29.0)",
|
||||
"31": "oklch(31.0% 0.1174 29.0)",
|
||||
"32": "oklch(32.0% 0.1209 29.0)",
|
||||
"33": "oklch(33.0% 0.1244 29.0)",
|
||||
"34": "oklch(34.0% 0.1278 29.0)",
|
||||
"35": "oklch(35.0% 0.1312 29.0)",
|
||||
"36": "oklch(36.0% 0.1346 29.0)",
|
||||
"37": "oklch(37.0% 0.1378 29.0)",
|
||||
"38": "oklch(38.0% 0.1411 29.0)",
|
||||
"39": "oklch(39.0% 0.1443 29.0)",
|
||||
"40": "oklch(40.0% 0.1474 29.0)",
|
||||
"41": "oklch(41.0% 0.1505 29.0)",
|
||||
"42": "oklch(42.0% 0.1535 29.0)",
|
||||
"43": "oklch(43.0% 0.1564 29.0)",
|
||||
"44": "oklch(44.0% 0.1592 29.0)",
|
||||
"45": "oklch(45.0% 0.1619 29.0)",
|
||||
"46": "oklch(46.0% 0.1645 29.0)",
|
||||
"47": "oklch(47.0% 0.1670 29.0)",
|
||||
"48": "oklch(48.0% 0.1694 29.0)",
|
||||
"49": "oklch(49.0% 0.1716 29.0)",
|
||||
"50": "oklch(50.0% 0.1737 29.0)",
|
||||
"51": "oklch(51.0% 0.1756 29.0)",
|
||||
"52": "oklch(52.0% 0.1774 29.0)",
|
||||
"53": "oklch(53.0% 0.1789 29.0)",
|
||||
"54": "oklch(54.0% 0.1802 29.0)",
|
||||
"55": "oklch(55.0% 0.1813 29.0)",
|
||||
"56": "oklch(56.0% 0.1821 29.0)",
|
||||
"57": "oklch(57.0% 0.1827 29.0)",
|
||||
"58": "oklch(58.0% 0.1829 29.0)",
|
||||
"59": "oklch(59.0% 0.1829 29.0)",
|
||||
"60": "oklch(60.0% 0.1825 29.0)",
|
||||
"61": "oklch(61.0% 0.1819 29.0)",
|
||||
"62": "oklch(62.0% 0.1809 29.0)",
|
||||
"63": "oklch(63.0% 0.1795 29.0)",
|
||||
"64": "oklch(64.0% 0.1778 29.0)",
|
||||
"10": "oklch(10.0% 0.0406 29.0)",
|
||||
"11": "oklch(11.0% 0.0446 29.0)",
|
||||
"12": "oklch(12.0% 0.0486 29.0)",
|
||||
"13": "oklch(13.0% 0.0526 29.0)",
|
||||
"14": "oklch(14.0% 0.0566 29.0)",
|
||||
"15": "oklch(15.0% 0.0606 29.0)",
|
||||
"16": "oklch(16.0% 0.0645 29.0)",
|
||||
"17": "oklch(17.0% 0.0685 29.0)",
|
||||
"18": "oklch(18.0% 0.0724 29.0)",
|
||||
"19": "oklch(19.0% 0.0763 29.0)",
|
||||
"20": "oklch(20.0% 0.0802 29.0)",
|
||||
"21": "oklch(21.0% 0.0841 29.0)",
|
||||
"22": "oklch(22.0% 0.0879 29.0)",
|
||||
"23": "oklch(23.0% 0.0918 29.0)",
|
||||
"24": "oklch(24.0% 0.0956 29.0)",
|
||||
"25": "oklch(25.0% 0.0994 29.0)",
|
||||
"26": "oklch(26.0% 0.1031 29.0)",
|
||||
"27": "oklch(27.0% 0.1068 29.0)",
|
||||
"28": "oklch(28.0% 0.1106 29.0)",
|
||||
"29": "oklch(29.0% 0.1142 29.0)",
|
||||
"30": "oklch(30.0% 0.1179 29.0)",
|
||||
"31": "oklch(31.0% 0.1215 29.0)",
|
||||
"32": "oklch(32.0% 0.1250 29.0)",
|
||||
"33": "oklch(33.0% 0.1286 29.0)",
|
||||
"34": "oklch(34.0% 0.1320 29.0)",
|
||||
"35": "oklch(35.0% 0.1355 29.0)",
|
||||
"36": "oklch(36.0% 0.1389 29.0)",
|
||||
"37": "oklch(37.0% 0.1422 29.0)",
|
||||
"38": "oklch(38.0% 0.1454 29.0)",
|
||||
"39": "oklch(39.0% 0.1486 29.0)",
|
||||
"40": "oklch(40.0% 0.1517 29.0)",
|
||||
"41": "oklch(41.0% 0.1548 29.0)",
|
||||
"42": "oklch(42.0% 0.1577 29.0)",
|
||||
"43": "oklch(43.0% 0.1606 29.0)",
|
||||
"44": "oklch(44.0% 0.1634 29.0)",
|
||||
"45": "oklch(45.0% 0.1660 29.0)",
|
||||
"46": "oklch(46.0% 0.1685 29.0)",
|
||||
"47": "oklch(47.0% 0.1709 29.0)",
|
||||
"48": "oklch(48.0% 0.1732 29.0)",
|
||||
"49": "oklch(49.0% 0.1752 29.0)",
|
||||
"50": "oklch(50.0% 0.1772 29.0)",
|
||||
"51": "oklch(51.0% 0.1789 29.0)",
|
||||
"52": "oklch(52.0% 0.1804 29.0)",
|
||||
"53": "oklch(53.0% 0.1817 29.0)",
|
||||
"54": "oklch(54.0% 0.1828 29.0)",
|
||||
"55": "oklch(55.0% 0.1836 29.0)",
|
||||
"56": "oklch(56.0% 0.1842 29.0)",
|
||||
"57": "oklch(57.0% 0.1845 29.0)",
|
||||
"58": "oklch(58.0% 0.1845 29.0)",
|
||||
"59": "oklch(59.0% 0.1842 29.0)",
|
||||
"60": "oklch(60.0% 0.1836 29.0)",
|
||||
"61": "oklch(61.0% 0.1826 29.0)",
|
||||
"62": "oklch(62.0% 0.1814 29.0)",
|
||||
"63": "oklch(63.0% 0.1799 29.0)",
|
||||
"64": "oklch(64.0% 0.1780 29.0)",
|
||||
"65": "oklch(65.0% 0.1758 29.0)",
|
||||
"66": "oklch(66.0% 0.1735 29.0)",
|
||||
"67": "oklch(67.0% 0.1709 29.0)",
|
||||
"68": "oklch(68.0% 0.1680 29.0)",
|
||||
"69": "oklch(69.0% 0.1648 29.0)",
|
||||
"70": "oklch(70.0% 0.1613 29.0)",
|
||||
"71": "oklch(71.0% 0.1576 29.0)",
|
||||
"72": "oklch(72.0% 0.1537 29.0)",
|
||||
"73": "oklch(73.0% 0.1496 29.0)",
|
||||
"74": "oklch(74.0% 0.1453 29.0)",
|
||||
"75": "oklch(75.0% 0.1408 29.0)",
|
||||
"76": "oklch(76.0% 0.1361 29.0)",
|
||||
"77": "oklch(77.0% 0.1313 29.0)",
|
||||
"78": "oklch(78.0% 0.1264 29.0)",
|
||||
"79": "oklch(79.0% 0.1213 29.0)",
|
||||
"66": "oklch(66.0% 0.1734 29.0)",
|
||||
"67": "oklch(67.0% 0.1706 29.0)",
|
||||
"68": "oklch(68.0% 0.1676 29.0)",
|
||||
"69": "oklch(69.0% 0.1643 29.0)",
|
||||
"70": "oklch(70.0% 0.1608 29.0)",
|
||||
"71": "oklch(71.0% 0.1571 29.0)",
|
||||
"72": "oklch(72.0% 0.1531 29.0)",
|
||||
"73": "oklch(73.0% 0.1490 29.0)",
|
||||
"74": "oklch(74.0% 0.1447 29.0)",
|
||||
"75": "oklch(75.0% 0.1402 29.0)",
|
||||
"76": "oklch(76.0% 0.1355 29.0)",
|
||||
"77": "oklch(77.0% 0.1308 29.0)",
|
||||
"78": "oklch(78.0% 0.1259 29.0)",
|
||||
"79": "oklch(79.0% 0.1208 29.0)",
|
||||
"80": "oklch(80.0% 0.1147 29.0)",
|
||||
"81": "oklch(81.0% 0.1079 29.0)",
|
||||
"82": "oklch(82.0% 0.1012 29.0)",
|
||||
@@ -637,351 +637,351 @@
|
||||
"98": "oklch(98.0% 0.0097 29.0)"
|
||||
},
|
||||
"orange": {
|
||||
"10": "oklch(10.0% 0.0213 62.5)",
|
||||
"11": "oklch(11.0% 0.0234 62.5)",
|
||||
"10": "oklch(10.0% 0.0214 62.5)",
|
||||
"11": "oklch(11.0% 0.0235 62.5)",
|
||||
"12": "oklch(12.0% 0.0256 62.5)",
|
||||
"13": "oklch(13.0% 0.0277 62.5)",
|
||||
"14": "oklch(14.0% 0.0298 62.5)",
|
||||
"15": "oklch(15.0% 0.0319 62.5)",
|
||||
"16": "oklch(16.0% 0.0340 62.5)",
|
||||
"17": "oklch(17.0% 0.0361 62.5)",
|
||||
"18": "oklch(18.0% 0.0382 62.5)",
|
||||
"19": "oklch(19.0% 0.0403 62.5)",
|
||||
"20": "oklch(20.0% 0.0424 62.5)",
|
||||
"21": "oklch(21.0% 0.0445 62.5)",
|
||||
"22": "oklch(22.0% 0.0465 62.5)",
|
||||
"23": "oklch(23.0% 0.0486 62.5)",
|
||||
"24": "oklch(24.0% 0.0507 62.5)",
|
||||
"25": "oklch(25.0% 0.0527 62.5)",
|
||||
"26": "oklch(26.0% 0.0548 62.5)",
|
||||
"27": "oklch(27.0% 0.0569 62.5)",
|
||||
"28": "oklch(28.0% 0.0589 62.5)",
|
||||
"29": "oklch(29.0% 0.0609 62.5)",
|
||||
"30": "oklch(30.0% 0.0630 62.5)",
|
||||
"31": "oklch(31.0% 0.0650 62.5)",
|
||||
"32": "oklch(32.0% 0.0670 62.5)",
|
||||
"33": "oklch(33.0% 0.0690 62.5)",
|
||||
"34": "oklch(34.0% 0.0710 62.5)",
|
||||
"35": "oklch(35.0% 0.0730 62.5)",
|
||||
"36": "oklch(36.0% 0.0749 62.5)",
|
||||
"37": "oklch(37.0% 0.0769 62.5)",
|
||||
"38": "oklch(38.0% 0.0788 62.5)",
|
||||
"39": "oklch(39.0% 0.0808 62.5)",
|
||||
"40": "oklch(40.0% 0.0827 62.5)",
|
||||
"41": "oklch(41.0% 0.0846 62.5)",
|
||||
"42": "oklch(42.0% 0.0864 62.5)",
|
||||
"43": "oklch(43.0% 0.0883 62.5)",
|
||||
"44": "oklch(44.0% 0.0901 62.5)",
|
||||
"45": "oklch(45.0% 0.0919 62.5)",
|
||||
"46": "oklch(46.0% 0.0937 62.5)",
|
||||
"47": "oklch(47.0% 0.0954 62.5)",
|
||||
"48": "oklch(48.0% 0.0971 62.5)",
|
||||
"49": "oklch(49.0% 0.0987 62.5)",
|
||||
"50": "oklch(50.0% 0.1004 62.5)",
|
||||
"51": "oklch(51.0% 0.1019 62.5)",
|
||||
"52": "oklch(52.0% 0.1034 62.5)",
|
||||
"53": "oklch(53.0% 0.1049 62.5)",
|
||||
"54": "oklch(54.0% 0.1062 62.5)",
|
||||
"55": "oklch(55.0% 0.1075 62.5)",
|
||||
"56": "oklch(56.0% 0.1087 62.5)",
|
||||
"57": "oklch(57.0% 0.1098 62.5)",
|
||||
"58": "oklch(58.0% 0.1108 62.5)",
|
||||
"59": "oklch(59.0% 0.1117 62.5)",
|
||||
"60": "oklch(60.0% 0.1124 62.5)",
|
||||
"61": "oklch(61.0% 0.1130 62.5)",
|
||||
"62": "oklch(62.0% 0.1134 62.5)",
|
||||
"63": "oklch(63.0% 0.1136 62.5)",
|
||||
"64": "oklch(64.0% 0.1136 62.5)",
|
||||
"65": "oklch(65.0% 0.1133 62.5)",
|
||||
"66": "oklch(66.0% 0.1129 62.5)",
|
||||
"67": "oklch(67.0% 0.1121 62.5)",
|
||||
"68": "oklch(68.0% 0.1111 62.5)",
|
||||
"69": "oklch(69.0% 0.1099 62.5)",
|
||||
"70": "oklch(70.0% 0.1083 62.5)",
|
||||
"71": "oklch(71.0% 0.1066 62.5)",
|
||||
"72": "oklch(72.0% 0.1045 62.5)",
|
||||
"73": "oklch(73.0% 0.1023 62.5)",
|
||||
"74": "oklch(74.0% 0.0998 62.5)",
|
||||
"75": "oklch(75.0% 0.0971 62.5)",
|
||||
"76": "oklch(76.0% 0.0942 62.5)",
|
||||
"77": "oklch(77.0% 0.0912 62.5)",
|
||||
"78": "oklch(78.0% 0.0880 62.5)",
|
||||
"79": "oklch(79.0% 0.0847 62.5)",
|
||||
"80": "oklch(80.0% 0.0812 62.5)",
|
||||
"81": "oklch(81.0% 0.0777 62.5)",
|
||||
"82": "oklch(82.0% 0.0740 62.5)",
|
||||
"83": "oklch(83.0% 0.0703 62.5)",
|
||||
"84": "oklch(84.0% 0.0665 62.5)",
|
||||
"85": "oklch(85.0% 0.0627 62.5)",
|
||||
"86": "oklch(86.0% 0.0587 62.5)",
|
||||
"87": "oklch(87.0% 0.0547 62.5)",
|
||||
"88": "oklch(88.0% 0.0507 62.5)",
|
||||
"89": "oklch(89.0% 0.0466 62.5)",
|
||||
"90": "oklch(90.0% 0.0425 62.5)",
|
||||
"91": "oklch(91.0% 0.0384 62.5)",
|
||||
"92": "oklch(92.0% 0.0342 62.5)",
|
||||
"93": "oklch(93.0% 0.0300 62.5)",
|
||||
"94": "oklch(94.0% 0.0258 62.5)",
|
||||
"95": "oklch(95.0% 0.0215 62.5)",
|
||||
"96": "oklch(96.0% 0.0173 62.5)",
|
||||
"97": "oklch(97.0% 0.0130 62.5)",
|
||||
"98": "oklch(98.0% 0.0087 62.5)"
|
||||
"13": "oklch(13.0% 0.0278 62.5)",
|
||||
"14": "oklch(14.0% 0.0299 62.5)",
|
||||
"15": "oklch(15.0% 0.0320 62.5)",
|
||||
"16": "oklch(16.0% 0.0341 62.5)",
|
||||
"17": "oklch(17.0% 0.0362 62.5)",
|
||||
"18": "oklch(18.0% 0.0383 62.5)",
|
||||
"19": "oklch(19.0% 0.0404 62.5)",
|
||||
"20": "oklch(20.0% 0.0425 62.5)",
|
||||
"21": "oklch(21.0% 0.0446 62.5)",
|
||||
"22": "oklch(22.0% 0.0467 62.5)",
|
||||
"23": "oklch(23.0% 0.0488 62.5)",
|
||||
"24": "oklch(24.0% 0.0509 62.5)",
|
||||
"25": "oklch(25.0% 0.0529 62.5)",
|
||||
"26": "oklch(26.0% 0.0550 62.5)",
|
||||
"27": "oklch(27.0% 0.0571 62.5)",
|
||||
"28": "oklch(28.0% 0.0591 62.5)",
|
||||
"29": "oklch(29.0% 0.0612 62.5)",
|
||||
"30": "oklch(30.0% 0.0632 62.5)",
|
||||
"31": "oklch(31.0% 0.0653 62.5)",
|
||||
"32": "oklch(32.0% 0.0673 62.5)",
|
||||
"33": "oklch(33.0% 0.0693 62.5)",
|
||||
"34": "oklch(34.0% 0.0714 62.5)",
|
||||
"35": "oklch(35.0% 0.0734 62.5)",
|
||||
"36": "oklch(36.0% 0.0753 62.5)",
|
||||
"37": "oklch(37.0% 0.0773 62.5)",
|
||||
"38": "oklch(38.0% 0.0793 62.5)",
|
||||
"39": "oklch(39.0% 0.0813 62.5)",
|
||||
"40": "oklch(40.0% 0.0832 62.5)",
|
||||
"41": "oklch(41.0% 0.0851 62.5)",
|
||||
"42": "oklch(42.0% 0.0870 62.5)",
|
||||
"43": "oklch(43.0% 0.0889 62.5)",
|
||||
"44": "oklch(44.0% 0.0908 62.5)",
|
||||
"45": "oklch(45.0% 0.0926 62.5)",
|
||||
"46": "oklch(46.0% 0.0945 62.5)",
|
||||
"47": "oklch(47.0% 0.0963 62.5)",
|
||||
"48": "oklch(48.0% 0.0980 62.5)",
|
||||
"49": "oklch(49.0% 0.0998 62.5)",
|
||||
"50": "oklch(50.0% 0.1015 62.5)",
|
||||
"51": "oklch(51.0% 0.1031 62.5)",
|
||||
"52": "oklch(52.0% 0.1048 62.5)",
|
||||
"53": "oklch(53.0% 0.1063 62.5)",
|
||||
"54": "oklch(54.0% 0.1078 62.5)",
|
||||
"55": "oklch(55.0% 0.1093 62.5)",
|
||||
"56": "oklch(56.0% 0.1107 62.5)",
|
||||
"57": "oklch(57.0% 0.1120 62.5)",
|
||||
"58": "oklch(58.0% 0.1132 62.5)",
|
||||
"59": "oklch(59.0% 0.1143 62.5)",
|
||||
"60": "oklch(60.0% 0.1153 62.5)",
|
||||
"61": "oklch(61.0% 0.1162 62.5)",
|
||||
"62": "oklch(62.0% 0.1169 62.5)",
|
||||
"63": "oklch(63.0% 0.1175 62.5)",
|
||||
"64": "oklch(64.0% 0.1179 62.5)",
|
||||
"65": "oklch(65.0% 0.1181 62.5)",
|
||||
"66": "oklch(66.0% 0.1181 62.5)",
|
||||
"67": "oklch(67.0% 0.1178 62.5)",
|
||||
"68": "oklch(68.0% 0.1173 62.5)",
|
||||
"69": "oklch(69.0% 0.1166 62.5)",
|
||||
"70": "oklch(70.0% 0.1155 62.5)",
|
||||
"71": "oklch(71.0% 0.1141 62.5)",
|
||||
"72": "oklch(72.0% 0.1125 62.5)",
|
||||
"73": "oklch(73.0% 0.1105 62.5)",
|
||||
"74": "oklch(74.0% 0.1083 62.5)",
|
||||
"75": "oklch(75.0% 0.1058 62.5)",
|
||||
"76": "oklch(76.0% 0.1030 62.5)",
|
||||
"77": "oklch(77.0% 0.1000 62.5)",
|
||||
"78": "oklch(78.0% 0.0968 62.5)",
|
||||
"79": "oklch(79.0% 0.0934 62.5)",
|
||||
"80": "oklch(80.0% 0.0898 62.5)",
|
||||
"81": "oklch(81.0% 0.0861 62.5)",
|
||||
"82": "oklch(82.0% 0.0822 62.5)",
|
||||
"83": "oklch(83.0% 0.0782 62.5)",
|
||||
"84": "oklch(84.0% 0.0741 62.5)",
|
||||
"85": "oklch(85.0% 0.0699 62.5)",
|
||||
"86": "oklch(86.0% 0.0656 62.5)",
|
||||
"87": "oklch(87.0% 0.0612 62.5)",
|
||||
"88": "oklch(88.0% 0.0567 62.5)",
|
||||
"89": "oklch(89.0% 0.0522 62.5)",
|
||||
"90": "oklch(90.0% 0.0477 62.5)",
|
||||
"91": "oklch(91.0% 0.0430 62.5)",
|
||||
"92": "oklch(92.0% 0.0384 62.5)",
|
||||
"93": "oklch(93.0% 0.0337 62.5)",
|
||||
"94": "oklch(94.0% 0.0290 62.5)",
|
||||
"95": "oklch(95.0% 0.0242 62.5)",
|
||||
"96": "oklch(96.0% 0.0194 62.5)",
|
||||
"97": "oklch(97.0% 0.0146 62.5)",
|
||||
"98": "oklch(98.0% 0.0097 62.5)"
|
||||
},
|
||||
"yellow": {
|
||||
"10": "oklch(10.0% 0.0114 104.0)",
|
||||
"11": "oklch(11.0% 0.0125 104.0)",
|
||||
"12": "oklch(12.0% 0.0136 104.0)",
|
||||
"13": "oklch(13.0% 0.0147 104.0)",
|
||||
"14": "oklch(14.0% 0.0159 104.0)",
|
||||
"15": "oklch(15.0% 0.0170 104.0)",
|
||||
"16": "oklch(16.0% 0.0181 104.0)",
|
||||
"17": "oklch(17.0% 0.0192 104.0)",
|
||||
"18": "oklch(18.0% 0.0203 104.0)",
|
||||
"19": "oklch(19.0% 0.0215 104.0)",
|
||||
"20": "oklch(20.0% 0.0226 104.0)",
|
||||
"21": "oklch(21.0% 0.0237 104.0)",
|
||||
"22": "oklch(22.0% 0.0248 104.0)",
|
||||
"23": "oklch(23.0% 0.0259 104.0)",
|
||||
"24": "oklch(24.0% 0.0270 104.0)",
|
||||
"25": "oklch(25.0% 0.0281 104.0)",
|
||||
"26": "oklch(26.0% 0.0292 104.0)",
|
||||
"27": "oklch(27.0% 0.0303 104.0)",
|
||||
"28": "oklch(28.0% 0.0314 104.0)",
|
||||
"29": "oklch(29.0% 0.0325 104.0)",
|
||||
"30": "oklch(30.0% 0.0336 104.0)",
|
||||
"31": "oklch(31.0% 0.0347 104.0)",
|
||||
"32": "oklch(32.0% 0.0358 104.0)",
|
||||
"33": "oklch(33.0% 0.0369 104.0)",
|
||||
"34": "oklch(34.0% 0.0379 104.0)",
|
||||
"35": "oklch(35.0% 0.0390 104.0)",
|
||||
"36": "oklch(36.0% 0.0401 104.0)",
|
||||
"37": "oklch(37.0% 0.0411 104.0)",
|
||||
"38": "oklch(38.0% 0.0422 104.0)",
|
||||
"39": "oklch(39.0% 0.0433 104.0)",
|
||||
"40": "oklch(40.0% 0.0443 104.0)",
|
||||
"41": "oklch(41.0% 0.0453 104.0)",
|
||||
"42": "oklch(42.0% 0.0464 104.0)",
|
||||
"43": "oklch(43.0% 0.0474 104.0)",
|
||||
"44": "oklch(44.0% 0.0484 104.0)",
|
||||
"45": "oklch(45.0% 0.0494 104.0)",
|
||||
"46": "oklch(46.0% 0.0504 104.0)",
|
||||
"47": "oklch(47.0% 0.0514 104.0)",
|
||||
"48": "oklch(48.0% 0.0523 104.0)",
|
||||
"49": "oklch(49.0% 0.0533 104.0)",
|
||||
"50": "oklch(50.0% 0.0542 104.0)",
|
||||
"51": "oklch(51.0% 0.0552 104.0)",
|
||||
"52": "oklch(52.0% 0.0561 104.0)",
|
||||
"53": "oklch(53.0% 0.0569 104.0)",
|
||||
"54": "oklch(54.0% 0.0578 104.0)",
|
||||
"55": "oklch(55.0% 0.0586 104.0)",
|
||||
"56": "oklch(56.0% 0.0594 104.0)",
|
||||
"57": "oklch(57.0% 0.0602 104.0)",
|
||||
"58": "oklch(58.0% 0.0610 104.0)",
|
||||
"59": "oklch(59.0% 0.0616 104.0)",
|
||||
"60": "oklch(60.0% 0.0623 104.0)",
|
||||
"61": "oklch(61.0% 0.0629 104.0)",
|
||||
"62": "oklch(62.0% 0.0634 104.0)",
|
||||
"63": "oklch(63.0% 0.0639 104.0)",
|
||||
"64": "oklch(64.0% 0.0643 104.0)",
|
||||
"65": "oklch(65.0% 0.0646 104.0)",
|
||||
"66": "oklch(66.0% 0.0648 104.0)",
|
||||
"67": "oklch(67.0% 0.0649 104.0)",
|
||||
"68": "oklch(68.0% 0.0649 104.0)",
|
||||
"69": "oklch(69.0% 0.0648 104.0)",
|
||||
"70": "oklch(70.0% 0.0645 104.0)",
|
||||
"71": "oklch(71.0% 0.0641 104.0)",
|
||||
"72": "oklch(72.0% 0.0634 104.0)",
|
||||
"73": "oklch(73.0% 0.0627 104.0)",
|
||||
"74": "oklch(74.0% 0.0617 104.0)",
|
||||
"75": "oklch(75.0% 0.0606 104.0)",
|
||||
"76": "oklch(76.0% 0.0593 104.0)",
|
||||
"77": "oklch(77.0% 0.0578 104.0)",
|
||||
"78": "oklch(78.0% 0.0562 104.0)",
|
||||
"79": "oklch(79.0% 0.0544 104.0)",
|
||||
"80": "oklch(80.0% 0.0525 104.0)",
|
||||
"81": "oklch(81.0% 0.0504 104.0)",
|
||||
"82": "oklch(82.0% 0.0483 104.0)",
|
||||
"83": "oklch(83.0% 0.0461 104.0)",
|
||||
"84": "oklch(84.0% 0.0437 104.0)",
|
||||
"85": "oklch(85.0% 0.0413 104.0)",
|
||||
"86": "oklch(86.0% 0.0388 104.0)",
|
||||
"87": "oklch(87.0% 0.0363 104.0)",
|
||||
"88": "oklch(88.0% 0.0337 104.0)",
|
||||
"89": "oklch(89.0% 0.0311 104.0)",
|
||||
"90": "oklch(90.0% 0.0284 104.0)",
|
||||
"91": "oklch(91.0% 0.0256 104.0)",
|
||||
"92": "oklch(92.0% 0.0229 104.0)",
|
||||
"93": "oklch(93.0% 0.0201 104.0)",
|
||||
"94": "oklch(94.0% 0.0173 104.0)",
|
||||
"95": "oklch(95.0% 0.0145 104.0)",
|
||||
"96": "oklch(96.0% 0.0116 104.0)",
|
||||
"97": "oklch(97.0% 0.0087 104.0)",
|
||||
"98": "oklch(98.0% 0.0058 104.0)"
|
||||
"10": "oklch(10.0% 0.0143 104.0)",
|
||||
"11": "oklch(11.0% 0.0157 104.0)",
|
||||
"12": "oklch(12.0% 0.0171 104.0)",
|
||||
"13": "oklch(13.0% 0.0185 104.0)",
|
||||
"14": "oklch(14.0% 0.0199 104.0)",
|
||||
"15": "oklch(15.0% 0.0214 104.0)",
|
||||
"16": "oklch(16.0% 0.0228 104.0)",
|
||||
"17": "oklch(17.0% 0.0242 104.0)",
|
||||
"18": "oklch(18.0% 0.0256 104.0)",
|
||||
"19": "oklch(19.0% 0.0270 104.0)",
|
||||
"20": "oklch(20.0% 0.0284 104.0)",
|
||||
"21": "oklch(21.0% 0.0298 104.0)",
|
||||
"22": "oklch(22.0% 0.0312 104.0)",
|
||||
"23": "oklch(23.0% 0.0326 104.0)",
|
||||
"24": "oklch(24.0% 0.0340 104.0)",
|
||||
"25": "oklch(25.0% 0.0354 104.0)",
|
||||
"26": "oklch(26.0% 0.0368 104.0)",
|
||||
"27": "oklch(27.0% 0.0382 104.0)",
|
||||
"28": "oklch(28.0% 0.0396 104.0)",
|
||||
"29": "oklch(29.0% 0.0410 104.0)",
|
||||
"30": "oklch(30.0% 0.0424 104.0)",
|
||||
"31": "oklch(31.0% 0.0437 104.0)",
|
||||
"32": "oklch(32.0% 0.0451 104.0)",
|
||||
"33": "oklch(33.0% 0.0465 104.0)",
|
||||
"34": "oklch(34.0% 0.0478 104.0)",
|
||||
"35": "oklch(35.0% 0.0492 104.0)",
|
||||
"36": "oklch(36.0% 0.0506 104.0)",
|
||||
"37": "oklch(37.0% 0.0519 104.0)",
|
||||
"38": "oklch(38.0% 0.0533 104.0)",
|
||||
"39": "oklch(39.0% 0.0546 104.0)",
|
||||
"40": "oklch(40.0% 0.0559 104.0)",
|
||||
"41": "oklch(41.0% 0.0573 104.0)",
|
||||
"42": "oklch(42.0% 0.0586 104.0)",
|
||||
"43": "oklch(43.0% 0.0599 104.0)",
|
||||
"44": "oklch(44.0% 0.0612 104.0)",
|
||||
"45": "oklch(45.0% 0.0625 104.0)",
|
||||
"46": "oklch(46.0% 0.0638 104.0)",
|
||||
"47": "oklch(47.0% 0.0651 104.0)",
|
||||
"48": "oklch(48.0% 0.0663 104.0)",
|
||||
"49": "oklch(49.0% 0.0676 104.0)",
|
||||
"50": "oklch(50.0% 0.0688 104.0)",
|
||||
"51": "oklch(51.0% 0.0700 104.0)",
|
||||
"52": "oklch(52.0% 0.0713 104.0)",
|
||||
"53": "oklch(53.0% 0.0724 104.0)",
|
||||
"54": "oklch(54.0% 0.0736 104.0)",
|
||||
"55": "oklch(55.0% 0.0748 104.0)",
|
||||
"56": "oklch(56.0% 0.0759 104.0)",
|
||||
"57": "oklch(57.0% 0.0770 104.0)",
|
||||
"58": "oklch(58.0% 0.0781 104.0)",
|
||||
"59": "oklch(59.0% 0.0791 104.0)",
|
||||
"60": "oklch(60.0% 0.0801 104.0)",
|
||||
"61": "oklch(61.0% 0.0811 104.0)",
|
||||
"62": "oklch(62.0% 0.0820 104.0)",
|
||||
"63": "oklch(63.0% 0.0828 104.0)",
|
||||
"64": "oklch(64.0% 0.0836 104.0)",
|
||||
"65": "oklch(65.0% 0.0844 104.0)",
|
||||
"66": "oklch(66.0% 0.0850 104.0)",
|
||||
"67": "oklch(67.0% 0.0856 104.0)",
|
||||
"68": "oklch(68.0% 0.0861 104.0)",
|
||||
"69": "oklch(69.0% 0.0864 104.0)",
|
||||
"70": "oklch(70.0% 0.0866 104.0)",
|
||||
"71": "oklch(71.0% 0.0867 104.0)",
|
||||
"72": "oklch(72.0% 0.0866 104.0)",
|
||||
"73": "oklch(73.0% 0.0864 104.0)",
|
||||
"74": "oklch(74.0% 0.0859 104.0)",
|
||||
"75": "oklch(75.0% 0.0852 104.0)",
|
||||
"76": "oklch(76.0% 0.0842 104.0)",
|
||||
"77": "oklch(77.0% 0.0830 104.0)",
|
||||
"78": "oklch(78.0% 0.0814 104.0)",
|
||||
"79": "oklch(79.0% 0.0796 104.0)",
|
||||
"80": "oklch(80.0% 0.0776 104.0)",
|
||||
"81": "oklch(81.0% 0.0752 104.0)",
|
||||
"82": "oklch(82.0% 0.0726 104.0)",
|
||||
"83": "oklch(83.0% 0.0697 104.0)",
|
||||
"84": "oklch(84.0% 0.0666 104.0)",
|
||||
"85": "oklch(85.0% 0.0633 104.0)",
|
||||
"86": "oklch(86.0% 0.0598 104.0)",
|
||||
"87": "oklch(87.0% 0.0562 104.0)",
|
||||
"88": "oklch(88.0% 0.0524 104.0)",
|
||||
"89": "oklch(89.0% 0.0484 104.0)",
|
||||
"90": "oklch(90.0% 0.0444 104.0)",
|
||||
"91": "oklch(91.0% 0.0402 104.0)",
|
||||
"92": "oklch(92.0% 0.0360 104.0)",
|
||||
"93": "oklch(93.0% 0.0317 104.0)",
|
||||
"94": "oklch(94.0% 0.0273 104.0)",
|
||||
"95": "oklch(95.0% 0.0229 104.0)",
|
||||
"96": "oklch(96.0% 0.0184 104.0)",
|
||||
"97": "oklch(97.0% 0.0138 104.0)",
|
||||
"98": "oklch(98.0% 0.0093 104.0)"
|
||||
},
|
||||
"green": {
|
||||
"10": "oklch(10.0% 0.0158 148.0)",
|
||||
"11": "oklch(11.0% 0.0174 148.0)",
|
||||
"12": "oklch(12.0% 0.0190 148.0)",
|
||||
"13": "oklch(13.0% 0.0206 148.0)",
|
||||
"14": "oklch(14.0% 0.0221 148.0)",
|
||||
"15": "oklch(15.0% 0.0237 148.0)",
|
||||
"16": "oklch(16.0% 0.0253 148.0)",
|
||||
"17": "oklch(17.0% 0.0268 148.0)",
|
||||
"18": "oklch(18.0% 0.0284 148.0)",
|
||||
"19": "oklch(19.0% 0.0299 148.0)",
|
||||
"20": "oklch(20.0% 0.0315 148.0)",
|
||||
"21": "oklch(21.0% 0.0330 148.0)",
|
||||
"22": "oklch(22.0% 0.0346 148.0)",
|
||||
"23": "oklch(23.0% 0.0361 148.0)",
|
||||
"24": "oklch(24.0% 0.0377 148.0)",
|
||||
"25": "oklch(25.0% 0.0392 148.0)",
|
||||
"26": "oklch(26.0% 0.0407 148.0)",
|
||||
"27": "oklch(27.0% 0.0423 148.0)",
|
||||
"28": "oklch(28.0% 0.0438 148.0)",
|
||||
"29": "oklch(29.0% 0.0453 148.0)",
|
||||
"30": "oklch(30.0% 0.0468 148.0)",
|
||||
"31": "oklch(31.0% 0.0483 148.0)",
|
||||
"32": "oklch(32.0% 0.0498 148.0)",
|
||||
"33": "oklch(33.0% 0.0513 148.0)",
|
||||
"34": "oklch(34.0% 0.0528 148.0)",
|
||||
"35": "oklch(35.0% 0.0543 148.0)",
|
||||
"36": "oklch(36.0% 0.0558 148.0)",
|
||||
"37": "oklch(37.0% 0.0572 148.0)",
|
||||
"38": "oklch(38.0% 0.0587 148.0)",
|
||||
"39": "oklch(39.0% 0.0601 148.0)",
|
||||
"40": "oklch(40.0% 0.0616 148.0)",
|
||||
"41": "oklch(41.0% 0.0630 148.0)",
|
||||
"42": "oklch(42.0% 0.0644 148.0)",
|
||||
"43": "oklch(43.0% 0.0658 148.0)",
|
||||
"44": "oklch(44.0% 0.0672 148.0)",
|
||||
"45": "oklch(45.0% 0.0685 148.0)",
|
||||
"46": "oklch(46.0% 0.0699 148.0)",
|
||||
"47": "oklch(47.0% 0.0712 148.0)",
|
||||
"48": "oklch(48.0% 0.0725 148.0)",
|
||||
"49": "oklch(49.0% 0.0738 148.0)",
|
||||
"50": "oklch(50.0% 0.0750 148.0)",
|
||||
"51": "oklch(51.0% 0.0762 148.0)",
|
||||
"52": "oklch(52.0% 0.0774 148.0)",
|
||||
"53": "oklch(53.0% 0.0786 148.0)",
|
||||
"54": "oklch(54.0% 0.0797 148.0)",
|
||||
"55": "oklch(55.0% 0.0807 148.0)",
|
||||
"56": "oklch(56.0% 0.0817 148.0)",
|
||||
"57": "oklch(57.0% 0.0827 148.0)",
|
||||
"58": "oklch(58.0% 0.0835 148.0)",
|
||||
"59": "oklch(59.0% 0.0843 148.0)",
|
||||
"60": "oklch(60.0% 0.0850 148.0)",
|
||||
"61": "oklch(61.0% 0.0857 148.0)",
|
||||
"62": "oklch(62.0% 0.0862 148.0)",
|
||||
"63": "oklch(63.0% 0.0865 148.0)",
|
||||
"64": "oklch(64.0% 0.0868 148.0)",
|
||||
"65": "oklch(65.0% 0.0869 148.0)",
|
||||
"66": "oklch(66.0% 0.0868 148.0)",
|
||||
"67": "oklch(67.0% 0.0865 148.0)",
|
||||
"68": "oklch(68.0% 0.0861 148.0)",
|
||||
"69": "oklch(69.0% 0.0854 148.0)",
|
||||
"70": "oklch(70.0% 0.0845 148.0)",
|
||||
"71": "oklch(71.0% 0.0835 148.0)",
|
||||
"72": "oklch(72.0% 0.0822 148.0)",
|
||||
"73": "oklch(73.0% 0.0807 148.0)",
|
||||
"74": "oklch(74.0% 0.0790 148.0)",
|
||||
"75": "oklch(75.0% 0.0771 148.0)",
|
||||
"76": "oklch(76.0% 0.0750 148.0)",
|
||||
"77": "oklch(77.0% 0.0728 148.0)",
|
||||
"78": "oklch(78.0% 0.0704 148.0)",
|
||||
"79": "oklch(79.0% 0.0679 148.0)",
|
||||
"80": "oklch(80.0% 0.0652 148.0)",
|
||||
"81": "oklch(81.0% 0.0625 148.0)",
|
||||
"82": "oklch(82.0% 0.0596 148.0)",
|
||||
"83": "oklch(83.0% 0.0567 148.0)",
|
||||
"84": "oklch(84.0% 0.0537 148.0)",
|
||||
"85": "oklch(85.0% 0.0506 148.0)",
|
||||
"86": "oklch(86.0% 0.0475 148.0)",
|
||||
"87": "oklch(87.0% 0.0443 148.0)",
|
||||
"88": "oklch(88.0% 0.0411 148.0)",
|
||||
"89": "oklch(89.0% 0.0378 148.0)",
|
||||
"90": "oklch(90.0% 0.0345 148.0)",
|
||||
"91": "oklch(91.0% 0.0312 148.0)",
|
||||
"92": "oklch(92.0% 0.0278 148.0)",
|
||||
"93": "oklch(93.0% 0.0244 148.0)",
|
||||
"94": "oklch(94.0% 0.0210 148.0)",
|
||||
"95": "oklch(95.0% 0.0175 148.0)",
|
||||
"96": "oklch(96.0% 0.0140 148.0)",
|
||||
"97": "oklch(97.0% 0.0106 148.0)",
|
||||
"98": "oklch(98.0% 0.0071 148.0)"
|
||||
"10": "oklch(10.0% 0.0170 148.0)",
|
||||
"11": "oklch(11.0% 0.0187 148.0)",
|
||||
"12": "oklch(12.0% 0.0204 148.0)",
|
||||
"13": "oklch(13.0% 0.0221 148.0)",
|
||||
"14": "oklch(14.0% 0.0237 148.0)",
|
||||
"15": "oklch(15.0% 0.0254 148.0)",
|
||||
"16": "oklch(16.0% 0.0271 148.0)",
|
||||
"17": "oklch(17.0% 0.0288 148.0)",
|
||||
"18": "oklch(18.0% 0.0305 148.0)",
|
||||
"19": "oklch(19.0% 0.0321 148.0)",
|
||||
"20": "oklch(20.0% 0.0338 148.0)",
|
||||
"21": "oklch(21.0% 0.0355 148.0)",
|
||||
"22": "oklch(22.0% 0.0371 148.0)",
|
||||
"23": "oklch(23.0% 0.0388 148.0)",
|
||||
"24": "oklch(24.0% 0.0405 148.0)",
|
||||
"25": "oklch(25.0% 0.0421 148.0)",
|
||||
"26": "oklch(26.0% 0.0438 148.0)",
|
||||
"27": "oklch(27.0% 0.0454 148.0)",
|
||||
"28": "oklch(28.0% 0.0471 148.0)",
|
||||
"29": "oklch(29.0% 0.0487 148.0)",
|
||||
"30": "oklch(30.0% 0.0504 148.0)",
|
||||
"31": "oklch(31.0% 0.0520 148.0)",
|
||||
"32": "oklch(32.0% 0.0536 148.0)",
|
||||
"33": "oklch(33.0% 0.0553 148.0)",
|
||||
"34": "oklch(34.0% 0.0569 148.0)",
|
||||
"35": "oklch(35.0% 0.0585 148.0)",
|
||||
"36": "oklch(36.0% 0.0601 148.0)",
|
||||
"37": "oklch(37.0% 0.0617 148.0)",
|
||||
"38": "oklch(38.0% 0.0633 148.0)",
|
||||
"39": "oklch(39.0% 0.0649 148.0)",
|
||||
"40": "oklch(40.0% 0.0664 148.0)",
|
||||
"41": "oklch(41.0% 0.0680 148.0)",
|
||||
"42": "oklch(42.0% 0.0696 148.0)",
|
||||
"43": "oklch(43.0% 0.0711 148.0)",
|
||||
"44": "oklch(44.0% 0.0726 148.0)",
|
||||
"45": "oklch(45.0% 0.0742 148.0)",
|
||||
"46": "oklch(46.0% 0.0757 148.0)",
|
||||
"47": "oklch(47.0% 0.0772 148.0)",
|
||||
"48": "oklch(48.0% 0.0786 148.0)",
|
||||
"49": "oklch(49.0% 0.0801 148.0)",
|
||||
"50": "oklch(50.0% 0.0815 148.0)",
|
||||
"51": "oklch(51.0% 0.0830 148.0)",
|
||||
"52": "oklch(52.0% 0.0844 148.0)",
|
||||
"53": "oklch(53.0% 0.0857 148.0)",
|
||||
"54": "oklch(54.0% 0.0871 148.0)",
|
||||
"55": "oklch(55.0% 0.0884 148.0)",
|
||||
"56": "oklch(56.0% 0.0897 148.0)",
|
||||
"57": "oklch(57.0% 0.0909 148.0)",
|
||||
"58": "oklch(58.0% 0.0921 148.0)",
|
||||
"59": "oklch(59.0% 0.0932 148.0)",
|
||||
"60": "oklch(60.0% 0.0943 148.0)",
|
||||
"61": "oklch(61.0% 0.0953 148.0)",
|
||||
"62": "oklch(62.0% 0.0963 148.0)",
|
||||
"63": "oklch(63.0% 0.0971 148.0)",
|
||||
"64": "oklch(64.0% 0.0979 148.0)",
|
||||
"65": "oklch(65.0% 0.0986 148.0)",
|
||||
"66": "oklch(66.0% 0.0991 148.0)",
|
||||
"67": "oklch(67.0% 0.0995 148.0)",
|
||||
"68": "oklch(68.0% 0.0998 148.0)",
|
||||
"69": "oklch(69.0% 0.0999 148.0)",
|
||||
"70": "oklch(70.0% 0.0998 148.0)",
|
||||
"71": "oklch(71.0% 0.0995 148.0)",
|
||||
"72": "oklch(72.0% 0.0989 148.0)",
|
||||
"73": "oklch(73.0% 0.0981 148.0)",
|
||||
"74": "oklch(74.0% 0.0971 148.0)",
|
||||
"75": "oklch(75.0% 0.0957 148.0)",
|
||||
"76": "oklch(76.0% 0.0941 148.0)",
|
||||
"77": "oklch(77.0% 0.0921 148.0)",
|
||||
"78": "oklch(78.0% 0.0899 148.0)",
|
||||
"79": "oklch(79.0% 0.0874 148.0)",
|
||||
"80": "oklch(80.0% 0.0846 148.0)",
|
||||
"81": "oklch(81.0% 0.0816 148.0)",
|
||||
"82": "oklch(82.0% 0.0784 148.0)",
|
||||
"83": "oklch(83.0% 0.0749 148.0)",
|
||||
"84": "oklch(84.0% 0.0713 148.0)",
|
||||
"85": "oklch(85.0% 0.0675 148.0)",
|
||||
"86": "oklch(86.0% 0.0636 148.0)",
|
||||
"87": "oklch(87.0% 0.0595 148.0)",
|
||||
"88": "oklch(88.0% 0.0553 148.0)",
|
||||
"89": "oklch(89.0% 0.0511 148.0)",
|
||||
"90": "oklch(90.0% 0.0467 148.0)",
|
||||
"91": "oklch(91.0% 0.0423 148.0)",
|
||||
"92": "oklch(92.0% 0.0377 148.0)",
|
||||
"93": "oklch(93.0% 0.0332 148.0)",
|
||||
"94": "oklch(94.0% 0.0286 148.0)",
|
||||
"95": "oklch(95.0% 0.0239 148.0)",
|
||||
"96": "oklch(96.0% 0.0192 148.0)",
|
||||
"97": "oklch(97.0% 0.0144 148.0)",
|
||||
"98": "oklch(98.0% 0.0097 148.0)"
|
||||
},
|
||||
"blue": {
|
||||
"10": "oklch(10.0% 0.0285 262.0)",
|
||||
"11": "oklch(11.0% 0.0313 262.0)",
|
||||
"12": "oklch(12.0% 0.0341 262.0)",
|
||||
"11": "oklch(11.0% 0.0314 262.0)",
|
||||
"12": "oklch(12.0% 0.0342 262.0)",
|
||||
"13": "oklch(13.0% 0.0370 262.0)",
|
||||
"14": "oklch(14.0% 0.0398 262.0)",
|
||||
"15": "oklch(15.0% 0.0426 262.0)",
|
||||
"16": "oklch(16.0% 0.0454 262.0)",
|
||||
"17": "oklch(17.0% 0.0482 262.0)",
|
||||
"18": "oklch(18.0% 0.0509 262.0)",
|
||||
"19": "oklch(19.0% 0.0537 262.0)",
|
||||
"20": "oklch(20.0% 0.0565 262.0)",
|
||||
"21": "oklch(21.0% 0.0592 262.0)",
|
||||
"22": "oklch(22.0% 0.0620 262.0)",
|
||||
"23": "oklch(23.0% 0.0647 262.0)",
|
||||
"24": "oklch(24.0% 0.0674 262.0)",
|
||||
"25": "oklch(25.0% 0.0701 262.0)",
|
||||
"26": "oklch(26.0% 0.0728 262.0)",
|
||||
"27": "oklch(27.0% 0.0755 262.0)",
|
||||
"28": "oklch(28.0% 0.0782 262.0)",
|
||||
"29": "oklch(29.0% 0.0809 262.0)",
|
||||
"30": "oklch(30.0% 0.0835 262.0)",
|
||||
"31": "oklch(31.0% 0.0861 262.0)",
|
||||
"32": "oklch(32.0% 0.0887 262.0)",
|
||||
"33": "oklch(33.0% 0.0913 262.0)",
|
||||
"34": "oklch(34.0% 0.0939 262.0)",
|
||||
"35": "oklch(35.0% 0.0964 262.0)",
|
||||
"36": "oklch(36.0% 0.0990 262.0)",
|
||||
"37": "oklch(37.0% 0.1015 262.0)",
|
||||
"38": "oklch(38.0% 0.1039 262.0)",
|
||||
"39": "oklch(39.0% 0.1064 262.0)",
|
||||
"40": "oklch(40.0% 0.1088 262.0)",
|
||||
"41": "oklch(41.0% 0.1111 262.0)",
|
||||
"42": "oklch(42.0% 0.1135 262.0)",
|
||||
"43": "oklch(43.0% 0.1157 262.0)",
|
||||
"44": "oklch(44.0% 0.1180 262.0)",
|
||||
"45": "oklch(45.0% 0.1202 262.0)",
|
||||
"46": "oklch(46.0% 0.1223 262.0)",
|
||||
"47": "oklch(47.0% 0.1244 262.0)",
|
||||
"48": "oklch(48.0% 0.1264 262.0)",
|
||||
"49": "oklch(49.0% 0.1283 262.0)",
|
||||
"50": "oklch(50.0% 0.1301 262.0)",
|
||||
"51": "oklch(51.0% 0.1319 262.0)",
|
||||
"52": "oklch(52.0% 0.1336 262.0)",
|
||||
"53": "oklch(53.0% 0.1351 262.0)",
|
||||
"54": "oklch(54.0% 0.1365 262.0)",
|
||||
"55": "oklch(55.0% 0.1378 262.0)",
|
||||
"56": "oklch(56.0% 0.1390 262.0)",
|
||||
"57": "oklch(57.0% 0.1400 262.0)",
|
||||
"58": "oklch(58.0% 0.1408 262.0)",
|
||||
"59": "oklch(59.0% 0.1414 262.0)",
|
||||
"60": "oklch(60.0% 0.1419 262.0)",
|
||||
"61": "oklch(61.0% 0.1421 262.0)",
|
||||
"62": "oklch(62.0% 0.1421 262.0)",
|
||||
"63": "oklch(63.0% 0.1418 262.0)",
|
||||
"64": "oklch(64.0% 0.1413 262.0)",
|
||||
"65": "oklch(65.0% 0.1405 262.0)",
|
||||
"66": "oklch(66.0% 0.1394 262.0)",
|
||||
"67": "oklch(67.0% 0.1381 262.0)",
|
||||
"68": "oklch(68.0% 0.1365 262.0)",
|
||||
"69": "oklch(69.0% 0.1346 262.0)",
|
||||
"70": "oklch(70.0% 0.1324 262.0)",
|
||||
"71": "oklch(71.0% 0.1300 262.0)",
|
||||
"72": "oklch(72.0% 0.1273 262.0)",
|
||||
"73": "oklch(73.0% 0.1244 262.0)",
|
||||
"74": "oklch(74.0% 0.1212 262.0)",
|
||||
"75": "oklch(75.0% 0.1179 262.0)",
|
||||
"76": "oklch(76.0% 0.1144 262.0)",
|
||||
"77": "oklch(77.0% 0.1107 262.0)",
|
||||
"78": "oklch(78.0% 0.1068 262.0)",
|
||||
"79": "oklch(79.0% 0.1028 262.0)",
|
||||
"80": "oklch(80.0% 0.0986 262.0)",
|
||||
"81": "oklch(81.0% 0.0943 262.0)",
|
||||
"18": "oklch(18.0% 0.0510 262.0)",
|
||||
"19": "oklch(19.0% 0.0538 262.0)",
|
||||
"20": "oklch(20.0% 0.0566 262.0)",
|
||||
"21": "oklch(21.0% 0.0593 262.0)",
|
||||
"22": "oklch(22.0% 0.0621 262.0)",
|
||||
"23": "oklch(23.0% 0.0648 262.0)",
|
||||
"24": "oklch(24.0% 0.0676 262.0)",
|
||||
"25": "oklch(25.0% 0.0703 262.0)",
|
||||
"26": "oklch(26.0% 0.0730 262.0)",
|
||||
"27": "oklch(27.0% 0.0757 262.0)",
|
||||
"28": "oklch(28.0% 0.0784 262.0)",
|
||||
"29": "oklch(29.0% 0.0811 262.0)",
|
||||
"30": "oklch(30.0% 0.0838 262.0)",
|
||||
"31": "oklch(31.0% 0.0864 262.0)",
|
||||
"32": "oklch(32.0% 0.0891 262.0)",
|
||||
"33": "oklch(33.0% 0.0917 262.0)",
|
||||
"34": "oklch(34.0% 0.0943 262.0)",
|
||||
"35": "oklch(35.0% 0.0969 262.0)",
|
||||
"36": "oklch(36.0% 0.0994 262.0)",
|
||||
"37": "oklch(37.0% 0.1019 262.0)",
|
||||
"38": "oklch(38.0% 0.1045 262.0)",
|
||||
"39": "oklch(39.0% 0.1069 262.0)",
|
||||
"40": "oklch(40.0% 0.1094 262.0)",
|
||||
"41": "oklch(41.0% 0.1118 262.0)",
|
||||
"42": "oklch(42.0% 0.1142 262.0)",
|
||||
"43": "oklch(43.0% 0.1165 262.0)",
|
||||
"44": "oklch(44.0% 0.1188 262.0)",
|
||||
"45": "oklch(45.0% 0.1210 262.0)",
|
||||
"46": "oklch(46.0% 0.1232 262.0)",
|
||||
"47": "oklch(47.0% 0.1254 262.0)",
|
||||
"48": "oklch(48.0% 0.1274 262.0)",
|
||||
"49": "oklch(49.0% 0.1294 262.0)",
|
||||
"50": "oklch(50.0% 0.1313 262.0)",
|
||||
"51": "oklch(51.0% 0.1332 262.0)",
|
||||
"52": "oklch(52.0% 0.1349 262.0)",
|
||||
"53": "oklch(53.0% 0.1365 262.0)",
|
||||
"54": "oklch(54.0% 0.1380 262.0)",
|
||||
"55": "oklch(55.0% 0.1394 262.0)",
|
||||
"56": "oklch(56.0% 0.1407 262.0)",
|
||||
"57": "oklch(57.0% 0.1417 262.0)",
|
||||
"58": "oklch(58.0% 0.1426 262.0)",
|
||||
"59": "oklch(59.0% 0.1433 262.0)",
|
||||
"60": "oklch(60.0% 0.1438 262.0)",
|
||||
"61": "oklch(61.0% 0.1441 262.0)",
|
||||
"62": "oklch(62.0% 0.1441 262.0)",
|
||||
"63": "oklch(63.0% 0.1439 262.0)",
|
||||
"64": "oklch(64.0% 0.1434 262.0)",
|
||||
"65": "oklch(65.0% 0.1426 262.0)",
|
||||
"66": "oklch(66.0% 0.1415 262.0)",
|
||||
"67": "oklch(67.0% 0.1401 262.0)",
|
||||
"68": "oklch(68.0% 0.1384 262.0)",
|
||||
"69": "oklch(69.0% 0.1364 262.0)",
|
||||
"70": "oklch(70.0% 0.1342 262.0)",
|
||||
"71": "oklch(71.0% 0.1317 262.0)",
|
||||
"72": "oklch(72.0% 0.1289 262.0)",
|
||||
"73": "oklch(73.0% 0.1259 262.0)",
|
||||
"74": "oklch(74.0% 0.1226 262.0)",
|
||||
"75": "oklch(75.0% 0.1192 262.0)",
|
||||
"76": "oklch(76.0% 0.1155 262.0)",
|
||||
"77": "oklch(77.0% 0.1117 262.0)",
|
||||
"78": "oklch(78.0% 0.1077 262.0)",
|
||||
"79": "oklch(79.0% 0.1036 262.0)",
|
||||
"80": "oklch(80.0% 0.0994 262.0)",
|
||||
"81": "oklch(81.0% 0.0950 262.0)",
|
||||
"82": "oklch(82.0% 0.0898 262.0)",
|
||||
"83": "oklch(83.0% 0.0845 262.0)",
|
||||
"84": "oklch(84.0% 0.0792 262.0)",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
|
||||
[alpine]
|
||||
l10 = "#030303"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "alpine"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "alpine"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "badlands"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "badlands"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "chaparral"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "chaparral"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "grassland"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "grassland"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "savanna"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "savanna"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "tundra"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "tundra"
|
||||
contrast = "default"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "alpine"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "alpine"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "badlands"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "badlands"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "chaparral"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "chaparral"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "grassland"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "grassland"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "savanna"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "savanna"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "tundra"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "tundra"
|
||||
contrast = "hard"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "alpine"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "alpine"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "badlands"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "badlands"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "chaparral"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "chaparral"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "grassland"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "grassland"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "savanna"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "savanna"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "dark"
|
||||
biome = "tundra"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
mode = "light"
|
||||
biome = "tundra"
|
||||
contrast = "soft"
|
||||
version = "f{{version}}"
|
||||
# --------------------------------------------
|
||||
|
||||
# - SYS --------------------------------------
|
||||
|
||||
39
uv.lock
generated
@@ -290,6 +290,39 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imageio"
|
||||
version = "2.37.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy" },
|
||||
{ name = "pillow" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/6f/606be632e37bf8d05b253e8626c2291d74c691ddc7bcdf7d6aaf33b32f6a/imageio-2.37.2.tar.gz", hash = "sha256:0212ef2727ac9caa5ca4b2c75ae89454312f440a756fcfc8ef1993e718f50f8a", size = 389600, upload-time = "2025-11-04T14:29:39.898Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/fe/301e0936b79bcab4cacc7548bf2853fc28dced0a578bab1f7ef53c9aa75b/imageio-2.37.2-py3-none-any.whl", hash = "sha256:ad9adfb20335d718c03de457358ed69f141021a333c40a53e57273d8a5bd0b9b", size = 317646, upload-time = "2025-11-04T14:29:37.948Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
ffmpeg = [
|
||||
{ name = "imageio-ffmpeg" },
|
||||
{ name = "psutil" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imageio-ffmpeg"
|
||||
version = "0.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/44/bd/c3343c721f2a1b0c9fc71c1aebf1966a3b7f08c2eea8ed5437a2865611d6/imageio_ffmpeg-0.6.0.tar.gz", hash = "sha256:e2556bed8e005564a9f925bb7afa4002d82770d6b08825078b7697ab88ba1755", size = 25210, upload-time = "2025-01-16T21:34:32.747Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/58/87ef68ac83f4c7690961bce288fd8e382bc5f1513860fc7f90a9c1c1c6bf/imageio_ffmpeg-0.6.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.whl", hash = "sha256:9d2baaf867088508d4a3458e61eeb30e945c4ad8016025545f66c4b5aaef0a61", size = 24932969, upload-time = "2025-01-16T21:34:20.464Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/5c/f3d8a657d362cc93b81aab8feda487317da5b5d31c0e1fdfd5e986e55d17/imageio_ffmpeg-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b1ae3173414b5fc5f538a726c4e48ea97edc0d2cdc11f103afee655c463fa742", size = 21113891, upload-time = "2025-01-16T21:34:00.277Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/e7/1925bfbc563c39c1d2e82501d8372734a5c725e53ac3b31b4c2d081e895b/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1d47bebd83d2c5fc770720d211855f208af8a596c82d17730aa51e815cdee6dc", size = 25632706, upload-time = "2025-01-16T21:33:53.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/2d/43c8522a2038e9d0e7dbdf3a61195ecc31ca576fb1527a528c877e87d973/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c7e46fcec401dd990405049d2e2f475e2b397779df2519b544b8aab515195282", size = 29498237, upload-time = "2025-01-16T21:34:13.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/13/59da54728351883c3c1d9fca1710ab8eee82c7beba585df8f25ca925f08f/imageio_ffmpeg-0.6.0-py3-none-win32.whl", hash = "sha256:196faa79366b4a82f95c0f4053191d2013f4714a715780f0ad2a68ff37483cc2", size = 19652251, upload-time = "2025-01-16T21:34:06.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/c6/fa760e12a2483469e2bf5058c5faff664acf66cadb4df2ad6205b016a73d/imageio_ffmpeg-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02fa47c83703c37df6bfe4896aab339013f62bf02c5ebf2dce6da56af04ffc0a", size = 31246824, upload-time = "2025-01-16T21:34:28.6Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
@@ -589,10 +622,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "monobiome"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
version = "1.4.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "coloraide" },
|
||||
{ name = "imageio", extra = ["ffmpeg"] },
|
||||
{ name = "ipython" },
|
||||
{ name = "kaleido" },
|
||||
{ name = "matplotlib" },
|
||||
@@ -611,6 +645,7 @@ dev = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "coloraide", specifier = ">=5.1" },
|
||||
{ name = "imageio", extras = ["ffmpeg"], specifier = ">=2.37.2" },
|
||||
{ name = "ipython", specifier = ">=9.6.0" },
|
||||
{ name = "kaleido", specifier = ">=1.1.0" },
|
||||
{ name = "matplotlib", specifier = ">=3.10.7" },
|
||||
|
||||