@tokens-studio/tokenscript-schemas 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -7
- package/dist/cli/index.cjs +142 -88
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +141 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +19 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bundler/{bundle-schema.ts → build-schema.ts} +2 -2
- package/src/bundler/index.ts +25 -25
- package/src/bundler/schema-dependency-resolver.ts +3 -3
- package/src/bundler/selective-bundler.ts +3 -3
- package/src/cli/commands/build-dir.test.ts +354 -0
- package/src/cli/commands/build-dir.ts +90 -0
- package/src/cli/commands/bundle.test.ts +95 -1
- package/src/cli/commands/bundle.ts +22 -15
- package/src/cli/index.ts +16 -0
- package/bundled/functions/adjust_chroma.json +0 -60
- package/bundled/functions/adjust_hue.json +0 -60
- package/bundled/functions/adjust_lightness.json +0 -60
- package/bundled/functions/adjust_to_contrast.json +0 -67
- package/bundled/functions/alpha_blend.json +0 -31
- package/bundled/functions/alpha_scale.json +0 -27
- package/bundled/functions/analogous.json +0 -32
- package/bundled/functions/apca_contrast.json +0 -27
- package/bundled/functions/are_similar.json +0 -73
- package/bundled/functions/auto_text_color.json +0 -66
- package/bundled/functions/best_contrast.json +0 -28
- package/bundled/functions/chroma.json +0 -54
- package/bundled/functions/clamp_chroma.json +0 -66
- package/bundled/functions/clamp_lightness.json +0 -66
- package/bundled/functions/clamp_to_gamut.json +0 -23
- package/bundled/functions/complement.json +0 -24
- package/bundled/functions/contrast_ratio.json +0 -27
- package/bundled/functions/cooler.json +0 -52
- package/bundled/functions/darken.json +0 -28
- package/bundled/functions/delta_e_2000.json +0 -40
- package/bundled/functions/delta_e_76.json +0 -27
- package/bundled/functions/delta_e_ok.json +0 -27
- package/bundled/functions/desaturate.json +0 -28
- package/bundled/functions/distributed.json +0 -36
- package/bundled/functions/diverging.json +0 -36
- package/bundled/functions/grayscale.json +0 -24
- package/bundled/functions/harmonize.json +0 -65
- package/bundled/functions/hue.json +0 -54
- package/bundled/functions/hue_difference.json +0 -27
- package/bundled/functions/in_gamut.json +0 -27
- package/bundled/functions/interpolate.json +0 -66
- package/bundled/functions/invert.json +0 -23
- package/bundled/functions/is_cool.json +0 -23
- package/bundled/functions/is_dark.json +0 -27
- package/bundled/functions/is_light.json +0 -27
- package/bundled/functions/is_neutral.json +0 -65
- package/bundled/functions/is_warm.json +0 -23
- package/bundled/functions/lighten.json +0 -28
- package/bundled/functions/lightness.json +0 -61
- package/bundled/functions/luminance.json +0 -23
- package/bundled/functions/meets_contrast.json +0 -31
- package/bundled/functions/mix.json +0 -32
- package/bundled/functions/monochromatic.json +0 -28
- package/bundled/functions/muted.json +0 -59
- package/bundled/functions/neutral_variant.json +0 -59
- package/bundled/functions/relative_luminance.json +0 -61
- package/bundled/functions/rotate_hue.json +0 -28
- package/bundled/functions/saturate.json +0 -28
- package/bundled/functions/scale_chroma.json +0 -60
- package/bundled/functions/scale_lightness.json +0 -60
- package/bundled/functions/sepia.json +0 -59
- package/bundled/functions/set_chroma.json +0 -28
- package/bundled/functions/set_hue.json +0 -28
- package/bundled/functions/set_lightness.json +0 -28
- package/bundled/functions/shade_scale.json +0 -28
- package/bundled/functions/split_complement.json +0 -28
- package/bundled/functions/steps.json +0 -32
- package/bundled/functions/tetradic.json +0 -24
- package/bundled/functions/tint_scale.json +0 -36
- package/bundled/functions/to_gamut.json +0 -59
- package/bundled/functions/triadic.json +0 -24
- package/bundled/functions/vibrant.json +0 -59
- package/bundled/functions/warmer.json +0 -52
- package/bundled/functions/wcag_level.json +0 -60
- package/bundled/functions.json +0 -2624
- package/bundled/registry.json +0 -3833
- package/bundled/types/css-color.json +0 -151
- package/bundled/types/hex-color.json +0 -25
- package/bundled/types/hsl-color.json +0 -66
- package/bundled/types/hsv-color.json +0 -57
- package/bundled/types/hwb-color.json +0 -66
- package/bundled/types/lab-color.json +0 -57
- package/bundled/types/lch-color.json +0 -57
- package/bundled/types/okhsl-color.json +0 -57
- package/bundled/types/okhsv-color.json +0 -57
- package/bundled/types/oklab-color.json +0 -87
- package/bundled/types/oklch-color.json +0 -57
- package/bundled/types/p3-color.json +0 -57
- package/bundled/types/p3-linear-color.json +0 -57
- package/bundled/types/rgb-color.json +0 -73
- package/bundled/types/srgb-color.json +0 -77
- package/bundled/types/srgb-linear-color.json +0 -67
- package/bundled/types/xyz-d50-color.json +0 -57
- package/bundled/types/xyz-d65-color.json +0 -77
- package/bundled/types.json +0 -1207
package/bundled/registry.json
DELETED
|
@@ -1,3833 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "0.0.10",
|
|
3
|
-
"types": [
|
|
4
|
-
{
|
|
5
|
-
"name": "CSS",
|
|
6
|
-
"type": "color",
|
|
7
|
-
"description": "CSS color string representation. Outputs the appropriate CSS syntax for any color space.",
|
|
8
|
-
"schema": {
|
|
9
|
-
"type": "object",
|
|
10
|
-
"properties": {
|
|
11
|
-
"value": {
|
|
12
|
-
"type": "string",
|
|
13
|
-
"description": "CSS color string (e.g., 'rgb(255 128 64)', 'oklch(0.7 0.15 180)')"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"required": [
|
|
17
|
-
"value"
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
"initializers": [
|
|
21
|
-
{
|
|
22
|
-
"title": "CSS Color Initializer",
|
|
23
|
-
"keyword": "css",
|
|
24
|
-
"description": "Creates a CSS color from a string value",
|
|
25
|
-
"script": {
|
|
26
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
27
|
-
"script": "// CSS Color Initializer\n// Accepts a CSS color string and returns it\n// This is primarily used for type registration; \n// the main usage is converting TO css from other color types\n//\n// Input: String (CSS color value)\n// Output: String (same value)\n\nvariable input: List = {input};\nvariable value: String = input.get(0);\n\nreturn value;"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
],
|
|
31
|
-
"conversions": [
|
|
32
|
-
{
|
|
33
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/",
|
|
34
|
-
"target": "$self",
|
|
35
|
-
"description": "Converts RGB (0-255) to CSS rgb() syntax",
|
|
36
|
-
"lossless": true,
|
|
37
|
-
"script": {
|
|
38
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
39
|
-
"script": "// RGB to CSS Conversion\n// Converts RGB (0-255) to CSS rgb() syntax\n// CSS Color Level 4 modern syntax: rgb(r g b) or rgb(r g b / alpha)\n//\n// Input: Color.Rgb with r, g, b in 0-255 range, optional alpha (0-1)\n// Output: Color.CSS with value like \"rgb(255 128 64)\" or \"rgb(255 128 64 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable r: Number = round({input}.r);\nvariable g: Number = round({input}.g);\nvariable b: Number = round({input}.b);\n\nvariable css_value: String = \"rgb(\".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
44
|
-
"target": "$self",
|
|
45
|
-
"description": "Converts sRGB (0-1) to CSS color(srgb) syntax",
|
|
46
|
-
"lossless": true,
|
|
47
|
-
"script": {
|
|
48
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
49
|
-
"script": "// sRGB to CSS Conversion\n// Converts sRGB (0-1) to CSS color(srgb) syntax\n// CSS Color Level 4: color(srgb r g b) or color(srgb r g b / alpha)\n//\n// Input: Color.SRGB with r, g, b in 0-1 range, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(srgb 1 0.5 0.25)\" or \"color(srgb 1 0.5 0.25 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable r: Number = round({input}.r * precision) / precision;\nvariable g: Number = round({input}.g * precision) / precision;\nvariable b: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"color(srgb \".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/",
|
|
54
|
-
"target": "$self",
|
|
55
|
-
"description": "Converts HSL to CSS hsl() syntax",
|
|
56
|
-
"lossless": true,
|
|
57
|
-
"script": {
|
|
58
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
59
|
-
"script": "// HSL to CSS Conversion\n// Converts HSL to CSS hsl() syntax\n// CSS Color Level 4: hsl(h s l) or hsl(h s l / alpha) where s and l are percentages\n//\n// Input: Color.HSL with h (0-360), s (0-1), l (0-1), optional alpha (0-1)\n// Output: Color.CSS with value like \"hsl(120 50% 75%)\" or \"hsl(120 50% 75% / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100;\nvariable h: Number = round({input}.h * precision) / precision;\nvariable s: Number = round({input}.s * 100 * precision) / precision;\nvariable l: Number = round({input}.l * 100 * precision) / precision;\n\nvariable css_value: String = \"hsl(\".concat(h.to_string()).concat(\" \").concat(s.to_string()).concat(\"% \").concat(l.to_string()).concat(\"%\");\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hwb-color/0/",
|
|
64
|
-
"target": "$self",
|
|
65
|
-
"description": "Converts HWB to CSS hwb() syntax",
|
|
66
|
-
"lossless": true,
|
|
67
|
-
"script": {
|
|
68
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
69
|
-
"script": "// HWB to CSS Conversion\n// Converts HWB to CSS hwb() syntax\n// CSS Color Level 4: hwb(h w b) or hwb(h w b / alpha) where w and b are percentages\n//\n// Input: Color.HWB with h (0-360), w (0-1), b (0-1), optional alpha (0-1)\n// Output: Color.CSS with value like \"hwb(120 10% 20%)\" or \"hwb(120 10% 20% / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100;\nvariable h: Number = round({input}.h * precision) / precision;\nvariable w: Number = round({input}.w * 100 * precision) / precision;\nvariable b_val: Number = round({input}.b * 100 * precision) / precision;\n\nvariable css_value: String = \"hwb(\".concat(h.to_string()).concat(\" \").concat(w.to_string()).concat(\"% \").concat(b_val.to_string()).concat(\"%\");\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/",
|
|
74
|
-
"target": "$self",
|
|
75
|
-
"description": "Converts CIE Lab to CSS lab() syntax",
|
|
76
|
-
"lossless": true,
|
|
77
|
-
"script": {
|
|
78
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
79
|
-
"script": "// CIE Lab to CSS Conversion\n// Converts CIE Lab to CSS lab() syntax\n// CSS Color Level 4: lab(L a b) or lab(L a b / alpha) where L is a percentage\n//\n// Input: Color.Lab with l (0-100), a, b, optional alpha (0-1)\n// Output: Color.CSS with value like \"lab(75% 20 -30)\" or \"lab(75% 20 -30 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 10000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable a: Number = round({input}.a * precision) / precision;\nvariable b_val: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"lab(\".concat(l.to_string()).concat(\"% \").concat(a.to_string()).concat(\" \").concat(b_val.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lch-color/0/",
|
|
84
|
-
"target": "$self",
|
|
85
|
-
"description": "Converts CIE LCH to CSS lch() syntax",
|
|
86
|
-
"lossless": true,
|
|
87
|
-
"script": {
|
|
88
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
89
|
-
"script": "// CIE LCH to CSS Conversion\n// Converts CIE LCH to CSS lch() syntax\n// CSS Color Level 4: lch(L C H) or lch(L C H / alpha) where L is a percentage\n//\n// Input: Color.LCH with l (0-100), c, h (0-360), optional alpha (0-1)\n// Output: Color.CSS with value like \"lch(75% 50 180)\" or \"lch(75% 50 180 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 10000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable c: Number = round({input}.c * precision) / precision;\nvariable h: Number = round({input}.h * precision) / precision;\n\nvariable css_value: String = \"lch(\".concat(l.to_string()).concat(\"% \").concat(c.to_string()).concat(\" \").concat(h.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
94
|
-
"target": "$self",
|
|
95
|
-
"description": "Converts OKLab to CSS oklab() syntax",
|
|
96
|
-
"lossless": true,
|
|
97
|
-
"script": {
|
|
98
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
99
|
-
"script": "// OKLab to CSS Conversion\n// Converts OKLab to CSS oklab() syntax\n// CSS Color Level 4: oklab(L a b) or oklab(L a b / alpha) where L is 0-1 decimal\n//\n// Input: Color.OKLab with l (0-1), a, b, optional alpha (0-1)\n// Output: Color.CSS with value like \"oklab(0.7 0.1 -0.05)\" or \"oklab(0.7 0.1 -0.05 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable a: Number = round({input}.a * precision) / precision;\nvariable b_val: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"oklab(\".concat(l.to_string()).concat(\" \").concat(a.to_string()).concat(\" \").concat(b_val.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
104
|
-
"target": "$self",
|
|
105
|
-
"description": "Converts OKLCH to CSS oklch() syntax",
|
|
106
|
-
"lossless": true,
|
|
107
|
-
"script": {
|
|
108
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
109
|
-
"script": "// OKLCH to CSS Conversion\n// Converts OKLCH to CSS oklch() syntax\n// CSS Color Level 4: oklch(L C H) or oklch(L C H / alpha) where L is 0-1 decimal\n//\n// Input: Color.OKLCH with l (0-1), c, h (0-360), optional alpha (0-1)\n// Output: Color.CSS with value like \"oklch(0.7 0.15 180)\" or \"oklch(0.7 0.15 180 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable c: Number = round({input}.c * precision) / precision;\nvariable h: Number = round({input}.h * precision) / precision;\n\nvariable css_value: String = \"oklch(\".concat(l.to_string()).concat(\" \").concat(c.to_string()).concat(\" \").concat(h.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/",
|
|
114
|
-
"target": "$self",
|
|
115
|
-
"description": "Converts Linear sRGB to CSS color(srgb-linear) syntax",
|
|
116
|
-
"lossless": true,
|
|
117
|
-
"script": {
|
|
118
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
119
|
-
"script": "// Linear sRGB to CSS Conversion\n// Converts Linear sRGB to CSS color(srgb-linear) syntax\n// CSS Color Level 4: color(srgb-linear r g b)\n//\n// Input: Color.LinearSRGB with r, g, b in 0-1 range\n// Output: Color.CSS with value like \"color(srgb-linear 1 0.25 0.0625)\"\n\nvariable precision: Number = 100000;\nvariable r: Number = round({input}.r * precision) / precision;\nvariable g: Number = round({input}.g * precision) / precision;\nvariable b: Number = round({input}.b * precision) / precision;\n\nvariable output: Color.CSS;\noutput.value = \"color(srgb-linear \".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string()).concat(\")\");\nreturn output;"
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/",
|
|
124
|
-
"target": "$self",
|
|
125
|
-
"description": "Converts XYZ-D65 to CSS color(xyz-d65) syntax",
|
|
126
|
-
"lossless": true,
|
|
127
|
-
"script": {
|
|
128
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
129
|
-
"script": "// XYZ-D65 to CSS Conversion\n// Converts XYZ-D65 to CSS color(xyz-d65) syntax\n// CSS Color Level 4: color(xyz-d65 x y z) or color(xyz-d65 x y z / alpha)\n//\n// Input: Color.XYZD65 with x, y, z tristimulus values, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(xyz-d65 0.4124 0.2126 0.0193)\" or \"color(xyz-d65 0.4124 0.2126 0.0193 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable x: Number = round({input}.x * precision) / precision;\nvariable y: Number = round({input}.y * precision) / precision;\nvariable z: Number = round({input}.z * precision) / precision;\n\nvariable css_value: String = \"color(xyz-d65 \".concat(x.to_string()).concat(\" \").concat(y.to_string()).concat(\" \").concat(z.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/",
|
|
134
|
-
"target": "$self",
|
|
135
|
-
"description": "Converts XYZ-D50 to CSS color(xyz-d50) syntax",
|
|
136
|
-
"lossless": true,
|
|
137
|
-
"script": {
|
|
138
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
139
|
-
"script": "// XYZ-D50 to CSS Conversion\n// Converts XYZ-D50 to CSS color(xyz-d50) syntax\n// CSS Color Level 4: color(xyz-d50 x y z) or color(xyz-d50 x y z / alpha)\n//\n// Input: Color.XYZD50 with x, y, z tristimulus values, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(xyz-d50 0.4360 0.2225 0.0139)\" or \"color(xyz-d50 0.4360 0.2225 0.0139 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable x: Number = round({input}.x * precision) / precision;\nvariable y: Number = round({input}.y * precision) / precision;\nvariable z: Number = round({input}.z * precision) / precision;\n\nvariable css_value: String = \"color(xyz-d50 \".concat(x.to_string()).concat(\" \").concat(y.to_string()).concat(\" \").concat(z.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-color/0/",
|
|
144
|
-
"target": "$self",
|
|
145
|
-
"description": "Converts Display-P3 to CSS color(display-p3) syntax",
|
|
146
|
-
"lossless": true,
|
|
147
|
-
"script": {
|
|
148
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
149
|
-
"script": "// Display-P3 to CSS Conversion\n// Converts Display-P3 to CSS color(display-p3) syntax\n// CSS Color Level 4: color(display-p3 r g b) or color(display-p3 r g b / alpha)\n//\n// Input: Color.P3 with r, g, b in 0-1 range, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(display-p3 1 0.5 0.25)\" or \"color(display-p3 1 0.5 0.25 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable r: Number = round({input}.r * precision) / precision;\nvariable g: Number = round({input}.g * precision) / precision;\nvariable b: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"color(display-p3 \".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;"
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
],
|
|
153
|
-
"slug": "css-color"
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
"name": "Hex",
|
|
157
|
-
"type": "color",
|
|
158
|
-
"description": "A color in hex format, e.g. #ff0000",
|
|
159
|
-
"schema": {
|
|
160
|
-
"type": "object",
|
|
161
|
-
"properties": {
|
|
162
|
-
"value": {
|
|
163
|
-
"type": "string"
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
"initializers": [
|
|
168
|
-
{
|
|
169
|
-
"title": "Hex Color Initializer",
|
|
170
|
-
"keyword": "hex",
|
|
171
|
-
"script": {
|
|
172
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
173
|
-
"script": "variable c: Color.Hex;\nc.value = {input};\nreturn c;"
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
],
|
|
177
|
-
"conversions": [],
|
|
178
|
-
"slug": "hex-color"
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
"name": "HSL",
|
|
182
|
-
"type": "color",
|
|
183
|
-
"description": "HSL color space - Hue, Saturation, Lightness. Popular for color pickers and CSS.",
|
|
184
|
-
"schema": {
|
|
185
|
-
"type": "object",
|
|
186
|
-
"properties": {
|
|
187
|
-
"h": {
|
|
188
|
-
"type": "number",
|
|
189
|
-
"description": "Hue angle (0-360 degrees)"
|
|
190
|
-
},
|
|
191
|
-
"s": {
|
|
192
|
-
"type": "number",
|
|
193
|
-
"description": "Saturation (0-1)"
|
|
194
|
-
},
|
|
195
|
-
"l": {
|
|
196
|
-
"type": "number",
|
|
197
|
-
"description": "Lightness (0-1)"
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
"required": [
|
|
201
|
-
"h",
|
|
202
|
-
"s",
|
|
203
|
-
"l"
|
|
204
|
-
],
|
|
205
|
-
"order": [
|
|
206
|
-
"h",
|
|
207
|
-
"s",
|
|
208
|
-
"l"
|
|
209
|
-
],
|
|
210
|
-
"additionalProperties": false
|
|
211
|
-
},
|
|
212
|
-
"initializers": [
|
|
213
|
-
{
|
|
214
|
-
"title": "HSL Color Initializer",
|
|
215
|
-
"keyword": "hsl",
|
|
216
|
-
"description": "Creates an HSL color from H, S, L values",
|
|
217
|
-
"script": {
|
|
218
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
219
|
-
"script": "// HSL Color Initializer\n// Creates an HSL color from H, S, L values\n// Input: List of [h, s, l] or [h, s, l, alpha] values\n\nvariable hsl_values: List = {input};\nvariable output: Color.HSL;\n\noutput.h = hsl_values.get(0);\noutput.s = hsl_values.get(1);\noutput.l = hsl_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (hsl_values.length() > 3) [\n output.alpha = hsl_values.get(3);\n];\n\nreturn output;"
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
"title": "HSLA Color Initializer",
|
|
224
|
-
"keyword": "hsla",
|
|
225
|
-
"description": "Creates an HSL color with alpha from H, S, L, A values",
|
|
226
|
-
"script": {
|
|
227
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
228
|
-
"script": "// HSLA Color Initializer\n// Creates an HSL color with alpha from H, S, L, A values\n//\n// Usage: hsla(180, 0.5, 0.5, 0.8) → Color.HSL { h: 180, s: 0.5, l: 0.5, alpha: 0.8 }\n//\n// Input: List of 4 numbers [h, s, l, alpha]\n// Output: Color.HSL with alpha\n\nvariable hsl_values: List = {input};\nvariable output: Color.HSL;\n\noutput.h = hsl_values.get(0);\noutput.s = hsl_values.get(1);\noutput.l = hsl_values.get(2);\noutput.alpha = hsl_values.get(3);\n\nreturn output;"
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
],
|
|
232
|
-
"conversions": [
|
|
233
|
-
{
|
|
234
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
235
|
-
"target": "$self",
|
|
236
|
-
"description": "Converts sRGB to HSL",
|
|
237
|
-
"lossless": true,
|
|
238
|
-
"script": {
|
|
239
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
240
|
-
"script": "// sRGB to HSL Conversion\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js\n//\n// Algorithm:\n// 1. Find max and min of R, G, B\n// 2. L = (max + min) / 2\n// 3. If max == min, S = 0 (achromatic)\n// 4. Else S = (max - min) / (1 - |2L - 1|)\n// 5. H depends on which channel is max\n//\n// Input: Color.SRGB with r, g, b in 0-1 range\n// Output: Color.HSL with h (0-360), s (0-1), l (0-1)\n\n// Get input sRGB values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Find max and min\nvariable max_val: Number = r;\nif (g > max_val) [\n max_val = g;\n];\nif (b > max_val) [\n max_val = b;\n];\n\nvariable min_val: Number = r;\nif (g < min_val) [\n min_val = g;\n];\nif (b < min_val) [\n min_val = b;\n];\n\n// Calculate lightness\nvariable l: Number = (max_val + min_val) / 2;\n\n// Calculate saturation and hue\nvariable s: Number = 0;\nvariable h: Number = 0;\nvariable delta: Number = max_val - min_val;\n\nif (delta > 0) [\n // Not achromatic\n \n // Saturation formula\n variable abs_2l_minus_1: Number = 2 * l - 1;\n if (abs_2l_minus_1 < 0) [\n abs_2l_minus_1 = 0 - abs_2l_minus_1;\n ];\n s = delta / (1 - abs_2l_minus_1);\n \n // Hue calculation depends on which channel is max\n if (max_val == r) [\n h = ((g - b) / delta);\n if (g < b) [\n h = h + 6;\n ];\n ] else [\n if (max_val == g) [\n h = (b - r) / delta + 2;\n ] else [\n h = (r - g) / delta + 4;\n ];\n ];\n \n // Convert to degrees\n h = h * 60;\n];\n\n// Normalize hue to 0-360\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.HSL;\noutput.h = h;\noutput.s = s;\noutput.l = l;\n\nreturn output;"
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
],
|
|
244
|
-
"slug": "hsl-color"
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
"name": "HSV",
|
|
248
|
-
"type": "color",
|
|
249
|
-
"description": "HSV color space - Hue, Saturation, Value. Also known as HSB (Hue, Saturation, Brightness).",
|
|
250
|
-
"schema": {
|
|
251
|
-
"type": "object",
|
|
252
|
-
"properties": {
|
|
253
|
-
"h": {
|
|
254
|
-
"type": "number",
|
|
255
|
-
"description": "Hue angle (0-360 degrees)"
|
|
256
|
-
},
|
|
257
|
-
"s": {
|
|
258
|
-
"type": "number",
|
|
259
|
-
"description": "Saturation (0-1)"
|
|
260
|
-
},
|
|
261
|
-
"v": {
|
|
262
|
-
"type": "number",
|
|
263
|
-
"description": "Value/Brightness (0-1)"
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
"required": [
|
|
267
|
-
"h",
|
|
268
|
-
"s",
|
|
269
|
-
"v"
|
|
270
|
-
],
|
|
271
|
-
"order": [
|
|
272
|
-
"h",
|
|
273
|
-
"s",
|
|
274
|
-
"v"
|
|
275
|
-
],
|
|
276
|
-
"additionalProperties": false
|
|
277
|
-
},
|
|
278
|
-
"initializers": [
|
|
279
|
-
{
|
|
280
|
-
"title": "HSV Color Initializer",
|
|
281
|
-
"keyword": "hsv",
|
|
282
|
-
"description": "Creates an HSV color from H, S, V values",
|
|
283
|
-
"script": {
|
|
284
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
285
|
-
"script": "// HSV Color Initializer\n// Creates an HSV color from H, S, V values\n// Input: List of [h, s, v] or [h, s, v, alpha] values\n\nvariable hsv_values: List = {input};\nvariable output: Color.HSV;\n\noutput.h = hsv_values.get(0);\noutput.s = hsv_values.get(1);\noutput.v = hsv_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (hsv_values.length() > 3) [\n output.alpha = hsv_values.get(3);\n];\n\nreturn output;"
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
],
|
|
289
|
-
"conversions": [
|
|
290
|
-
{
|
|
291
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
292
|
-
"target": "$self",
|
|
293
|
-
"description": "Converts sRGB to HSV",
|
|
294
|
-
"lossless": true,
|
|
295
|
-
"script": {
|
|
296
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
297
|
-
"script": "// sRGB to HSV Conversion\n// Reference: Standard RGB to HSV algorithm\n//\n// Algorithm:\n// V = max(R, G, B)\n// S = (V - min(R, G, B)) / V (if V > 0)\n// H depends on which channel is max\n//\n// Input: Color.SRGB with r, g, b in 0-1 range\n// Output: Color.HSV with h (0-360), s (0-1), v (0-1)\n\n// Get input sRGB values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Find max and min\nvariable max_val: Number = r;\nif (g > max_val) [\n max_val = g;\n];\nif (b > max_val) [\n max_val = b;\n];\n\nvariable min_val: Number = r;\nif (g < min_val) [\n min_val = g;\n];\nif (b < min_val) [\n min_val = b;\n];\n\n// Value is the max channel\nvariable v: Number = max_val;\n\n// Calculate saturation and hue\nvariable s: Number = 0;\nvariable h: Number = 0;\nvariable delta: Number = max_val - min_val;\n\nif (max_val > 0) [\n s = delta / max_val;\n];\n\nif (delta > 0) [\n // Hue calculation depends on which channel is max\n if (max_val == r) [\n h = ((g - b) / delta);\n if (g < b) [\n h = h + 6;\n ];\n ] else [\n if (max_val == g) [\n h = (b - r) / delta + 2;\n ] else [\n h = (r - g) / delta + 4;\n ];\n ];\n \n // Convert to degrees\n h = h * 60;\n];\n\n// Normalize hue to 0-360\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.HSV;\noutput.h = h;\noutput.s = s;\noutput.v = v;\n\nreturn output;"
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
],
|
|
301
|
-
"slug": "hsv-color"
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
"name": "HWB",
|
|
305
|
-
"type": "color",
|
|
306
|
-
"description": "HWB color space - Hue, Whiteness, Blackness. CSS Color Level 4.",
|
|
307
|
-
"schema": {
|
|
308
|
-
"type": "object",
|
|
309
|
-
"properties": {
|
|
310
|
-
"h": {
|
|
311
|
-
"type": "number",
|
|
312
|
-
"description": "Hue angle (0-360 degrees)"
|
|
313
|
-
},
|
|
314
|
-
"w": {
|
|
315
|
-
"type": "number",
|
|
316
|
-
"description": "Whiteness (0-1)"
|
|
317
|
-
},
|
|
318
|
-
"b": {
|
|
319
|
-
"type": "number",
|
|
320
|
-
"description": "Blackness (0-1)"
|
|
321
|
-
}
|
|
322
|
-
},
|
|
323
|
-
"required": [
|
|
324
|
-
"h",
|
|
325
|
-
"w",
|
|
326
|
-
"b"
|
|
327
|
-
],
|
|
328
|
-
"order": [
|
|
329
|
-
"h",
|
|
330
|
-
"w",
|
|
331
|
-
"b"
|
|
332
|
-
],
|
|
333
|
-
"additionalProperties": false
|
|
334
|
-
},
|
|
335
|
-
"initializers": [
|
|
336
|
-
{
|
|
337
|
-
"title": "HWB Color Initializer",
|
|
338
|
-
"keyword": "hwb",
|
|
339
|
-
"description": "Creates an HWB color from H, W, B values",
|
|
340
|
-
"script": {
|
|
341
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
342
|
-
"script": "// HWB Color Initializer\n// Creates an HWB color from H, W, B values\n// Input: List of [h, w, b] or [h, w, b, alpha] values\n\nvariable hwb_values: List = {input};\nvariable output: Color.HWB;\n\noutput.h = hwb_values.get(0);\noutput.w = hwb_values.get(1);\noutput.b = hwb_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (hwb_values.length() > 3) [\n output.alpha = hwb_values.get(3);\n];\n\nreturn output;"
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
"title": "HWBA Color Initializer",
|
|
347
|
-
"keyword": "hwba",
|
|
348
|
-
"description": "Creates an HWB color with alpha from H, W, B, A values",
|
|
349
|
-
"script": {
|
|
350
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
351
|
-
"script": "// HWBA Color Initializer\n// Creates an HWB color with alpha from H, W, B, A values\n//\n// Usage: hwba(180, 0.2, 0.3, 0.9) → Color.HWB { h: 180, w: 0.2, b: 0.3, alpha: 0.9 }\n//\n// Input: List of 4 numbers [h, w, b, alpha]\n// Output: Color.HWB with alpha\n\nvariable hwb_values: List = {input};\nvariable output: Color.HWB;\n\noutput.h = hwb_values.get(0);\noutput.w = hwb_values.get(1);\noutput.b = hwb_values.get(2);\noutput.alpha = hwb_values.get(3);\n\nreturn output;"
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
],
|
|
355
|
-
"conversions": [
|
|
356
|
-
{
|
|
357
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsv-color/0/",
|
|
358
|
-
"target": "$self",
|
|
359
|
-
"description": "Converts HSV to HWB",
|
|
360
|
-
"lossless": true,
|
|
361
|
-
"script": {
|
|
362
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
363
|
-
"script": "// HSV to HWB Conversion\n// Reference: CSS Color Level 4\n//\n// Algorithm:\n// H stays the same\n// W = (1 - S) * V (Whiteness)\n// B = 1 - V (Blackness)\n//\n// Input: Color.HSV with h, s, v values\n// Output: Color.HWB with h, w, b values\n\n// Get input HSV values\nvariable h: Number = {input}.h;\nvariable s: Number = {input}.s;\nvariable v: Number = {input}.v;\n\n// Calculate whiteness and blackness\nvariable w: Number = (1 - s) * v;\nvariable b: Number = 1 - v;\n\n// Create output\nvariable output: Color.HWB;\noutput.h = h;\noutput.w = w;\noutput.b = b;\n\nreturn output;"
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
],
|
|
367
|
-
"slug": "hwb-color"
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
"name": "Lab",
|
|
371
|
-
"type": "color",
|
|
372
|
-
"description": "CIE Lab (L*a*b*) perceptually uniform color space. L is lightness (0-100), a is green-red axis, b is blue-yellow axis.",
|
|
373
|
-
"schema": {
|
|
374
|
-
"type": "object",
|
|
375
|
-
"properties": {
|
|
376
|
-
"l": {
|
|
377
|
-
"type": "number",
|
|
378
|
-
"description": "Lightness (0-100)"
|
|
379
|
-
},
|
|
380
|
-
"a": {
|
|
381
|
-
"type": "number",
|
|
382
|
-
"description": "Green-red axis (typically -125 to 125)"
|
|
383
|
-
},
|
|
384
|
-
"b": {
|
|
385
|
-
"type": "number",
|
|
386
|
-
"description": "Blue-yellow axis (typically -125 to 125)"
|
|
387
|
-
}
|
|
388
|
-
},
|
|
389
|
-
"required": [
|
|
390
|
-
"l",
|
|
391
|
-
"a",
|
|
392
|
-
"b"
|
|
393
|
-
],
|
|
394
|
-
"order": [
|
|
395
|
-
"l",
|
|
396
|
-
"a",
|
|
397
|
-
"b"
|
|
398
|
-
],
|
|
399
|
-
"additionalProperties": false
|
|
400
|
-
},
|
|
401
|
-
"initializers": [
|
|
402
|
-
{
|
|
403
|
-
"title": "Lab Color Initializer",
|
|
404
|
-
"keyword": "lab",
|
|
405
|
-
"description": "Creates a CIE Lab color from L, a, b values",
|
|
406
|
-
"script": {
|
|
407
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
408
|
-
"script": "// CIE Lab Color Initializer\n// Creates a Lab color from L, a, b values\n// Input: List of [l, a, b] or [l, a, b, alpha] values\n\nvariable lab_values: List = {input};\nvariable output: Color.Lab;\n\noutput.l = lab_values.get(0);\noutput.a = lab_values.get(1);\noutput.b = lab_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lab_values.length() > 3) [\n output.alpha = lab_values.get(3);\n];\n\nreturn output;"
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
],
|
|
412
|
-
"conversions": [
|
|
413
|
-
{
|
|
414
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/",
|
|
415
|
-
"target": "$self",
|
|
416
|
-
"description": "Converts XYZ-D50 to CIE Lab using the CIE standard formula",
|
|
417
|
-
"lossless": true,
|
|
418
|
-
"script": {
|
|
419
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
420
|
-
"script": "// XYZ-D50 to CIE Lab Conversion\n// Reference: CIE 15.3:2004 section 8.2.1.1\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/lab.js\n//\n// Algorithm:\n// 1. Scale XYZ by D50 white point reference\n// 2. Apply f function: f(x) = x > ε ? cbrt(x) : (κ*x + 16)/116\n// 3. L = 116 * f[Y] - 16\n// 4. a = 500 * (f[X] - f[Y])\n// 5. b = 200 * (f[Y] - f[Z])\n//\n// Input: Color.XYZD50 with x, y, z tristimulus\n// Output: Color.Lab with l, a, b coordinates\n\n// Get input XYZ-D50 values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// CIE constants (exact rational fractions)\n// ε = 216/24389 = (6/29)^3\nvariable epsilon: Number = 0.008856451679035631;\n// κ = 24389/27 = (29/3)^3 \nvariable kappa: Number = 903.2962962962963;\n\n// D50 white point reference (ColorJS exact values)\n// D50: [0.3457/0.3585, 1.0, (1-0.3457-0.3585)/0.3585]\nvariable white_x: Number = 0.9642956764295677;\nvariable white_y: Number = 1.0;\nvariable white_z: Number = 0.8251046025104602;\n\n// Scale XYZ by white point\nvariable xr: Number = x / white_x;\nvariable yr: Number = y / white_y;\nvariable zr: Number = z / white_z;\n\n// Apply f function with cube root\n// f(t) = t > ε ? t^(1/3) : (κt + 16) / 116\nvariable cube_root_exp: Number = 0.3333333333333333;\n\nvariable fx: Number = 0;\nvariable fy: Number = 0;\nvariable fz: Number = 0;\n\nif (xr > epsilon) [\n fx = pow(xr, cube_root_exp);\n] else [\n fx = (kappa * xr + 16) / 116;\n];\n\nif (yr > epsilon) [\n fy = pow(yr, cube_root_exp);\n] else [\n fy = (kappa * yr + 16) / 116;\n];\n\nif (zr > epsilon) [\n fz = pow(zr, cube_root_exp);\n] else [\n fz = (kappa * zr + 16) / 116;\n];\n\n// Calculate Lab values\nvariable lab_l: Number = 116 * fy - 16;\nvariable lab_a: Number = 500 * (fx - fy);\nvariable lab_b: Number = 200 * (fy - fz);\n\n// Create output\nvariable output: Color.Lab;\noutput.l = lab_l;\noutput.a = lab_a;\noutput.b = lab_b;\n\nreturn output;"
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
],
|
|
424
|
-
"slug": "lab-color"
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
"name": "LCH",
|
|
428
|
-
"type": "color",
|
|
429
|
-
"description": "CIE LCH color space - the polar form of CIE Lab. L is lightness (0-100), C is chroma, H is hue angle (0-360).",
|
|
430
|
-
"schema": {
|
|
431
|
-
"type": "object",
|
|
432
|
-
"properties": {
|
|
433
|
-
"l": {
|
|
434
|
-
"type": "number",
|
|
435
|
-
"description": "Lightness (0-100)"
|
|
436
|
-
},
|
|
437
|
-
"c": {
|
|
438
|
-
"type": "number",
|
|
439
|
-
"description": "Chroma"
|
|
440
|
-
},
|
|
441
|
-
"h": {
|
|
442
|
-
"type": "number",
|
|
443
|
-
"description": "Hue angle (0-360 degrees)"
|
|
444
|
-
}
|
|
445
|
-
},
|
|
446
|
-
"required": [
|
|
447
|
-
"l",
|
|
448
|
-
"c",
|
|
449
|
-
"h"
|
|
450
|
-
],
|
|
451
|
-
"order": [
|
|
452
|
-
"l",
|
|
453
|
-
"c",
|
|
454
|
-
"h"
|
|
455
|
-
],
|
|
456
|
-
"additionalProperties": false
|
|
457
|
-
},
|
|
458
|
-
"initializers": [
|
|
459
|
-
{
|
|
460
|
-
"title": "LCH Color Initializer",
|
|
461
|
-
"keyword": "lch",
|
|
462
|
-
"description": "Creates a CIE LCH color from L, C, H values",
|
|
463
|
-
"script": {
|
|
464
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
465
|
-
"script": "// CIE LCH Color Initializer\n// Creates a LCH color from L, C, H values\n// Input: List of [l, c, h] or [l, c, h, alpha] values\n\nvariable lch_values: List = {input};\nvariable output: Color.LCH;\n\noutput.l = lch_values.get(0);\noutput.c = lch_values.get(1);\noutput.h = lch_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lch_values.length() > 3) [\n output.alpha = lch_values.get(3);\n];\n\nreturn output;"
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
],
|
|
469
|
-
"conversions": [
|
|
470
|
-
{
|
|
471
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/",
|
|
472
|
-
"target": "$self",
|
|
473
|
-
"description": "Converts CIE Lab to LCH using Cartesian to polar transformation",
|
|
474
|
-
"lossless": true,
|
|
475
|
-
"script": {
|
|
476
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
477
|
-
"script": "// CIE Lab to LCH Conversion\n// Converts Cartesian (a, b) to polar (C, H) coordinates\n// Same algorithm as OKLab → OKLCH, but for CIE Lab\n//\n// Algorithm:\n// L stays the same (lightness 0-100)\n// C = sqrt(a² + b²) (chroma)\n// H = atan2(b, a) * 180/π (hue angle in degrees)\n//\n// Input: Color.Lab with l, a, b coordinates\n// Output: Color.LCH with l, c, h coordinates\n\n// Get input Lab values\nvariable l: Number = {input}.l;\nvariable a: Number = {input}.a;\nvariable b: Number = {input}.b;\n\n// Constants\nvariable pi: Number = pi();\nvariable rad_to_deg: Number = 180 / pi;\n\n// Calculate chroma (distance from neutral axis)\nvariable c: Number = sqrt(a * a + b * b);\n\n// Calculate hue angle using atan2\nvariable h_rad: Number = atan2(b, a);\nvariable h: Number = h_rad * rad_to_deg;\n\n// Normalize hue to 0-360 range\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.LCH;\noutput.l = l;\noutput.c = c;\noutput.h = h;\n\nreturn output;"
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
],
|
|
481
|
-
"slug": "lch-color"
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
"name": "OKHSL",
|
|
485
|
-
"type": "color",
|
|
486
|
-
"description": "OKHSL color space by Björn Ottosson - a perceptually uniform HSL based on OKLab. H is hue (0-360), S is saturation (0-1, normalized to sRGB gamut), L is lightness (0-1). Reference: https://bottosson.github.io/posts/colorpicker/",
|
|
487
|
-
"schema": {
|
|
488
|
-
"type": "object",
|
|
489
|
-
"properties": {
|
|
490
|
-
"h": {
|
|
491
|
-
"type": "number",
|
|
492
|
-
"description": "Hue angle (0-360 degrees), same as OKLCH hue"
|
|
493
|
-
},
|
|
494
|
-
"s": {
|
|
495
|
-
"type": "number",
|
|
496
|
-
"description": "Saturation (0-1), normalized relative to sRGB gamut boundary"
|
|
497
|
-
},
|
|
498
|
-
"l": {
|
|
499
|
-
"type": "number",
|
|
500
|
-
"description": "Lightness (0-1), perceptually uniform with toe function applied"
|
|
501
|
-
}
|
|
502
|
-
},
|
|
503
|
-
"required": [
|
|
504
|
-
"h",
|
|
505
|
-
"s",
|
|
506
|
-
"l"
|
|
507
|
-
],
|
|
508
|
-
"order": [
|
|
509
|
-
"h",
|
|
510
|
-
"s",
|
|
511
|
-
"l"
|
|
512
|
-
],
|
|
513
|
-
"additionalProperties": false
|
|
514
|
-
},
|
|
515
|
-
"initializers": [
|
|
516
|
-
{
|
|
517
|
-
"title": "OKHSL Color Initializer",
|
|
518
|
-
"keyword": "okhsl",
|
|
519
|
-
"description": "Creates an OKHSL color from H, S, L values",
|
|
520
|
-
"script": {
|
|
521
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
522
|
-
"script": "// OKHSL Color Initializer\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n//\n// Creates an OKHSL color from hue, saturation, and lightness values.\n// OKHSL is a perceptually uniform version of HSL built on OKLab.\n//\n// Parameters:\n// - h: Hue angle (0-360 degrees), same as OKLCH hue\n// - s: Saturation (0-1), normalized to sRGB gamut boundary\n// - l: Lightness (0-1), perceptually uniform with toe function\n// - alpha: Optional alpha channel (0-1)\n//\n// Input: Object with h, s, l, and optional alpha properties\n// Output: Color.OKHSL\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable l: Number = input.l;\n\nvariable output: Color.OKHSL;\noutput.h = h;\noutput.s = s;\noutput.l = l;\n\n// Set alpha if provided\nif (input.alpha != null) [\n output.alpha = input.alpha;\n];\n\noutput"
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
],
|
|
526
|
-
"conversions": [
|
|
527
|
-
{
|
|
528
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
529
|
-
"target": "$self",
|
|
530
|
-
"description": "Converts OKLab to OKHSL using Ottosson's algorithm with Halley's method refinement for gamut boundary",
|
|
531
|
-
"lossless": true,
|
|
532
|
-
"script": {
|
|
533
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
534
|
-
"script": "// OKLab to OKHSL Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsl.js\n// Reference: https://bottosson.github.io/posts/oklab/ (original OKLab paper)\n//\n// OKHSL provides a perceptually uniform HSL with proper saturation scaling.\n// The algorithm uses three key chroma values (C_0, C_mid, C_max) and\n// performs piecewise interpolation to map chroma to saturation.\n//\n// This implementation includes the FULL findGamutIntersection algorithm\n// with Halley's method refinement for accurate gamut boundary detection.\n//\n// Input: Color.OKLab with l (0-1), a, b coordinates\n// Output: Color.OKHSL with h (0-360), s (0-1), l (0-1)\n\nvariable lab_l: Number = input.l;\nvariable lab_a: Number = input.a;\nvariable lab_b: Number = input.b;\n\n// Native constants\nvariable pi_val: Number = pi();\nvariable float_max: Number = 999999999;\n\n// Toe function constants\n// K3 = (1 + K1) / (1 + K2)\nvariable toe_k1: Number = 0.206;\nvariable toe_k2: Number = 0.03;\nvariable toe_k3: Number = 1.17009708737864;\n\n// LMS to OKLab matrix coefficients (LabtoLMS_M columns 1,2)\nvariable lab_lms_kl_a: Number = 0.3963377774;\nvariable lab_lms_kl_b: Number = 0.2158037573;\nvariable lab_lms_km_a: Number = -0.1055613458;\nvariable lab_lms_km_b: Number = -0.0638541728;\nvariable lab_lms_ks_a: Number = -0.0894841775;\nvariable lab_lms_ks_b: Number = -1.2914855480;\n\n// LMS to sRGB-linear matrix\nvariable lms_r0: Number = 4.0767416360759583;\nvariable lms_r1: Number = -3.3077115392580629;\nvariable lms_r2: Number = 0.2309699031821043;\nvariable lms_g0: Number = -1.2684379732850315;\nvariable lms_g1: Number = 2.6097573492876882;\nvariable lms_g2: Number = -0.3413193760026570;\nvariable lms_b0: Number = -0.0041960761386756;\nvariable lms_b1: Number = -0.7034186179359362;\nvariable lms_b2: Number = 1.7076146940746117;\n\n// RGB limit coefficients for determining which channel clips first\nvariable red_limit_a: Number = -1.8817031;\nvariable red_limit_b: Number = -0.80936501;\nvariable green_limit_a: Number = 1.8144408;\nvariable green_limit_b: Number = -1.19445267;\n\n// Red Kn coefficients\nvariable red_k0: Number = 1.19086277;\nvariable red_k1: Number = 1.76576728;\nvariable red_k2: Number = 0.59662641;\nvariable red_k3: Number = 0.75515197;\nvariable red_k4: Number = 0.56771245;\n\n// Green Kn coefficients\nvariable green_k0: Number = 0.73956515;\nvariable green_k1: Number = -0.45954404;\nvariable green_k2: Number = 0.08285427;\nvariable green_k3: Number = 0.12541073;\nvariable green_k4: Number = -0.14503204;\n\n// Blue Kn coefficients\nvariable blue_k0: Number = 1.35733652;\nvariable blue_k1: Number = -0.00915799;\nvariable blue_k2: Number = -1.1513021;\nvariable blue_k3: Number = -0.50559606;\nvariable blue_k4: Number = 0.00692167;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Convert to polar coordinates and apply toe function\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = sqrt(lab_a * lab_a + lab_b * lab_b);\n\n// Apply toe function: toe(x) = 0.5 * (k3*x - k1 + sqrt((k3*x - k1)^2 + 4*k2*k3*x))\nvariable l: Number = lab_l;\nif (lab_l > 0.0001 && lab_l < 0.9999) [\n variable term: Number = toe_k3 * lab_l - toe_k1;\n l = 0.5 * (term + sqrt(term * term + 4 * toe_k2 * toe_k3 * lab_l));\n];\n\n// Compute hue using atan2(-b, -a) + 0.5 (ColorJS convention)\nvariable h: Number = 0;\nif (c > 0.00001) [\n variable h_normalized: Number = 0.5 + atan2(-lab_b, -lab_a) / (2 * pi_val);\n h = h_normalized * 360;\n if (h < 0) [ h = h + 360; ];\n if (h >= 360) [ h = h - 360; ];\n];\n\n// Initialize saturation\nvariable s: Number = 0;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Check if chromatic (non-achromatic, non-edge)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable is_chromatic: Number = 1;\nif (c < 0.00001) [ is_chromatic = 0; ];\nif (l < 0.0001) [ is_chromatic = 0; ];\nif (l > 0.9999) [ is_chromatic = 0; ];\n\nif (is_chromatic > 0.5) [\n // ═══════════════════════════════════════════════════════════════════════\n // Step 3: Normalize hue direction (a_, b_)\n // ═══════════════════════════════════════════════════════════════════════\n variable a_: Number = lab_a / c;\n variable b_: Number = lab_b / c;\n \n // Pre-compute LMS coefficients\n variable kl: Number = lab_lms_kl_a * a_ + lab_lms_kl_b * b_;\n variable km: Number = lab_lms_km_a * a_ + lab_lms_km_b * b_;\n variable ks: Number = lab_lms_ks_a * a_ + lab_lms_ks_b * b_;\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 4: computeMaxSaturation - Find S_max for this hue\n // ═══════════════════════════════════════════════════════════════════════\n variable test_red: Number = red_limit_a * a_ + red_limit_b * b_;\n variable test_green: Number = green_limit_a * a_ + green_limit_b * b_;\n \n variable k0: Number = blue_k0;\n variable k1_coef: Number = blue_k1;\n variable k2_coef: Number = blue_k2;\n variable k3_coef: Number = blue_k3;\n variable k4_coef: Number = blue_k4;\n variable wl: Number = lms_b0;\n variable wm: Number = lms_b1;\n variable ws: Number = lms_b2;\n \n if (test_red > 1) [\n k0 = red_k0; k1_coef = red_k1; k2_coef = red_k2; k3_coef = red_k3; k4_coef = red_k4;\n wl = lms_r0; wm = lms_r1; ws = lms_r2;\n ] else [\n if (test_green > 1) [\n k0 = green_k0; k1_coef = green_k1; k2_coef = green_k2; k3_coef = green_k3; k4_coef = green_k4;\n wl = lms_g0; wm = lms_g1; ws = lms_g2;\n ];\n ];\n \n // Polynomial approximation\n variable s_max: Number = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement for s_max\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable halley_denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(halley_denom) > 0.000001) [\n s_max = s_max - f * f1 / halley_denom;\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 5: findCusp - Get L_cusp and C_cusp\n // ═══════════════════════════════════════════════════════════════════════\n variable l_cusp: Number = 1;\n variable c_cusp: Number = 0;\n \n if (s_max > 0) [\n variable cusp_l_: Number = 1 + s_max * kl;\n variable cusp_m_: Number = 1 + s_max * km;\n variable cusp_s_: Number = 1 + s_max * ks;\n \n variable cusp_l: Number = cusp_l_ * cusp_l_ * cusp_l_;\n variable cusp_m: Number = cusp_m_ * cusp_m_ * cusp_m_;\n variable cusp_s: Number = cusp_s_ * cusp_s_ * cusp_s_;\n \n variable cusp_r: Number = lms_r0 * cusp_l + lms_r1 * cusp_m + lms_r2 * cusp_s;\n variable cusp_g: Number = lms_g0 * cusp_l + lms_g1 * cusp_m + lms_g2 * cusp_s;\n variable cusp_b: Number = lms_b0 * cusp_l + lms_b1 * cusp_m + lms_b2 * cusp_s;\n \n variable max_rgb: Number = cusp_r;\n if (cusp_g > max_rgb) [ max_rgb = cusp_g; ];\n if (cusp_b > max_rgb) [ max_rgb = cusp_b; ];\n if (max_rgb < 0.0001) [ max_rgb = 0.0001; ];\n \n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 6: findGamutIntersection - Find C_max at current L\n // This is the FULL algorithm with Halley's method for upper half\n //\n // Finds intersection of line: L = L0*(1-t) + t*L1, C = t*C1\n // where L0=L1=lab_l and C1=1 (seeking max chroma at this L)\n // ═══════════════════════════════════════════════════════════════════════\n variable c_max: Number = 0;\n \n if (l_cusp > 0.0001 && c_cusp > 0.0001) [\n // Parameters for gamut intersection: l1=lab_l, c1=1, l0=lab_l\n variable l1: Number = lab_l;\n variable c1: Number = 1;\n variable l0: Number = lab_l;\n \n // Check which half: (l1 - l0) * c_cusp - (l_cusp - l0) * c1\n // Since l1 = l0 = lab_l, this simplifies to: -(l_cusp - lab_l) * 1 = lab_l - l_cusp\n variable half_test: Number = lab_l - l_cusp;\n variable t_intersect: Number = 0;\n \n if (half_test <= 0) [\n // Lower half (below cusp) - simple triangle intersection\n // t = (c_cusp * l0) / (c1 * l_cusp + c_cusp * (l0 - l1))\n // Since l0 = l1, denominator = c1 * l_cusp = l_cusp\n variable lower_denom: Number = l_cusp;\n if (abs(lower_denom) > 0.00001) [\n t_intersect = (c_cusp * lab_l) / lower_denom;\n ];\n ] else [\n // Upper half (above cusp) - triangle + Halley's method\n // First: triangle intersection\n // t = (c_cusp * (l0 - 1)) / (c1 * (l_cusp - 1) + c_cusp * (l0 - l1))\n // Since l0 = l1 = lab_l: t = (c_cusp * (lab_l - 1)) / (l_cusp - 1)\n variable upper_denom: Number = l_cusp - 1;\n if (abs(upper_denom) > 0.00001) [\n t_intersect = c_cusp * (lab_l - 1) / upper_denom;\n ];\n \n // Halley's method refinement for upper half\n // dl = l1 - l0 = 0, dc = c1 = 1\n variable dl: Number = 0;\n variable dc: Number = 1;\n \n variable ldt_: Number = dl + dc * kl;\n variable mdt_: Number = dl + dc * km;\n variable sdt_: Number = dl + dc * ks;\n \n // Compute L and C at current t\n variable L_at_t: Number = l0 * (1 - t_intersect) + t_intersect * l1;\n variable C_at_t: Number = t_intersect * c1;\n \n // LMS values at (L, C)\n variable l_at: Number = L_at_t + C_at_t * kl;\n variable m_at: Number = L_at_t + C_at_t * km;\n variable s_at: Number = L_at_t + C_at_t * ks;\n \n variable l_lms: Number = l_at * l_at * l_at;\n variable m_lms: Number = m_at * m_at * m_at;\n variable s_lms: Number = s_at * s_at * s_at;\n \n // First derivatives\n variable ldt: Number = 3 * ldt_ * l_at * l_at;\n variable mdt: Number = 3 * mdt_ * m_at * m_at;\n variable sdt: Number = 3 * sdt_ * s_at * s_at;\n \n // Second derivatives\n variable ldt2: Number = 6 * ldt_ * ldt_ * l_at;\n variable mdt2: Number = 6 * mdt_ * mdt_ * m_at;\n variable sdt2: Number = 6 * sdt_ * sdt_ * s_at;\n \n // Red channel Halley step\n variable r_val: Number = lms_r0 * l_lms + lms_r1 * m_lms + lms_r2 * s_lms - 1;\n variable r1: Number = lms_r0 * ldt + lms_r1 * mdt + lms_r2 * sdt;\n variable r2: Number = lms_r0 * ldt2 + lms_r1 * mdt2 + lms_r2 * sdt2;\n variable r_denom: Number = r1 * r1 - 0.5 * r_val * r2;\n variable ur: Number = 0;\n variable tr: Number = float_max;\n if (abs(r_denom) > 0.000001) [\n ur = r1 / r_denom;\n if (ur >= 0) [ tr = -r_val * ur; ];\n ];\n \n // Green channel Halley step\n variable g_val: Number = lms_g0 * l_lms + lms_g1 * m_lms + lms_g2 * s_lms - 1;\n variable g1: Number = lms_g0 * ldt + lms_g1 * mdt + lms_g2 * sdt;\n variable g2: Number = lms_g0 * ldt2 + lms_g1 * mdt2 + lms_g2 * sdt2;\n variable g_denom: Number = g1 * g1 - 0.5 * g_val * g2;\n variable ug: Number = 0;\n variable tg: Number = float_max;\n if (abs(g_denom) > 0.000001) [\n ug = g1 / g_denom;\n if (ug >= 0) [ tg = -g_val * ug; ];\n ];\n \n // Blue channel Halley step\n variable b_val: Number = lms_b0 * l_lms + lms_b1 * m_lms + lms_b2 * s_lms - 1;\n variable b1: Number = lms_b0 * ldt + lms_b1 * mdt + lms_b2 * sdt;\n variable b2: Number = lms_b0 * ldt2 + lms_b1 * mdt2 + lms_b2 * sdt2;\n variable b_denom: Number = b1 * b1 - 0.5 * b_val * b2;\n variable ub: Number = 0;\n variable tb: Number = float_max;\n if (abs(b_denom) > 0.000001) [\n ub = b1 / b_denom;\n if (ub >= 0) [ tb = -b_val * ub; ];\n ];\n \n // Take minimum of the three corrections\n variable t_correction: Number = tr;\n if (tg < t_correction) [ t_correction = tg; ];\n if (tb < t_correction) [ t_correction = tb; ];\n if (t_correction < float_max) [\n t_intersect = t_intersect + t_correction;\n ];\n ];\n \n // C_max = t * c1 = t (since c1 = 1)\n c_max = t_intersect;\n if (c_max < 0) [ c_max = 0; ];\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 7: getStMid - Polynomial approximation for mid-saturation ST\n // ═══════════════════════════════════════════════════════════════════════\n variable st_mid_s: Number = 0.11516993 + 1 / (\n 7.44778970 + 4.15901240 * b_ +\n a_ * (-2.19557347 + 1.75198401 * b_ +\n a_ * (-2.13704948 - 10.02301043 * b_ +\n a_ * (-4.24894561 + 5.38770819 * b_ + 4.69891013 * a_))));\n \n variable st_mid_t: Number = 0.11239642 + 1 / (\n 1.61320320 - 0.68124379 * b_ +\n a_ * (0.40370612 + 0.90148123 * b_ +\n a_ * (-0.27087943 + 0.61223990 * b_ +\n a_ * (0.00299215 - 0.45399568 * b_ - 0.14661872 * a_))));\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 8: getCs - Compute C_0, C_mid, C_max with scale factor k\n // ═══════════════════════════════════════════════════════════════════════\n variable st_max_s: Number = c_cusp / (l_cusp + 0.0001);\n variable st_max_t: Number = c_cusp / (1 - l_cusp + 0.0001);\n \n // Scale factor k = c_max / min(L * st_max_s, (1-L) * st_max_t)\n variable min_st: Number = lab_l * st_max_s;\n variable min_st_t: Number = (1 - lab_l) * st_max_t;\n if (min_st_t < min_st) [ min_st = min_st_t; ];\n \n variable k_factor: Number = 1;\n if (min_st > 0.0001 && c_max > 0.0001) [\n k_factor = c_max / min_st;\n ];\n \n // C_mid = 0.9 * k * sqrt(sqrt(1 / (1/ca^4 + 1/cb^4)))\n variable ca: Number = lab_l * st_mid_s;\n variable cb: Number = (1 - lab_l) * st_mid_t;\n variable ca4: Number = ca * ca * ca * ca;\n variable cb4: Number = cb * cb * cb * cb;\n variable c_mid: Number = 0;\n if (ca4 > 0.0000001 && cb4 > 0.0000001) [\n c_mid = 0.9 * k_factor * sqrt(sqrt(1 / (1 / ca4 + 1 / cb4)));\n ];\n \n // C_0 using average ST values (0.4, 0.8)\n variable ca0: Number = lab_l * 0.4;\n variable cb0: Number = (1 - lab_l) * 0.8;\n variable ca0_sq: Number = ca0 * ca0;\n variable cb0_sq: Number = cb0 * cb0;\n variable c_0: Number = 0;\n if (ca0_sq > 0.0000001 && cb0_sq > 0.0000001) [\n c_0 = sqrt(1 / (1 / ca0_sq + 1 / cb0_sq));\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 9: Compute saturation using piecewise interpolation\n // ═══════════════════════════════════════════════════════════════════════\n variable mid: Number = 0.8;\n \n if (c < c_mid && c_mid > 0.0001 && c_0 > 0.0001) [\n // Below mid-point: s = t * 0.8 where t = c / (k1 + k2*c)\n variable k1_low: Number = mid * c_0;\n variable k2_low: Number = 1 - k1_low / c_mid;\n variable t_low: Number = c / (k1_low + k2_low * c + 0.0001);\n s = t_low * mid;\n ] else [\n if (c_mid > 0.0001 && c_0 > 0.0001 && c_max > c_mid) [\n // Above mid-point: s = 0.8 + 0.2 * t where t = (c - c_mid) / (k1 + k2*(c - c_mid))\n variable mid_inv: Number = 1.25;\n variable k0_high: Number = c_mid;\n variable k1_high: Number = 0.2 * c_mid * c_mid * mid_inv * mid_inv / c_0;\n variable k2_high: Number = 1 - k1_high / (c_max - c_mid + 0.0001);\n variable c_diff: Number = c - k0_high;\n variable t_high: Number = c_diff / (k1_high + k2_high * c_diff + 0.0001);\n s = mid + 0.2 * t_high;\n ];\n ];\n \n // Clamp saturation\n if (s > 1) [ s = 1; ];\n if (s < 0) [ s = 0; ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKHSL;\noutput.h = h;\noutput.s = s;\noutput.l = l;\noutput"
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
],
|
|
538
|
-
"slug": "okhsl-color"
|
|
539
|
-
},
|
|
540
|
-
{
|
|
541
|
-
"name": "OKHSV",
|
|
542
|
-
"type": "color",
|
|
543
|
-
"description": "OKHSV color space by Björn Ottosson - a perceptually uniform HSV based on OKLab. H is hue (0-360), S is saturation (0-1), V is value (0-1). Reference: https://bottosson.github.io/posts/colorpicker/",
|
|
544
|
-
"schema": {
|
|
545
|
-
"type": "object",
|
|
546
|
-
"properties": {
|
|
547
|
-
"h": {
|
|
548
|
-
"type": "number",
|
|
549
|
-
"description": "Hue angle (0-360 degrees), same as OKLCH hue"
|
|
550
|
-
},
|
|
551
|
-
"s": {
|
|
552
|
-
"type": "number",
|
|
553
|
-
"description": "Saturation (0-1), ratio of chroma to maximum at this value"
|
|
554
|
-
},
|
|
555
|
-
"v": {
|
|
556
|
-
"type": "number",
|
|
557
|
-
"description": "Value/brightness (0-1), with V=1 being the brightest for that saturation"
|
|
558
|
-
}
|
|
559
|
-
},
|
|
560
|
-
"required": [
|
|
561
|
-
"h",
|
|
562
|
-
"s",
|
|
563
|
-
"v"
|
|
564
|
-
],
|
|
565
|
-
"order": [
|
|
566
|
-
"h",
|
|
567
|
-
"s",
|
|
568
|
-
"v"
|
|
569
|
-
],
|
|
570
|
-
"additionalProperties": false
|
|
571
|
-
},
|
|
572
|
-
"initializers": [
|
|
573
|
-
{
|
|
574
|
-
"title": "OKHSV Color Initializer",
|
|
575
|
-
"keyword": "okhsv",
|
|
576
|
-
"description": "Creates an OKHSV color from H, S, V values",
|
|
577
|
-
"script": {
|
|
578
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
579
|
-
"script": "// OKHSV Color Initializer\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n//\n// Creates an OKHSV color from hue, saturation, and value.\n// OKHSV is a perceptually uniform version of HSV built on OKLab.\n//\n// Parameters:\n// - h: Hue angle (0-360 degrees), same as OKLCH hue\n// - s: Saturation (0-1), ratio of chroma to maximum at this value\n// - v: Value/brightness (0-1), with V=1 being brightest for that saturation\n// - alpha: Optional alpha channel (0-1)\n//\n// Input: Object with h, s, v, and optional alpha properties\n// Output: Color.OKHSV\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable v: Number = input.v;\n\nvariable output: Color.OKHSV;\noutput.h = h;\noutput.s = s;\noutput.v = v;\n\n// Set alpha if provided\nif (input.alpha != null) [\n output.alpha = input.alpha;\n];\n\noutput"
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
],
|
|
583
|
-
"conversions": [
|
|
584
|
-
{
|
|
585
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
586
|
-
"target": "$self",
|
|
587
|
-
"description": "Converts OKLab to OKHSV using Ottosson's algorithm with Halley's method refinement for gamut boundary",
|
|
588
|
-
"lossless": true,
|
|
589
|
-
"script": {
|
|
590
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
591
|
-
"script": "// OKLab to OKHSV Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsv.js\n// Reference: https://bottosson.github.io/posts/oklab/ (original OKLab paper)\n//\n// OKHSV maps colors to a perceptually uniform HSV cylinder.\n// The algorithm finds the exact gamut boundary using compute_max_saturation\n// and positions colors relative to the cusp (maximum chroma point).\n//\n// Key difference from OKHSL:\n// - V=1 means maximum brightness (white at S=0, cusp color at S=1)\n// - More intuitive for picking saturated colors\n//\n// Input: Color.OKLab with l (0-1), a, b coordinates\n// Output: Color.OKHSV with h (0-360), s (0-1), v (0-1)\n\nvariable lab_l: Number = input.l;\nvariable lab_a: Number = input.a;\nvariable lab_b: Number = input.b;\n\n// Native constants\nvariable pi_val: Number = pi();\n\n// Toe function constants (same as OKHSL)\nvariable toe_k1: Number = 0.206;\nvariable toe_k2: Number = 0.03;\nvariable toe_k3: Number = 1.17009708737864;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Convert to polar coordinates (L, C, H)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = sqrt(lab_a * lab_a + lab_b * lab_b);\nvariable h: Number = 0;\n\nif (c > 0.00001) [\n // Note: ColorJS uses atan2(-b, -a) and adds 0.5, we use atan2(b, a)\n h = atan2(lab_b, lab_a) * 180 / pi_val;\n if (h < 0) [\n h = h + 360;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Initialize defaults and apply toe function to L for V\n// toe(x) = 0.5 * (k3*x - k1 + sqrt((k3*x - k1)^2 + 4*k2*k3*x))\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s: Number = 0;\nvariable v: Number = lab_l;\n\n// Apply toe function to get initial V\nif (lab_l > 0.0001 && lab_l < 0.9999) [\n variable term: Number = toe_k3 * lab_l - toe_k1;\n v = 0.5 * (term + sqrt(term * term + 4 * toe_k2 * toe_k3 * lab_l));\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 3: Compute normalized hue direction\n// ═══════════════════════════════════════════════════════════════════════════\nvariable a_: Number = 0;\nvariable b_: Number = 0;\nif (c > 0.00001) [\n a_ = lab_a / c;\n b_ = lab_b / c;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Pre-compute LMS coefficients (used in multiple steps)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable kl: Number = 0.3963377774 * a_ + 0.2158037573 * b_;\nvariable km: Number = -0.1055613458 * a_ - 0.0638541728 * b_;\nvariable ks: Number = -0.0894841775 * a_ - 1.2914855480 * b_;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 4: Find maximum saturation and cusp\n// Only process if we have chroma (non-achromatic) and L is valid\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s_max: Number = 0;\nvariable l_cusp: Number = 1;\nvariable c_cusp: Number = 0;\n\n// Variables for LMS intermediate calculations\nvariable l_lms: Number = 0;\nvariable m_lms: Number = 0;\nvariable s_lms: Number = 0;\n\nif (c > 0.00001 && lab_l > 0.00001 && lab_l < 0.99999) [\n // Determine which RGB component clips first\n variable k0: Number = 0;\n variable k1_coef: Number = 0;\n variable k2_coef: Number = 0;\n variable k3_coef: Number = 0;\n variable k4_coef: Number = 0;\n variable wl: Number = 0;\n variable wm: Number = 0;\n variable ws: Number = 0;\n \n variable test_r: Number = -1.88170328 * a_ - 0.80936493 * b_;\n variable test_g: Number = 1.81444104 * a_ - 1.19445276 * b_;\n \n if (test_r > 1) [\n k0 = 1.19086277;\n k1_coef = 1.76576728;\n k2_coef = 0.59662641;\n k3_coef = 0.75515197;\n k4_coef = 0.56771245;\n wl = 4.0767416621;\n wm = -3.3077115913;\n ws = 0.2309699292;\n ] else [\n if (test_g > 1) [\n k0 = 0.73956515;\n k1_coef = -0.45954404;\n k2_coef = 0.08285427;\n k3_coef = 0.12541073;\n k4_coef = -0.14503204;\n wl = -1.2684380046;\n wm = 2.6097574011;\n ws = -0.3413193965;\n ] else [\n k0 = 1.35733652;\n k1_coef = -0.00915799;\n k2_coef = -1.15130210;\n k3_coef = -0.50559606;\n k4_coef = 0.00692167;\n wl = -0.0041960863;\n wm = -0.7034186147;\n ws = 1.7076147010;\n ];\n ];\n \n // Polynomial approximation\n s_max = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(denom) > 0.000001) [\n s_max = s_max - f * f1 / denom;\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Find cusp (L_cusp, C_cusp)\n // ═══════════════════════════════════════════════════════════════════════\n if (s_max > 0) [\n variable l_cusp_temp: Number = 1 + s_max * kl;\n variable m_cusp_temp: Number = 1 + s_max * km;\n variable s_cusp_temp: Number = 1 + s_max * ks;\n \n l_lms = l_cusp_temp * l_cusp_temp * l_cusp_temp;\n m_lms = m_cusp_temp * m_cusp_temp * m_cusp_temp;\n s_lms = s_cusp_temp * s_cusp_temp * s_cusp_temp;\n \n variable r_lin: Number = 4.0767416621 * l_lms - 3.3077115913 * m_lms + 0.2309699292 * s_lms;\n variable g_lin: Number = -1.2684380046 * l_lms + 2.6097574011 * m_lms - 0.3413193965 * s_lms;\n variable b_lin: Number = -0.0041960863 * l_lms - 0.7034186147 * m_lms + 1.7076147010 * s_lms;\n \n variable max_rgb: Number = r_lin;\n if (g_lin > max_rgb) [ max_rgb = g_lin; ];\n if (b_lin > max_rgb) [ max_rgb = b_lin; ];\n \n if (max_rgb > 0) [\n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 5: Compute OKHSV S and V using ColorJS algorithm\n//\n// The algorithm uses the ST coordinate system where:\n// - S_t = C/L (slope from origin)\n// - T_t = C/(1-L) (slope from white)\n//\n// Key formulas from ColorJS:\n// - t = tMax / (c + l * tMax)\n// - lv = t * l, cv = t * c\n// - Apply RGB scaling and toe compensation\n// - v = l / lv, s = ((s0 + tMax) * cv) / (tMax * s0 + tMax * k * cv)\n// ═══════════════════════════════════════════════════════════════════════════\nif (c > 0.00001 && lab_l > 0.00001 && lab_l < 0.99999 && l_cusp > 0.0001 && c_cusp > 0.0001) [\n // Compute ST values at cusp\n variable s_t_cusp: Number = c_cusp / l_cusp;\n variable t_t_cusp: Number = c_cusp / (1 - l_cusp + 0.0001);\n \n // Fixed s0 parameter\n variable s_0: Number = 0.5;\n variable k_param: Number = 1 - s_0 / s_t_cusp;\n \n // Compute t and derived values (following ColorJS exactly)\n variable t: Number = t_t_cusp / (c + lab_l * t_t_cusp + 0.0001);\n variable lv: Number = t * lab_l;\n variable cv: Number = t * c;\n \n // Apply inverse toe to lv for compensation\n variable lvt: Number = lv;\n if (lv > 0.0001 && lv < 0.9999) [\n lvt = (lv * lv + toe_k1 * lv) / (toe_k3 * (lv + toe_k2));\n ];\n \n variable cvt: Number = cv;\n if (lv > 0.0001) [\n cvt = cv * lvt / lv;\n ];\n \n // RGB scale computation\n variable scale_l_: Number = 1 + s_max * kl;\n variable scale_m_: Number = 1 + s_max * km;\n variable scale_s_: Number = 1 + s_max * ks;\n \n variable lms_l: Number = lvt + a_ * cvt * kl;\n variable lms_m: Number = lvt + a_ * cvt * km;\n variable lms_s: Number = lvt + a_ * cvt * ks;\n \n // Convert to linear RGB using LMS\n variable lms_l_cubed: Number = lms_l * lms_l * lms_l;\n variable lms_m_cubed: Number = lms_m * lms_m * lms_m;\n variable lms_s_cubed: Number = lms_s * lms_s * lms_s;\n \n variable rs: Number = 4.0767416621 * lms_l_cubed - 3.3077115913 * lms_m_cubed + 0.2309699292 * lms_s_cubed;\n variable gs: Number = -1.2684380046 * lms_l_cubed + 2.6097574011 * lms_m_cubed - 0.3413193965 * lms_s_cubed;\n variable bs: Number = -0.0041960863 * lms_l_cubed - 0.7034186147 * lms_m_cubed + 1.7076147010 * lms_s_cubed;\n \n variable max_s: Number = rs;\n if (gs > max_s) [ max_s = gs; ];\n if (bs > max_s) [ max_s = bs; ];\n if (max_s < 0.0001) [ max_s = 0.0001; ];\n \n variable scale_l: Number = pow(1 / max_s, 0.3333333333333333);\n \n // Scale L and C\n variable l_scaled: Number = lab_l / scale_l;\n variable c_scaled: Number = c / scale_l;\n \n // Apply toe to scaled L for compensation\n variable l_toed: Number = l_scaled;\n if (l_scaled > 0.0001 && l_scaled < 0.9999) [\n variable term2: Number = toe_k3 * l_scaled - toe_k1;\n l_toed = 0.5 * (term2 + sqrt(term2 * term2 + 4 * toe_k2 * toe_k3 * l_scaled));\n ];\n \n c_scaled = c_scaled * l_toed / (l_scaled + 0.0001);\n \n // Compute final v and s\n if (lv > 0.0001) [\n v = l_toed / lv;\n if (v > 1) [ v = 1; ];\n if (v < 0) [ v = 0; ];\n ];\n \n variable denom_s: Number = t_t_cusp * s_0 + t_t_cusp * k_param * cv;\n if (abs(denom_s) > 0.0001) [\n s = ((s_0 + t_t_cusp) * cv) / denom_s;\n if (s > 1) [ s = 1; ];\n if (s < 0) [ s = 0; ];\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKHSV;\noutput.h = h;\noutput.s = s;\noutput.v = v;\noutput"
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
],
|
|
595
|
-
"slug": "okhsv-color"
|
|
596
|
-
},
|
|
597
|
-
{
|
|
598
|
-
"name": "OKLab",
|
|
599
|
-
"type": "color",
|
|
600
|
-
"description": "OKLab perceptually uniform color space by Björn Ottosson. L is lightness (0-1), a is green-red axis, b is blue-yellow axis.",
|
|
601
|
-
"schema": {
|
|
602
|
-
"type": "object",
|
|
603
|
-
"properties": {
|
|
604
|
-
"l": {
|
|
605
|
-
"type": "number",
|
|
606
|
-
"description": "Lightness (0-1)"
|
|
607
|
-
},
|
|
608
|
-
"a": {
|
|
609
|
-
"type": "number",
|
|
610
|
-
"description": "Green-red axis (typically -0.4 to 0.4)"
|
|
611
|
-
},
|
|
612
|
-
"b": {
|
|
613
|
-
"type": "number",
|
|
614
|
-
"description": "Blue-yellow axis (typically -0.4 to 0.4)"
|
|
615
|
-
}
|
|
616
|
-
},
|
|
617
|
-
"required": [
|
|
618
|
-
"l",
|
|
619
|
-
"a",
|
|
620
|
-
"b"
|
|
621
|
-
],
|
|
622
|
-
"order": [
|
|
623
|
-
"l",
|
|
624
|
-
"a",
|
|
625
|
-
"b"
|
|
626
|
-
],
|
|
627
|
-
"additionalProperties": false
|
|
628
|
-
},
|
|
629
|
-
"initializers": [
|
|
630
|
-
{
|
|
631
|
-
"title": "OKLab Color Initializer",
|
|
632
|
-
"keyword": "oklab",
|
|
633
|
-
"description": "Creates an OKLab color from L, a, b values",
|
|
634
|
-
"script": {
|
|
635
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
636
|
-
"script": "// OKLab Color Initializer\n// Creates an OKLab color from L, a, b values\n// Input: List of [l, a, b] or [l, a, b, alpha] values\n\nvariable lab_values: List = {input};\nvariable output: Color.OKLab;\n\noutput.l = lab_values.get(0);\noutput.a = lab_values.get(1);\noutput.b = lab_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lab_values.length() > 3) [\n output.alpha = lab_values.get(3);\n];\n\nreturn output;"
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
],
|
|
640
|
-
"conversions": [
|
|
641
|
-
{
|
|
642
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/",
|
|
643
|
-
"target": "$self",
|
|
644
|
-
"description": "Converts XYZ-D65 to OKLab using Björn Ottosson's algorithm",
|
|
645
|
-
"lossless": true,
|
|
646
|
-
"script": {
|
|
647
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
648
|
-
"script": "// XYZ-D65 to OKLab Conversion\n// Uses ColorJS-compatible matrices (recalculated for consistent D65 white)\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/oklab.js\n//\n// Algorithm:\n// 1. XYZ-D65 → LMS (M1 matrix)\n// 2. LMS → LMS' (cube root transformation)\n// 3. LMS' → OKLab (M2 matrix)\n//\n// Input: Color.XYZD65 with x, y, z tristimulus\n// Output: Color.OKLab with l, a, b coordinates\n\n// Get input XYZ-D65 values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// Step 1: XYZ-D65 → LMS cone response\n// ColorJS XYZtoLMS_M matrix (recalculated for consistent reference white)\nvariable lms_l: Number = x * 0.8190224379967030 + y * 0.3619062600528904 + z * -0.1288737815209879;\nvariable lms_m: Number = x * 0.0329836539323885 + y * 0.9292868615863434 + z * 0.0361446663506424;\nvariable lms_s: Number = x * 0.0481771893596242 + y * 0.2642395317527308 + z * 0.6335478284694309;\n\n// Step 2: Apply cube root (γ = 1/3)\n// Note: Using pow with 1/3 exponent (cbrt equivalent for positive values)\nvariable cube_root_exp: Number = 0.3333333333333333;\nvariable lms_l_prime: Number = pow(lms_l, cube_root_exp);\nvariable lms_m_prime: Number = pow(lms_m, cube_root_exp);\nvariable lms_s_prime: Number = pow(lms_s, cube_root_exp);\n\n// Step 3: LMS' → OKLab\n// ColorJS LMStoLab_M matrix\nvariable oklab_l: Number = lms_l_prime * 0.2104542683093140 + lms_m_prime * 0.7936177747023054 + lms_s_prime * -0.0040720430116193;\nvariable oklab_a: Number = lms_l_prime * 1.9779985324311684 + lms_m_prime * -2.4285922420485799 + lms_s_prime * 0.4505937096174110;\nvariable oklab_b: Number = lms_l_prime * 0.0259040424655478 + lms_m_prime * 0.7827717124575296 + lms_s_prime * -0.8086757549230774;\n\n// Create output\nvariable output: Color.OKLab;\noutput.l = oklab_l;\noutput.a = oklab_a;\noutput.b = oklab_b;\n\nreturn output;"
|
|
649
|
-
}
|
|
650
|
-
},
|
|
651
|
-
{
|
|
652
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
653
|
-
"target": "$self",
|
|
654
|
-
"description": "Converts OKLCH to OKLab (polar to cartesian)",
|
|
655
|
-
"lossless": true,
|
|
656
|
-
"script": {
|
|
657
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
658
|
-
"script": "// OKLCH to OKLab Conversion\n// Converts polar (C, H) back to cartesian (a, b) coordinates\n// Reference: Björn Ottosson's OKLab specification\n//\n// Algorithm:\n// L stays the same (lightness)\n// a = C * cos(H * π/180)\n// b = C * sin(H * π/180)\n//\n// Input: Color.OKLCH with l, c, h coordinates\n// Output: Color.OKLab with l, a, b coordinates\n\n// Get input OKLCH values\nvariable l: Number = {input}.l;\nvariable c: Number = {input}.c;\nvariable h: Number = {input}.h;\n\n// Constants\nvariable pi: Number = pi();\nvariable deg_to_rad: Number = pi / 180;\n\n// Convert hue to radians\nvariable h_rad: Number = h * deg_to_rad;\n\n// Convert polar to cartesian\nvariable a: Number = c * cos(h_rad);\nvariable b: Number = c * sin(h_rad);\n\n// Create output\nvariable output: Color.OKLab;\noutput.l = l;\noutput.a = a;\noutput.b = b;\n\nreturn output;"
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
{
|
|
662
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/okhsl-color/0/",
|
|
663
|
-
"target": "$self",
|
|
664
|
-
"description": "Converts OKHSL to OKLab",
|
|
665
|
-
"lossless": true,
|
|
666
|
-
"script": {
|
|
667
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
668
|
-
"script": "// OKHSL to OKLab Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsl.js\n//\n// Converts OKHSL back to OKLab using:\n// 1. Inverse toe function to recover OKLab lightness\n// 2. Cusp finding to determine max chroma at this hue\n// 3. Saturation denormalization to get actual chroma\n//\n// Input: Color.OKHSL with h (0-360), s (0-1), l (0-1)\n// Output: Color.OKLab with l (0-1), a, b coordinates\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable l: Number = input.l;\n\n// Native constants\nvariable pi_val: Number = pi();\n\n// Toe function constants\nvariable toe_k1: Number = 0.206;\nvariable toe_k2: Number = 0.03;\nvariable toe_k3: Number = 1.17009708737864;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Apply inverse toe function to get OKLab lightness\n// toe_inv(x) = (x^2 + k1*x) / (k3 * (x + k2))\n// ═══════════════════════════════════════════════════════════════════════════\nvariable lab_l: Number = l;\nif (l > 0.0001 && l < 0.9999) [\n lab_l = (l * l + toe_k1 * l) / (toe_k3 * (l + toe_k2));\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Handle achromatic case\n// ═══════════════════════════════════════════════════════════════════════════\nvariable lab_a: Number = 0;\nvariable lab_b: Number = 0;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 3: Compute hue direction\n// ═══════════════════════════════════════════════════════════════════════════\nvariable h_rad: Number = h * pi_val / 180;\nvariable a_: Number = cos(h_rad);\nvariable b_: Number = sin(h_rad);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Pre-compute LMS coefficients (used in steps 4 and 5)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable kl: Number = 0.3963377774 * a_ + 0.2158037573 * b_;\nvariable km: Number = -0.1055613458 * a_ - 0.0638541728 * b_;\nvariable ks: Number = -0.0894841775 * a_ - 1.2914855480 * b_;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 4: Find maximum saturation at this hue (same as forward conversion)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s_max: Number = 0;\n\n// Variables for LMS intermediate calculations (reused in step 5)\nvariable l_lms: Number = 0;\nvariable m_lms: Number = 0;\nvariable s_lms: Number = 0;\n\nif (s >= 0.0001) [\n variable k0: Number = 0;\n variable k1_coef: Number = 0;\n variable k2_coef: Number = 0;\n variable k3_coef: Number = 0;\n variable k4_coef: Number = 0;\n variable wl: Number = 0;\n variable wm: Number = 0;\n variable ws: Number = 0;\n \n variable test_r: Number = -1.88170328 * a_ - 0.80936493 * b_;\n variable test_g: Number = 1.81444104 * a_ - 1.19445276 * b_;\n \n if (test_r > 1) [\n k0 = 1.19086277;\n k1_coef = 1.76576728;\n k2_coef = 0.59662641;\n k3_coef = 0.75515197;\n k4_coef = 0.56771245;\n wl = 4.0767416621;\n wm = -3.3077115913;\n ws = 0.2309699292;\n ] else [\n if (test_g > 1) [\n k0 = 0.73956515;\n k1_coef = -0.45954404;\n k2_coef = 0.08285427;\n k3_coef = 0.12541073;\n k4_coef = -0.14503204;\n wl = -1.2684380046;\n wm = 2.6097574011;\n ws = -0.3413193965;\n ] else [\n k0 = 1.35733652;\n k1_coef = -0.00915799;\n k2_coef = -1.15130210;\n k3_coef = -0.50559606;\n k4_coef = 0.00692167;\n wl = -0.0041960863;\n wm = -0.7034186147;\n ws = 1.7076147010;\n ];\n ];\n \n s_max = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(denom) > 0.000001) [\n s_max = s_max - f * f1 / denom;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 5: Find cusp\n// ═══════════════════════════════════════════════════════════════════════════\nvariable l_cusp: Number = 1;\nvariable c_cusp: Number = 0;\n\nif (s >= 0.0001 && s_max > 0) [\n // Recompute LMS values with refined s_max (kl, km, ks already computed)\n variable l_cusp_temp: Number = 1 + s_max * kl;\n variable m_cusp_temp: Number = 1 + s_max * km;\n variable s_cusp_temp: Number = 1 + s_max * ks;\n \n l_lms = l_cusp_temp * l_cusp_temp * l_cusp_temp;\n m_lms = m_cusp_temp * m_cusp_temp * m_cusp_temp;\n s_lms = s_cusp_temp * s_cusp_temp * s_cusp_temp;\n \n variable r_lin: Number = 4.0767416621 * l_lms - 3.3077115913 * m_lms + 0.2309699292 * s_lms;\n variable g_lin: Number = -1.2684380046 * l_lms + 2.6097574011 * m_lms - 0.3413193965 * s_lms;\n variable b_lin: Number = -0.0041960863 * l_lms - 0.7034186147 * m_lms + 1.7076147010 * s_lms;\n \n variable max_rgb: Number = r_lin;\n if (g_lin > max_rgb) [ max_rgb = g_lin; ];\n if (b_lin > max_rgb) [ max_rgb = b_lin; ];\n \n if (max_rgb > 0) [\n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 6: Compute chroma from saturation\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = 0;\n\nif (s >= 0.0001 && l_cusp > 0.0001 && c_cusp > 0.0001) [\n variable s_t_cusp: Number = c_cusp / l_cusp;\n \n // Denormalize saturation to get chroma\n c = s * lab_l * s_t_cusp;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 7: Convert polar to cartesian\n// ═══════════════════════════════════════════════════════════════════════════\nif (s >= 0.0001) [\n lab_a = c * a_;\n lab_b = c * b_;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKLab;\noutput.l = lab_l;\noutput.a = lab_a;\noutput.b = lab_b;\noutput"
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/okhsv-color/0/",
|
|
673
|
-
"target": "$self",
|
|
674
|
-
"description": "Converts OKHSV to OKLab",
|
|
675
|
-
"lossless": true,
|
|
676
|
-
"script": {
|
|
677
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
678
|
-
"script": "// OKHSV to OKLab Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsv.js\n//\n// Converts OKHSV back to OKLab using:\n// 1. Cusp finding to determine gamut boundary at this hue\n// 2. V and S denormalization to get L and C\n//\n// Input: Color.OKHSV with h (0-360), s (0-1), v (0-1)\n// Output: Color.OKLab with l (0-1), a, b coordinates\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable v: Number = input.v;\n\n// Native constants\nvariable pi_val: Number = pi();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Handle edge cases\n// ═══════════════════════════════════════════════════════════════════════════\nvariable lab_l: Number = v;\nvariable lab_a: Number = 0;\nvariable lab_b: Number = 0;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Compute hue direction\n// ═══════════════════════════════════════════════════════════════════════════\nvariable h_rad: Number = h * pi_val / 180;\nvariable a_: Number = cos(h_rad);\nvariable b_: Number = sin(h_rad);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Pre-compute LMS coefficients (used in steps 3 and 4)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable kl: Number = 0.3963377774 * a_ + 0.2158037573 * b_;\nvariable km: Number = -0.1055613458 * a_ - 0.0638541728 * b_;\nvariable ks: Number = -0.0894841775 * a_ - 1.2914855480 * b_;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 3: Find maximum saturation at this hue\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s_max: Number = 0;\n\n// Variables for LMS intermediate calculations (reused in step 4)\nvariable l_lms: Number = 0;\nvariable m_lms: Number = 0;\nvariable s_lms: Number = 0;\n\nif (s >= 0.0001 && v >= 0.0001) [\n variable k0: Number = 0;\n variable k1_coef: Number = 0;\n variable k2_coef: Number = 0;\n variable k3_coef: Number = 0;\n variable k4_coef: Number = 0;\n variable wl: Number = 0;\n variable wm: Number = 0;\n variable ws: Number = 0;\n \n variable test_r: Number = -1.88170328 * a_ - 0.80936493 * b_;\n variable test_g: Number = 1.81444104 * a_ - 1.19445276 * b_;\n \n if (test_r > 1) [\n k0 = 1.19086277;\n k1_coef = 1.76576728;\n k2_coef = 0.59662641;\n k3_coef = 0.75515197;\n k4_coef = 0.56771245;\n wl = 4.0767416621;\n wm = -3.3077115913;\n ws = 0.2309699292;\n ] else [\n if (test_g > 1) [\n k0 = 0.73956515;\n k1_coef = -0.45954404;\n k2_coef = 0.08285427;\n k3_coef = 0.12541073;\n k4_coef = -0.14503204;\n wl = -1.2684380046;\n wm = 2.6097574011;\n ws = -0.3413193965;\n ] else [\n k0 = 1.35733652;\n k1_coef = -0.00915799;\n k2_coef = -1.15130210;\n k3_coef = -0.50559606;\n k4_coef = 0.00692167;\n wl = -0.0041960863;\n wm = -0.7034186147;\n ws = 1.7076147010;\n ];\n ];\n \n s_max = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(denom) > 0.000001) [\n s_max = s_max - f * f1 / denom;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 4: Find cusp\n// ═══════════════════════════════════════════════════════════════════════════\nvariable l_cusp: Number = 1;\nvariable c_cusp: Number = 0;\n\nif (s >= 0.0001 && v >= 0.0001 && s_max > 0) [\n // Recompute LMS values with refined s_max (kl, km, ks already computed)\n variable l_cusp_temp: Number = 1 + s_max * kl;\n variable m_cusp_temp: Number = 1 + s_max * km;\n variable s_cusp_temp: Number = 1 + s_max * ks;\n \n l_lms = l_cusp_temp * l_cusp_temp * l_cusp_temp;\n m_lms = m_cusp_temp * m_cusp_temp * m_cusp_temp;\n s_lms = s_cusp_temp * s_cusp_temp * s_cusp_temp;\n \n variable r_lin: Number = 4.0767416621 * l_lms - 3.3077115913 * m_lms + 0.2309699292 * s_lms;\n variable g_lin: Number = -1.2684380046 * l_lms + 2.6097574011 * m_lms - 0.3413193965 * s_lms;\n variable b_lin: Number = -0.0041960863 * l_lms - 0.7034186147 * m_lms + 1.7076147010 * s_lms;\n \n variable max_rgb: Number = r_lin;\n if (g_lin > max_rgb) [ max_rgb = g_lin; ];\n if (b_lin > max_rgb) [ max_rgb = b_lin; ];\n \n if (max_rgb > 0) [\n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 5: Compute L and C from V and S\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = 0;\n\nif (s >= 0.0001 && v >= 0.0001 && l_cusp > 0.0001 && c_cusp > 0.0001) [\n variable s_t_cusp: Number = c_cusp / l_cusp;\n variable t_t_cusp: Number = c_cusp / (1 - l_cusp + 0.0001);\n \n // Compute max chroma at this V\n variable c_max_at_v: Number = 0;\n \n if (v <= l_cusp) [\n c_max_at_v = v * s_t_cusp;\n ] else [\n c_max_at_v = (1 - v) * t_t_cusp;\n ];\n \n // Compute actual chroma\n c = s * c_max_at_v;\n \n // Compute L from V and C\n // Reverse of: V = L + C / s_t_cusp\n lab_l = v - c / (s_t_cusp + 0.0001);\n if (lab_l < 0) [ lab_l = 0; ];\n if (lab_l > 1) [ lab_l = 1; ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 6: Convert polar to cartesian\n// ═══════════════════════════════════════════════════════════════════════════\nif (s >= 0.0001 && v >= 0.0001) [\n lab_a = c * a_;\n lab_b = c * b_;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKLab;\noutput.l = lab_l;\noutput.a = lab_a;\noutput.b = lab_b;\noutput"
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
],
|
|
682
|
-
"slug": "oklab-color"
|
|
683
|
-
},
|
|
684
|
-
{
|
|
685
|
-
"name": "OKLCH",
|
|
686
|
-
"type": "color",
|
|
687
|
-
"description": "OKLCH color space - the polar form of OKLab. L is lightness (0-1), C is chroma, H is hue angle (0-360).",
|
|
688
|
-
"schema": {
|
|
689
|
-
"type": "object",
|
|
690
|
-
"properties": {
|
|
691
|
-
"l": {
|
|
692
|
-
"type": "number",
|
|
693
|
-
"description": "Lightness (0-1)"
|
|
694
|
-
},
|
|
695
|
-
"c": {
|
|
696
|
-
"type": "number",
|
|
697
|
-
"description": "Chroma (0 to ~0.4 for sRGB gamut)"
|
|
698
|
-
},
|
|
699
|
-
"h": {
|
|
700
|
-
"type": "number",
|
|
701
|
-
"description": "Hue angle (0-360 degrees)"
|
|
702
|
-
}
|
|
703
|
-
},
|
|
704
|
-
"required": [
|
|
705
|
-
"l",
|
|
706
|
-
"c",
|
|
707
|
-
"h"
|
|
708
|
-
],
|
|
709
|
-
"order": [
|
|
710
|
-
"l",
|
|
711
|
-
"c",
|
|
712
|
-
"h"
|
|
713
|
-
],
|
|
714
|
-
"additionalProperties": false
|
|
715
|
-
},
|
|
716
|
-
"initializers": [
|
|
717
|
-
{
|
|
718
|
-
"title": "OKLCH Color Initializer",
|
|
719
|
-
"keyword": "oklch",
|
|
720
|
-
"description": "Creates an OKLCH color from L, C, H values",
|
|
721
|
-
"script": {
|
|
722
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
723
|
-
"script": "// OKLCH Color Initializer\n// Creates an OKLCH color from L, C, H values\n// Input: List of [l, c, h] or [l, c, h, alpha] values\n\nvariable lch_values: List = {input};\nvariable output: Color.OKLCH;\n\noutput.l = lch_values.get(0);\noutput.c = lch_values.get(1);\noutput.h = lch_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lch_values.length() > 3) [\n output.alpha = lch_values.get(3);\n];\n\nreturn output;"
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
],
|
|
727
|
-
"conversions": [
|
|
728
|
-
{
|
|
729
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
730
|
-
"target": "$self",
|
|
731
|
-
"description": "Converts OKLab to OKLCH using Cartesian to polar transformation",
|
|
732
|
-
"lossless": true,
|
|
733
|
-
"script": {
|
|
734
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
735
|
-
"script": "// OKLab to OKLCH Conversion\n// Converts Cartesian (a, b) to polar (C, H) coordinates\n// Reference: Björn Ottosson's OKLab specification\n//\n// Algorithm:\n// L stays the same (lightness)\n// C = sqrt(a² + b²) (chroma - distance from neutral axis)\n// H = atan2(b, a) * 180/π (hue angle in degrees)\n//\n// Input: Color.OKLab with l, a, b coordinates\n// Output: Color.OKLCH with l, c, h coordinates\n\n// Get input OKLab values\nvariable l: Number = {input}.l;\nvariable a: Number = {input}.a;\nvariable b: Number = {input}.b;\n\n// Constants\nvariable pi: Number = pi();\nvariable rad_to_deg: Number = 180 / pi;\n\n// Calculate chroma (distance from neutral axis)\nvariable c: Number = sqrt(a * a + b * b);\n\n// Calculate hue angle using atan2\n// atan2(y, x) returns angle in radians from -π to π\nvariable h_rad: Number = atan2(b, a);\nvariable h: Number = h_rad * rad_to_deg;\n\n// Normalize hue to 0-360 range\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.OKLCH;\noutput.l = l;\noutput.c = c;\noutput.h = h;\n\nreturn output;"
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
],
|
|
739
|
-
"slug": "oklch-color"
|
|
740
|
-
},
|
|
741
|
-
{
|
|
742
|
-
"name": "P3",
|
|
743
|
-
"type": "color",
|
|
744
|
-
"description": "Display-P3 color space with sRGB transfer function. Wider gamut than sRGB, common on modern Apple displays.",
|
|
745
|
-
"schema": {
|
|
746
|
-
"type": "object",
|
|
747
|
-
"properties": {
|
|
748
|
-
"r": {
|
|
749
|
-
"type": "number",
|
|
750
|
-
"description": "Red channel (0-1, can exceed for out-of-gamut)"
|
|
751
|
-
},
|
|
752
|
-
"g": {
|
|
753
|
-
"type": "number",
|
|
754
|
-
"description": "Green channel (0-1, can exceed for out-of-gamut)"
|
|
755
|
-
},
|
|
756
|
-
"b": {
|
|
757
|
-
"type": "number",
|
|
758
|
-
"description": "Blue channel (0-1, can exceed for out-of-gamut)"
|
|
759
|
-
}
|
|
760
|
-
},
|
|
761
|
-
"required": [
|
|
762
|
-
"r",
|
|
763
|
-
"g",
|
|
764
|
-
"b"
|
|
765
|
-
],
|
|
766
|
-
"order": [
|
|
767
|
-
"r",
|
|
768
|
-
"g",
|
|
769
|
-
"b"
|
|
770
|
-
],
|
|
771
|
-
"additionalProperties": false
|
|
772
|
-
},
|
|
773
|
-
"initializers": [
|
|
774
|
-
{
|
|
775
|
-
"title": "Display-P3 Color Initializer",
|
|
776
|
-
"keyword": "p3",
|
|
777
|
-
"description": "Creates a Display-P3 color from 0-1 values",
|
|
778
|
-
"script": {
|
|
779
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
780
|
-
"script": "// Display-P3 Color Initializer\n// Creates a Display-P3 color from 0-1 values\n// Input: List of [r, g, b] or [r, g, b, alpha] values\n\nvariable color_values: List = {input};\nvariable output: Color.P3;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (color_values.length() > 3) [\n output.alpha = color_values.get(3);\n];\n\nreturn output;"
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
],
|
|
784
|
-
"conversions": [
|
|
785
|
-
{
|
|
786
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-linear-color/0/",
|
|
787
|
-
"target": "$self",
|
|
788
|
-
"description": "Converts Linear P3 to P3 by applying sRGB transfer function",
|
|
789
|
-
"lossless": true,
|
|
790
|
-
"script": {
|
|
791
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
792
|
-
"script": "// Linear P3 to P3 Conversion\n// Applies sRGB transfer function (gamma encoding)\n// P3 uses the same transfer function as sRGB\n// Reference: CSS Color Level 4\n//\n// Algorithm (same as sRGB):\n// if linear ≤ 0.0031308: encoded = 12.92 × linear\n// else: encoded = 1.055 × linear^(1/2.4) - 0.055\n//\n// Input: Color.LinearP3 with linear r, g, b values\n// Output: Color.P3 with gamma-encoded r, g, b values\n\n// Transfer function constants (same as sRGB)\nvariable threshold: Number = 0.0031308;\nvariable linear_scale: Number = 12.92;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_exponent: Number = 0.4166666666666667;\n\n// Get linear values\nvariable linear_r: Number = {input}.r;\nvariable linear_g: Number = {input}.g;\nvariable linear_b: Number = {input}.b;\n\n// Convert red channel\nvariable encoded_r: Number = 0;\nif (linear_r <= threshold) [\n encoded_r = linear_scale * linear_r;\n] else [\n encoded_r = gamma_scale * pow(linear_r, gamma_exponent) - gamma_offset;\n];\n\n// Convert green channel\nvariable encoded_g: Number = 0;\nif (linear_g <= threshold) [\n encoded_g = linear_scale * linear_g;\n] else [\n encoded_g = gamma_scale * pow(linear_g, gamma_exponent) - gamma_offset;\n];\n\n// Convert blue channel\nvariable encoded_b: Number = 0;\nif (linear_b <= threshold) [\n encoded_b = linear_scale * linear_b;\n] else [\n encoded_b = gamma_scale * pow(linear_b, gamma_exponent) - gamma_offset;\n];\n\n// Create output\nvariable output: Color.P3;\noutput.r = encoded_r;\noutput.g = encoded_g;\noutput.b = encoded_b;\n\nreturn output;"
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
],
|
|
796
|
-
"slug": "p3-color"
|
|
797
|
-
},
|
|
798
|
-
{
|
|
799
|
-
"name": "LinearP3",
|
|
800
|
-
"type": "color",
|
|
801
|
-
"description": "Linear Display-P3 color space (gamma-decoded). Used for matrix transformations to XYZ.",
|
|
802
|
-
"schema": {
|
|
803
|
-
"type": "object",
|
|
804
|
-
"properties": {
|
|
805
|
-
"r": {
|
|
806
|
-
"type": "number",
|
|
807
|
-
"description": "Linear red channel (0-1, can exceed for HDR)"
|
|
808
|
-
},
|
|
809
|
-
"g": {
|
|
810
|
-
"type": "number",
|
|
811
|
-
"description": "Linear green channel (0-1, can exceed for HDR)"
|
|
812
|
-
},
|
|
813
|
-
"b": {
|
|
814
|
-
"type": "number",
|
|
815
|
-
"description": "Linear blue channel (0-1, can exceed for HDR)"
|
|
816
|
-
}
|
|
817
|
-
},
|
|
818
|
-
"required": [
|
|
819
|
-
"r",
|
|
820
|
-
"g",
|
|
821
|
-
"b"
|
|
822
|
-
],
|
|
823
|
-
"order": [
|
|
824
|
-
"r",
|
|
825
|
-
"g",
|
|
826
|
-
"b"
|
|
827
|
-
],
|
|
828
|
-
"additionalProperties": false
|
|
829
|
-
},
|
|
830
|
-
"initializers": [
|
|
831
|
-
{
|
|
832
|
-
"title": "Linear P3 Color Initializer",
|
|
833
|
-
"keyword": "linearp3",
|
|
834
|
-
"description": "Creates a linear Display-P3 color from linear 0-1 values",
|
|
835
|
-
"script": {
|
|
836
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
837
|
-
"script": "// Linear P3 Color Initializer\n// Creates a linear Display-P3 color from linear 0-1 values\n// Input: List of [r, g, b] linear values\n\nvariable color_values: List = {input};\nvariable output: Color.LinearP3;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\nreturn output;"
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
],
|
|
841
|
-
"conversions": [
|
|
842
|
-
{
|
|
843
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/",
|
|
844
|
-
"target": "$self",
|
|
845
|
-
"description": "Converts XYZ-D65 to Linear P3 using the P3 transformation matrix",
|
|
846
|
-
"lossless": true,
|
|
847
|
-
"script": {
|
|
848
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
849
|
-
"script": "// XYZ-D65 to Linear P3 Conversion\n// Converts CIE XYZ (D65) to linear Display-P3 RGB\n// Reference: CSS Color Level 4 specification\n//\n// Uses the inverse of the P3 to XYZ-D65 matrix\n// Matrix values from ColorJS / CSS Color Level 4\n//\n// Input: Color.XYZD65 with x, y, z tristimulus values\n// Output: Color.LinearP3 with linear r, g, b values (may be outside 0-1 for out-of-gamut)\n\n// Get XYZ values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// XYZ to Linear P3 matrix (inverse of P3 to XYZ)\n// Row 1\nvariable m00: Number = 2.4934969119414254;\nvariable m01: Number = -0.9313836179191239;\nvariable m02: Number = -0.40271078445071684;\n\n// Row 2\nvariable m10: Number = -0.8294889695615747;\nvariable m11: Number = 1.7626640603183463;\nvariable m12: Number = 0.023624685841943577;\n\n// Row 3\nvariable m20: Number = 0.03584583024378447;\nvariable m21: Number = -0.07617238926804182;\nvariable m22: Number = 0.9568845240076872;\n\n// Matrix multiplication: [r, g, b] = M × [x, y, z]\nvariable linear_r: Number = m00 * x + m01 * y + m02 * z;\nvariable linear_g: Number = m10 * x + m11 * y + m12 * z;\nvariable linear_b: Number = m20 * x + m21 * y + m22 * z;\n\n// Create output (note: values may be outside 0-1 for out-of-gamut colors)\nvariable output: Color.LinearP3;\noutput.r = linear_r;\noutput.g = linear_g;\noutput.b = linear_b;\n\nreturn output;"
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
],
|
|
853
|
-
"slug": "p3-linear-color"
|
|
854
|
-
},
|
|
855
|
-
{
|
|
856
|
-
"name": "Rgb",
|
|
857
|
-
"type": "color",
|
|
858
|
-
"description": "RGB color",
|
|
859
|
-
"schema": {
|
|
860
|
-
"type": "object",
|
|
861
|
-
"properties": {
|
|
862
|
-
"r": {
|
|
863
|
-
"type": "number"
|
|
864
|
-
},
|
|
865
|
-
"g": {
|
|
866
|
-
"type": "number"
|
|
867
|
-
},
|
|
868
|
-
"b": {
|
|
869
|
-
"type": "number"
|
|
870
|
-
}
|
|
871
|
-
},
|
|
872
|
-
"required": [
|
|
873
|
-
"r",
|
|
874
|
-
"g",
|
|
875
|
-
"b"
|
|
876
|
-
],
|
|
877
|
-
"order": [
|
|
878
|
-
"r",
|
|
879
|
-
"g",
|
|
880
|
-
"b"
|
|
881
|
-
],
|
|
882
|
-
"additionalProperties": false
|
|
883
|
-
},
|
|
884
|
-
"initializers": [
|
|
885
|
-
{
|
|
886
|
-
"title": "function",
|
|
887
|
-
"keyword": "rgb",
|
|
888
|
-
"description": "Creates an RGB color",
|
|
889
|
-
"script": {
|
|
890
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
891
|
-
"script": "// RGB Color Initializer\n// Creates an RGB color from r, g, b values (0-255 range)\n//\n// Usage: rgb(255, 128, 64) → Color.Rgb { r: 255, g: 128, b: 64 }\n// rgb(255, 128, 64, 0.5) → Color.Rgb { r: 255, g: 128, b: 64, alpha: 0.5 }\n//\n// Input: List of 3 or 4 numbers [r, g, b] or [r, g, b, alpha]\n// Output: Color.Rgb\n\nvariable color_parts: List = {input}; \n\nvariable output: Color.Rgb;\noutput.r = color_parts.get(0);\noutput.g = color_parts.get(1);\noutput.b = color_parts.get(2);\n\n// Set alpha if provided as 4th parameter\nif (color_parts.length() > 3) [\n output.alpha = color_parts.get(3);\n];\n\nreturn output;"
|
|
892
|
-
}
|
|
893
|
-
},
|
|
894
|
-
{
|
|
895
|
-
"title": "function",
|
|
896
|
-
"keyword": "rgba",
|
|
897
|
-
"description": "Creates an RGB color with alpha",
|
|
898
|
-
"script": {
|
|
899
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
900
|
-
"script": "// RGBA Color Initializer\n// Creates an RGB color with alpha from r, g, b, a values\n//\n// Usage: rgba(255, 128, 64, 0.5) → Color.Rgb { r: 255, g: 128, b: 64, alpha: 0.5 }\n//\n// Input: List of 4 numbers [r, g, b, alpha]\n// Output: Color.Rgb with alpha\n\nvariable color_parts: List = {input}; \n\nvariable output: Color.Rgb;\noutput.r = color_parts.get(0);\noutput.g = color_parts.get(1);\noutput.b = color_parts.get(2);\noutput.alpha = color_parts.get(3);\n\nreturn output;"
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
],
|
|
904
|
-
"conversions": [
|
|
905
|
-
{
|
|
906
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/",
|
|
907
|
-
"target": "$self",
|
|
908
|
-
"description": "Converts HEX to RGB",
|
|
909
|
-
"lossless": true,
|
|
910
|
-
"script": {
|
|
911
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
912
|
-
"script": "// Hex to RGB Conversion\n// Converts hexadecimal color string to RGB (0-255 range)\n//\n// Supports both shorthand (#RGB) and standard (#RRGGBB) formats:\n// #f00 → RGB(255, 0, 0)\n// #ff0000 → RGB(255, 0, 0)\n//\n// Algorithm:\n// 1. Remove '#' prefix and split into characters\n// 2. For shorthand: duplicate each digit (f → ff)\n// 3. Parse hex pairs to decimal values\n//\n// Input: Color.Hex (e.g., #ff5733)\n// Output: Color.Rgb with r, g, b in 0-255 range\n\n// Split hex string on '#' and get the color value\nvariable color_parts: List = {input}.to_string().split('#'); \nvariable color: List = color_parts.get(1).split(); \nvariable length: Number = color.length(); \n\n// Initialize RGB values\nvariable rgb: List = 0, 0, 0; \n\n// Handle shorthand (#RGB) vs standard (#RRGGBB) format\nif(length == 3) [ \n // Shorthand: duplicate each digit\n rgb.update(0, parse_int(color.get(0).concat(color.get(0)), 16)); \n rgb.update(1, parse_int(color.get(1).concat(color.get(1)), 16)); \n rgb.update(2, parse_int(color.get(2).concat(color.get(2)), 16)); \n] else [ \n // Standard: pair adjacent digits\n rgb.update(0, parse_int(color.get(0).concat(color.get(1)), 16)); \n rgb.update(1, parse_int(color.get(2).concat(color.get(3)), 16)); \n rgb.update(2, parse_int(color.get(4).concat(color.get(5)), 16)); \n]; \n\n// Create output color\nvariable output: Color.Rgb; \noutput.r = rgb.get(0); \noutput.g = rgb.get(1); \noutput.b = rgb.get(2); \n\nreturn output;"
|
|
913
|
-
}
|
|
914
|
-
},
|
|
915
|
-
{
|
|
916
|
-
"source": "$self",
|
|
917
|
-
"target": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/",
|
|
918
|
-
"description": "Converts RGB to HEX",
|
|
919
|
-
"lossless": true,
|
|
920
|
-
"script": {
|
|
921
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
922
|
-
"script": "// RGB to Hex Conversion\n// Converts RGB (0-255) color to hexadecimal string format\n//\n// Examples:\n// RGB(255, 0, 0) → #ff0000\n// RGB(0, 255, 128) → #00ff80\n//\n// Algorithm:\n// 1. Round each channel to nearest integer\n// 2. Convert to base-16 string\n// 3. Pad single digits with leading zero\n// 4. Concatenate with '#' prefix\n//\n// Input: Color.Rgb with r, g, b in 0-255 range\n// Output: String (hex format #rrggbb)\n\nvariable rgba: List = {input}.r, {input}.g, {input}.b;\nvariable hex: String = \"#\";\nvariable i: Number = 0;\nvariable value: Number = 0;\n\n// Convert each RGB channel to hex\nwhile( i < min(rgba.length(), 3)) [\n value = round(rgba.get(i));\n if(value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n ] else [\n hex = hex.concat(value.to_string(16));\n ];\n i = i + 1;\n];\n\nif (rgba.length() == 4) [\n value = rgba.get(3) * 255; // Convert alpha to 0-255 range\n if(value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n ] else [\n hex = hex.concat(value.to_string(16));\n ];\n];\n\nreturn hex;"
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
],
|
|
926
|
-
"slug": "rgb-color"
|
|
927
|
-
},
|
|
928
|
-
{
|
|
929
|
-
"name": "SRGB",
|
|
930
|
-
"type": "color",
|
|
931
|
-
"description": "sRGB color space with normalized 0-1 range. The standard color space for web and displays.",
|
|
932
|
-
"schema": {
|
|
933
|
-
"type": "object",
|
|
934
|
-
"properties": {
|
|
935
|
-
"r": {
|
|
936
|
-
"type": "number",
|
|
937
|
-
"description": "Red channel (0-1)"
|
|
938
|
-
},
|
|
939
|
-
"g": {
|
|
940
|
-
"type": "number",
|
|
941
|
-
"description": "Green channel (0-1)"
|
|
942
|
-
},
|
|
943
|
-
"b": {
|
|
944
|
-
"type": "number",
|
|
945
|
-
"description": "Blue channel (0-1)"
|
|
946
|
-
}
|
|
947
|
-
},
|
|
948
|
-
"required": [
|
|
949
|
-
"r",
|
|
950
|
-
"g",
|
|
951
|
-
"b"
|
|
952
|
-
],
|
|
953
|
-
"order": [
|
|
954
|
-
"r",
|
|
955
|
-
"g",
|
|
956
|
-
"b"
|
|
957
|
-
],
|
|
958
|
-
"additionalProperties": false
|
|
959
|
-
},
|
|
960
|
-
"initializers": [
|
|
961
|
-
{
|
|
962
|
-
"title": "sRGB Color Initializer",
|
|
963
|
-
"keyword": "srgb",
|
|
964
|
-
"description": "Creates an sRGB color from normalized 0-1 values",
|
|
965
|
-
"script": {
|
|
966
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
967
|
-
"script": "// sRGB Color Initializer\n// Creates an sRGB color from normalized 0-1 values\n// Input: List of [r, g, b] or [r, g, b, alpha] values in 0-1 range\n\nvariable color_values: List = {input};\nvariable output: Color.SRGB;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (color_values.length() > 3) [\n output.alpha = color_values.get(3);\n];\n\nreturn output;"
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
],
|
|
971
|
-
"conversions": [
|
|
972
|
-
{
|
|
973
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/",
|
|
974
|
-
"target": "$self",
|
|
975
|
-
"description": "Converts RGB (0-255) to sRGB (0-1) by normalizing",
|
|
976
|
-
"lossless": true,
|
|
977
|
-
"script": {
|
|
978
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
979
|
-
"script": "// RGB to sRGB Conversion\n// Converts RGB (0-255) to sRGB (0-1) by normalizing\n// Input: Color.Rgb with r, g, b in 0-255 range\n// Output: Color.SRGB with r, g, b in 0-1 range\n// Lossless: Yes (simple division)\n\nvariable r_normalized: Number = {input}.r / 255;\nvariable g_normalized: Number = {input}.g / 255;\nvariable b_normalized: Number = {input}.b / 255;\n\nvariable output: Color.SRGB;\noutput.r = r_normalized;\noutput.g = g_normalized;\noutput.b = b_normalized;\n\nreturn output;"
|
|
980
|
-
}
|
|
981
|
-
},
|
|
982
|
-
{
|
|
983
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/",
|
|
984
|
-
"target": "$self",
|
|
985
|
-
"description": "Converts HSL to sRGB",
|
|
986
|
-
"lossless": true,
|
|
987
|
-
"script": {
|
|
988
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
989
|
-
"script": "// HSL to sRGB Conversion\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js\n//\n// Algorithm:\n// 1. If saturation is 0, it's achromatic: R=G=B=L\n// 2. Otherwise use the HSL to RGB formula:\n// - Calculate intermediate values based on L\n// - Use hue to determine RGB components\n//\n// Input: Color.HSL with h (0-360), s (0-1), l (0-1)\n// Output: Color.SRGB with r, g, b in 0-1 range\n\n// Get input HSL values\nvariable h: Number = {input}.h;\nvariable s: Number = {input}.s;\nvariable l: Number = {input}.l;\n\n// Normalize hue to 0-1 range\nvariable hue: Number = h / 360;\n\n// Output values\nvariable r: Number = l;\nvariable g: Number = l;\nvariable b: Number = l;\n\n// Only calculate if there's saturation (not achromatic)\nif (s > 0) [\n // Calculate intermediate value\n variable q: Number = 0;\n if (l < 0.5) [\n q = l * (1 + s);\n ] else [\n q = l + s - l * s;\n ];\n \n variable p: Number = 2 * l - q;\n \n // Helper function logic inlined for R (hue + 1/3)\n variable tr: Number = hue + 0.333333333333333;\n if (tr < 0) [ tr = tr + 1; ];\n if (tr > 1) [ tr = tr - 1; ];\n \n if (tr < 0.166666666666667) [\n r = p + (q - p) * 6 * tr;\n ] else [\n if (tr < 0.5) [\n r = q;\n ] else [\n if (tr < 0.666666666666667) [\n r = p + (q - p) * (0.666666666666667 - tr) * 6;\n ] else [\n r = p;\n ];\n ];\n ];\n \n // Helper function logic inlined for G (hue)\n variable tg: Number = hue;\n if (tg < 0) [ tg = tg + 1; ];\n if (tg > 1) [ tg = tg - 1; ];\n \n if (tg < 0.166666666666667) [\n g = p + (q - p) * 6 * tg;\n ] else [\n if (tg < 0.5) [\n g = q;\n ] else [\n if (tg < 0.666666666666667) [\n g = p + (q - p) * (0.666666666666667 - tg) * 6;\n ] else [\n g = p;\n ];\n ];\n ];\n \n // Helper function logic inlined for B (hue - 1/3)\n variable tb: Number = hue - 0.333333333333333;\n if (tb < 0) [ tb = tb + 1; ];\n if (tb > 1) [ tb = tb - 1; ];\n \n if (tb < 0.166666666666667) [\n b = p + (q - p) * 6 * tb;\n ] else [\n if (tb < 0.5) [\n b = q;\n ] else [\n if (tb < 0.666666666666667) [\n b = p + (q - p) * (0.666666666666667 - tb) * 6;\n ] else [\n b = p;\n ];\n ];\n ];\n];\n\n// Create output\nvariable output: Color.SRGB;\noutput.r = r;\noutput.g = g;\noutput.b = b;\n\nreturn output;"
|
|
990
|
-
}
|
|
991
|
-
},
|
|
992
|
-
{
|
|
993
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/",
|
|
994
|
-
"target": "$self",
|
|
995
|
-
"description": "Converts Linear sRGB to sRGB by applying gamma correction",
|
|
996
|
-
"lossless": true,
|
|
997
|
-
"script": {
|
|
998
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
999
|
-
"script": "// Linear sRGB to sRGB Conversion\n// Applies gamma correction (transfer function)\n// Reference: IEC 61966-2-1:1999 (sRGB specification)\n//\n// Algorithm:\n// if linear ≤ 0.0031308: srgb = linear * 12.92\n// else: srgb = 1.055 * linear^(1/2.4) - 0.055\n//\n// Input: Color.LinearSRGB with r, g, b in linear 0-1 range\n// Output: Color.SRGB with r, g, b in gamma-corrected 0-1 range\n\n// Gamma correction constants (IEC 61966-2-1)\nvariable threshold: Number = 0.0031308;\nvariable linear_scale: Number = 12.92;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_exponent: Number = 0.416666666666667;\n\n// Get input linear values\nvariable linear_r: Number = {input}.r;\nvariable linear_g: Number = {input}.g;\nvariable linear_b: Number = {input}.b;\n\n// Convert red channel\nvariable srgb_r: Number = 0;\nif (linear_r <= threshold) [\n srgb_r = linear_r * linear_scale;\n] else [\n srgb_r = gamma_scale * pow(linear_r, gamma_exponent) - gamma_offset;\n];\n\n// Convert green channel\nvariable srgb_g: Number = 0;\nif (linear_g <= threshold) [\n srgb_g = linear_g * linear_scale;\n] else [\n srgb_g = gamma_scale * pow(linear_g, gamma_exponent) - gamma_offset;\n];\n\n// Convert blue channel\nvariable srgb_b: Number = 0;\nif (linear_b <= threshold) [\n srgb_b = linear_b * linear_scale;\n] else [\n srgb_b = gamma_scale * pow(linear_b, gamma_exponent) - gamma_offset;\n];\n\n// Create output\nvariable output: Color.SRGB;\noutput.r = srgb_r;\noutput.g = srgb_g;\noutput.b = srgb_b;\n\nreturn output;"
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
],
|
|
1003
|
-
"slug": "srgb-color"
|
|
1004
|
-
},
|
|
1005
|
-
{
|
|
1006
|
-
"name": "LinearSRGB",
|
|
1007
|
-
"type": "color",
|
|
1008
|
-
"description": "Linear sRGB color space (gamma-decoded). Used for matrix transformations to XYZ and other linear operations.",
|
|
1009
|
-
"schema": {
|
|
1010
|
-
"type": "object",
|
|
1011
|
-
"properties": {
|
|
1012
|
-
"r": {
|
|
1013
|
-
"type": "number",
|
|
1014
|
-
"description": "Linear red channel (0-1)"
|
|
1015
|
-
},
|
|
1016
|
-
"g": {
|
|
1017
|
-
"type": "number",
|
|
1018
|
-
"description": "Linear green channel (0-1)"
|
|
1019
|
-
},
|
|
1020
|
-
"b": {
|
|
1021
|
-
"type": "number",
|
|
1022
|
-
"description": "Linear blue channel (0-1)"
|
|
1023
|
-
}
|
|
1024
|
-
},
|
|
1025
|
-
"required": [
|
|
1026
|
-
"r",
|
|
1027
|
-
"g",
|
|
1028
|
-
"b"
|
|
1029
|
-
],
|
|
1030
|
-
"order": [
|
|
1031
|
-
"r",
|
|
1032
|
-
"g",
|
|
1033
|
-
"b"
|
|
1034
|
-
],
|
|
1035
|
-
"additionalProperties": false
|
|
1036
|
-
},
|
|
1037
|
-
"initializers": [
|
|
1038
|
-
{
|
|
1039
|
-
"title": "Linear sRGB Color Initializer",
|
|
1040
|
-
"keyword": "linearsrgb",
|
|
1041
|
-
"description": "Creates a linear sRGB color from linear 0-1 values",
|
|
1042
|
-
"script": {
|
|
1043
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1044
|
-
"script": "// Linear sRGB Color Initializer\n// Creates a linear sRGB color from linear 0-1 values\n// Input: List of [r, g, b] linear values\n\nvariable color_values: List = {input};\nvariable output: Color.LinearSRGB;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\nreturn output;"
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
],
|
|
1048
|
-
"conversions": [
|
|
1049
|
-
{
|
|
1050
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1051
|
-
"target": "$self",
|
|
1052
|
-
"description": "Converts sRGB to Linear sRGB by removing gamma correction (IEC 61966-2-1)",
|
|
1053
|
-
"lossless": true,
|
|
1054
|
-
"script": {
|
|
1055
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1056
|
-
"script": "// sRGB to Linear sRGB Conversion\n// Removes gamma correction (inverse transfer function)\n// Reference: IEC 61966-2-1:1999 (sRGB specification)\n//\n// Algorithm:\n// if srgb ≤ 0.04045: linear = srgb / 12.92\n// else: linear = ((srgb + 0.055) / 1.055) ^ 2.4\n//\n// Input: Color.SRGB with r, g, b in 0-1 range\n// Output: Color.LinearSRGB with r, g, b in linear 0-1 range\n\n// Gamma correction constants (IEC 61966-2-1)\nvariable threshold: Number = 0.04045;\nvariable linear_scale: Number = 12.92;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_exponent: Number = 2.4;\n\n// Get input sRGB values\nvariable srgb_r: Number = {input}.r;\nvariable srgb_g: Number = {input}.g;\nvariable srgb_b: Number = {input}.b;\n\n// Convert red channel\nvariable linear_r: Number = 0;\nif (srgb_r <= threshold) [\n linear_r = srgb_r / linear_scale;\n] else [\n linear_r = pow((srgb_r + gamma_offset) / gamma_scale, gamma_exponent);\n];\n\n// Convert green channel\nvariable linear_g: Number = 0;\nif (srgb_g <= threshold) [\n linear_g = srgb_g / linear_scale;\n] else [\n linear_g = pow((srgb_g + gamma_offset) / gamma_scale, gamma_exponent);\n];\n\n// Convert blue channel\nvariable linear_b: Number = 0;\nif (srgb_b <= threshold) [\n linear_b = srgb_b / linear_scale;\n] else [\n linear_b = pow((srgb_b + gamma_offset) / gamma_scale, gamma_exponent);\n];\n\n// Create output\nvariable output: Color.LinearSRGB;\noutput.r = linear_r;\noutput.g = linear_g;\noutput.b = linear_b;\n\nreturn output;"
|
|
1057
|
-
}
|
|
1058
|
-
},
|
|
1059
|
-
{
|
|
1060
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/",
|
|
1061
|
-
"target": "$self",
|
|
1062
|
-
"description": "Converts XYZ-D65 to Linear sRGB using inverse transformation matrix",
|
|
1063
|
-
"lossless": true,
|
|
1064
|
-
"script": {
|
|
1065
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1066
|
-
"script": "// XYZ-D65 to Linear sRGB Conversion\n// Reference: ColorJS exact matrix values (extracted via unit vector conversion)\n//\n// Matrix (XYZ D65 to Linear sRGB):\n// [ 3.2409699419045226, -1.537383177570094, -0.4986107602930034 ]\n// [-0.9692436362808796, 1.8759675015077202, 0.04155505740717559]\n// [ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786]\n//\n// Input: Color.XYZD65 with x, y, z tristimulus values\n// Output: Color.LinearSRGB with r, g, b in linear 0-1 range\n\n// Get input XYZ values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// Apply inverse matrix transformation (ColorJS exact values)\n// Row 1: R\nvariable linear_r: Number = 3.2409699419045226 * x + -1.537383177570094 * y + -0.4986107602930034 * z;\n\n// Row 2: G\nvariable linear_g: Number = -0.9692436362808796 * x + 1.8759675015077202 * y + 0.04155505740717559 * z;\n\n// Row 3: B\nvariable linear_b: Number = 0.05563007969699366 * x + -0.20397695888897652 * y + 1.0569715142428786 * z;\n\n// Create output\nvariable output: Color.LinearSRGB;\noutput.r = linear_r;\noutput.g = linear_g;\noutput.b = linear_b;\n\nreturn output;"
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
],
|
|
1070
|
-
"slug": "srgb-linear-color"
|
|
1071
|
-
},
|
|
1072
|
-
{
|
|
1073
|
-
"name": "XYZD50",
|
|
1074
|
-
"type": "color",
|
|
1075
|
-
"description": "CIE XYZ color space with D50 white point. Used for Lab and LCH color spaces.",
|
|
1076
|
-
"schema": {
|
|
1077
|
-
"type": "object",
|
|
1078
|
-
"properties": {
|
|
1079
|
-
"x": {
|
|
1080
|
-
"type": "number",
|
|
1081
|
-
"description": "X tristimulus value"
|
|
1082
|
-
},
|
|
1083
|
-
"y": {
|
|
1084
|
-
"type": "number",
|
|
1085
|
-
"description": "Y tristimulus value (luminance)"
|
|
1086
|
-
},
|
|
1087
|
-
"z": {
|
|
1088
|
-
"type": "number",
|
|
1089
|
-
"description": "Z tristimulus value"
|
|
1090
|
-
}
|
|
1091
|
-
},
|
|
1092
|
-
"required": [
|
|
1093
|
-
"x",
|
|
1094
|
-
"y",
|
|
1095
|
-
"z"
|
|
1096
|
-
],
|
|
1097
|
-
"order": [
|
|
1098
|
-
"x",
|
|
1099
|
-
"y",
|
|
1100
|
-
"z"
|
|
1101
|
-
],
|
|
1102
|
-
"additionalProperties": false
|
|
1103
|
-
},
|
|
1104
|
-
"initializers": [
|
|
1105
|
-
{
|
|
1106
|
-
"title": "XYZ-D50 Color Initializer",
|
|
1107
|
-
"keyword": "xyzd50",
|
|
1108
|
-
"description": "Creates an XYZ-D50 color from tristimulus values",
|
|
1109
|
-
"script": {
|
|
1110
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1111
|
-
"script": "// XYZ-D50 Color Initializer\n// Creates an XYZ-D50 color from tristimulus values\n// Input: List of [x, y, z] tristimulus values\n\nvariable xyz_values: List = {input};\nvariable output: Color.XYZD50;\n\noutput.x = xyz_values.get(0);\noutput.y = xyz_values.get(1);\noutput.z = xyz_values.get(2);\n\nreturn output;"
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
],
|
|
1115
|
-
"conversions": [
|
|
1116
|
-
{
|
|
1117
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/",
|
|
1118
|
-
"target": "$self",
|
|
1119
|
-
"description": "Converts XYZ-D65 to XYZ-D50 using Bradford chromatic adaptation",
|
|
1120
|
-
"lossless": true,
|
|
1121
|
-
"script": {
|
|
1122
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1123
|
-
"script": "// XYZ-D65 to XYZ-D50 Conversion\n// Chromatic adaptation using Bradford transform\n// Reference: https://github.com/color-js/color.js/blob/main/src/adapt.js\n//\n// Bradford CAT matrix for D65 → D50:\n// [1.0479298 0.0229469 -0.0501923]\n// [0.0296278 0.9904344 -0.0170738]\n// [-0.0092430 0.0150552 0.7518743]\n//\n// Input: Color.XYZD65 with x, y, z tristimulus (D65 white)\n// Output: Color.XYZD50 with x, y, z tristimulus (D50 white)\n\n// Get input XYZ-D65 values\nvariable x65: Number = {input}.x;\nvariable y65: Number = {input}.y;\nvariable z65: Number = {input}.z;\n\n// Bradford chromatic adaptation matrix (ColorJS exact values)\n// Row 1\nvariable x50: Number = x65 * 1.0479297925449969 + y65 * 0.022946870601609652 + z65 * -0.05019226628920524;\n// Row 2\nvariable y50: Number = x65 * 0.02962780877005599 + y65 * 0.9904344267538799 + z65 * -0.017073799063418826;\n// Row 3\nvariable z50: Number = x65 * -0.009243040646204504 + y65 * 0.015055191490298152 + z65 * 0.7518742814281371;\n\n// Create output\nvariable output: Color.XYZD50;\noutput.x = x50;\noutput.y = y50;\noutput.z = z50;\n\nreturn output;"
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
],
|
|
1127
|
-
"slug": "xyz-d50-color"
|
|
1128
|
-
},
|
|
1129
|
-
{
|
|
1130
|
-
"name": "XYZD65",
|
|
1131
|
-
"type": "color",
|
|
1132
|
-
"description": "CIE XYZ color space with D65 white point. The primary connection hub for color space conversions.",
|
|
1133
|
-
"schema": {
|
|
1134
|
-
"type": "object",
|
|
1135
|
-
"properties": {
|
|
1136
|
-
"x": {
|
|
1137
|
-
"type": "number",
|
|
1138
|
-
"description": "X tristimulus value"
|
|
1139
|
-
},
|
|
1140
|
-
"y": {
|
|
1141
|
-
"type": "number",
|
|
1142
|
-
"description": "Y tristimulus value (luminance)"
|
|
1143
|
-
},
|
|
1144
|
-
"z": {
|
|
1145
|
-
"type": "number",
|
|
1146
|
-
"description": "Z tristimulus value"
|
|
1147
|
-
}
|
|
1148
|
-
},
|
|
1149
|
-
"required": [
|
|
1150
|
-
"x",
|
|
1151
|
-
"y",
|
|
1152
|
-
"z"
|
|
1153
|
-
],
|
|
1154
|
-
"order": [
|
|
1155
|
-
"x",
|
|
1156
|
-
"y",
|
|
1157
|
-
"z"
|
|
1158
|
-
],
|
|
1159
|
-
"additionalProperties": false
|
|
1160
|
-
},
|
|
1161
|
-
"initializers": [
|
|
1162
|
-
{
|
|
1163
|
-
"title": "XYZ-D65 Color Initializer",
|
|
1164
|
-
"keyword": "xyzd65",
|
|
1165
|
-
"description": "Creates an XYZ-D65 color from tristimulus values",
|
|
1166
|
-
"script": {
|
|
1167
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1168
|
-
"script": "// XYZ-D65 Color Initializer\n// Creates an XYZ-D65 color from tristimulus values\n// Input: List of [x, y, z] tristimulus values\n\nvariable xyz_values: List = {input};\nvariable output: Color.XYZD65;\n\noutput.x = xyz_values.get(0);\noutput.y = xyz_values.get(1);\noutput.z = xyz_values.get(2);\n\nreturn output;"
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
],
|
|
1172
|
-
"conversions": [
|
|
1173
|
-
{
|
|
1174
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/",
|
|
1175
|
-
"target": "$self",
|
|
1176
|
-
"description": "Converts Linear sRGB to XYZ-D65 using the sRGB transformation matrix",
|
|
1177
|
-
"lossless": true,
|
|
1178
|
-
"script": {
|
|
1179
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1180
|
-
"script": "// Linear sRGB to XYZ-D65 Conversion\n// Uses the sRGB primaries transformation matrix\n// Reference: IEC 61966-2-1:1999 (sRGB specification)\n//\n// Matrix (Linear RGB → XYZ-D65):\n// [X] [0.4123908 0.3575843 0.1804808] [R]\n// [Y] = [0.2126390 0.7151687 0.0721923] [G]\n// [Z] [0.0193308 0.1191948 0.9505322] [B]\n//\n// Input: Color.LinearSRGB with r, g, b in linear 0-1 range\n// Output: Color.XYZD65 with x, y, z tristimulus values\n\n// Get input linear sRGB values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Matrix multiplication: M × [R, G, B]ᵀ\n// Row 1: X = 0.4123908 * R + 0.3575843 * G + 0.1804808 * B\nvariable x: Number = r * 0.41239079926595934 + g * 0.357584339383878 + b * 0.1804807884018343;\n\n// Row 2: Y = 0.2126390 * R + 0.7151687 * G + 0.0721923 * B\nvariable y: Number = r * 0.21263900587151027 + g * 0.715168678767756 + b * 0.07219231536073371;\n\n// Row 3: Z = 0.0193308 * R + 0.1191948 * G + 0.9505322 * B\nvariable z: Number = r * 0.01933081871559182 + g * 0.11919477979462598 + b * 0.9505321522496607;\n\n// Create output\nvariable output: Color.XYZD65;\noutput.x = x;\noutput.y = y;\noutput.z = z;\n\nreturn output;"
|
|
1181
|
-
}
|
|
1182
|
-
},
|
|
1183
|
-
{
|
|
1184
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-linear-color/0/",
|
|
1185
|
-
"target": "$self",
|
|
1186
|
-
"description": "Converts Linear P3 to XYZ-D65 using the P3 transformation matrix",
|
|
1187
|
-
"lossless": true,
|
|
1188
|
-
"script": {
|
|
1189
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1190
|
-
"script": "// Linear P3 to XYZ-D65 Conversion\n// Converts linear Display-P3 RGB to CIE XYZ (D65)\n// Reference: CSS Color Level 4 specification\n//\n// Uses the P3 to XYZ-D65 transformation matrix\n// Matrix values from ColorJS / CSS Color Level 4\n//\n// Input: Color.LinearP3 with linear r, g, b values\n// Output: Color.XYZD65 with x, y, z tristimulus values\n\n// Get linear P3 values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Linear P3 to XYZ-D65 matrix\n// Row 1\nvariable m00: Number = 0.4865709486482162;\nvariable m01: Number = 0.26566769316909306;\nvariable m02: Number = 0.1982172852343625;\n\n// Row 2\nvariable m10: Number = 0.2289745640697488;\nvariable m11: Number = 0.6917385218365064;\nvariable m12: Number = 0.079286914093745;\n\n// Row 3\nvariable m20: Number = 0.0;\nvariable m21: Number = 0.04511338185890264;\nvariable m22: Number = 1.043944368900976;\n\n// Matrix multiplication: [x, y, z] = M × [r, g, b]\nvariable x: Number = m00 * r + m01 * g + m02 * b;\nvariable y: Number = m10 * r + m11 * g + m12 * b;\nvariable z: Number = m20 * r + m21 * g + m22 * b;\n\n// Create output\nvariable output: Color.XYZD65;\noutput.x = x;\noutput.y = y;\noutput.z = z;\n\nreturn output;"
|
|
1191
|
-
}
|
|
1192
|
-
},
|
|
1193
|
-
{
|
|
1194
|
-
"source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
1195
|
-
"target": "$self",
|
|
1196
|
-
"description": "Converts OKLab to XYZ-D65 using inverse LMS transformation",
|
|
1197
|
-
"lossless": true,
|
|
1198
|
-
"script": {
|
|
1199
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1200
|
-
"script": "// OKLab to XYZ-D65 Conversion\n// Reference: ColorJS oklab.js (inverse transformation)\n//\n// Algorithm:\n// 1. Apply inverse Lab-to-LMS matrix to get LMS cone responses\n// 2. Cube the LMS values (inverse of cube root)\n// 3. Apply inverse LMS-to-XYZ matrix\n//\n// Input: Color.OKLab with l, a, b perceptual coordinates\n// Output: Color.XYZD65 with x, y, z tristimulus values\n\n// Get input OKLab values\nvariable ok_l: Number = {input}.l;\nvariable ok_a: Number = {input}.a;\nvariable ok_b: Number = {input}.b;\n\n// Inverse LMStoLab_M matrix (Lab to LMS')\n// These are the inverse of the matrix used in from-xyz-d65.tokenscript\nvariable lms_l: Number = 1.0 * ok_l + 0.3963377773761749 * ok_a + 0.2158037573099136 * ok_b;\nvariable lms_m: Number = 1.0 * ok_l + -0.1055613458156586 * ok_a + -0.0638541728258133 * ok_b;\nvariable lms_s: Number = 1.0 * ok_l + -0.0894841775298119 * ok_a + -1.2914855480194092 * ok_b;\n\n// Cube the values (inverse of cube root)\nvariable lms_l_cubed: Number = lms_l * lms_l * lms_l;\nvariable lms_m_cubed: Number = lms_m * lms_m * lms_m;\nvariable lms_s_cubed: Number = lms_s * lms_s * lms_s;\n\n// Inverse XYZtoLMS_M matrix (LMS to XYZ)\n// From ColorJS oklab.js\nvariable x: Number = 1.2268798758459243 * lms_l_cubed + -0.5578149944602171 * lms_m_cubed + 0.2813910456659647 * lms_s_cubed;\nvariable y: Number = -0.0405757452148008 * lms_l_cubed + 1.1122868032803170 * lms_m_cubed + -0.0717110580655164 * lms_s_cubed;\nvariable z: Number = -0.0763729366746601 * lms_l_cubed + -0.4214933324022432 * lms_m_cubed + 1.5869240198367816 * lms_s_cubed;\n\n// Create output\nvariable output: Color.XYZD65;\noutput.x = x;\noutput.y = y;\noutput.z = z;\n\nreturn output;"
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
],
|
|
1204
|
-
"slug": "xyz-d65-color"
|
|
1205
|
-
}
|
|
1206
|
-
],
|
|
1207
|
-
"functions": [
|
|
1208
|
-
{
|
|
1209
|
-
"name": "adjust_chroma",
|
|
1210
|
-
"type": "function",
|
|
1211
|
-
"description": "Adjusts a color's chroma by a relative amount. Positive values increase saturation, negative values decrease it. Unlike set_chroma which sets an absolute value, adjust_chroma adds to the current chroma. Result is clamped to valid range [0, ~0.4].",
|
|
1212
|
-
"keyword": "adjust_chroma",
|
|
1213
|
-
"requirements": [
|
|
1214
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1215
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
1216
|
-
],
|
|
1217
|
-
"schema": {
|
|
1218
|
-
"type": "object",
|
|
1219
|
-
"properties": {
|
|
1220
|
-
"input": {
|
|
1221
|
-
"type": "array",
|
|
1222
|
-
"items": [
|
|
1223
|
-
{
|
|
1224
|
-
"description": "Color to adjust",
|
|
1225
|
-
"type": "color"
|
|
1226
|
-
},
|
|
1227
|
-
{
|
|
1228
|
-
"description": "Amount to adjust chroma (+/-)",
|
|
1229
|
-
"type": "number"
|
|
1230
|
-
}
|
|
1231
|
-
],
|
|
1232
|
-
"minItems": 2,
|
|
1233
|
-
"maxItems": 2
|
|
1234
|
-
}
|
|
1235
|
-
},
|
|
1236
|
-
"required": [
|
|
1237
|
-
"input"
|
|
1238
|
-
]
|
|
1239
|
-
},
|
|
1240
|
-
"returns": {
|
|
1241
|
-
"type": "color",
|
|
1242
|
-
"description": "Color with adjusted chroma"
|
|
1243
|
-
},
|
|
1244
|
-
"script": {
|
|
1245
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1246
|
-
"script": "// adjust_chroma: Adjust chroma by a relative amount\n//\n// Modifies the chroma (colorfulness) of a color by adding the\n// specified amount. Positive values increase saturation,\n// negative values decrease it. Result is clamped to [0, ∞).\n//\n// Uses OKLCH for perceptually uniform results.\n// Preserves lightness and hue.\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable amount: Number = input.get(1);\n\n// Calculate new chroma\nvariable new_c: Number = color.c + amount;\n\n// Clamp to valid range (chroma cannot be negative)\nif (new_c < 0) [ new_c = 0; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
1247
|
-
},
|
|
1248
|
-
"examples": [
|
|
1249
|
-
{
|
|
1250
|
-
"description": "Increase saturation",
|
|
1251
|
-
"input": [
|
|
1252
|
-
"#808080",
|
|
1253
|
-
0.1
|
|
1254
|
-
],
|
|
1255
|
-
"output": "More saturated color"
|
|
1256
|
-
},
|
|
1257
|
-
{
|
|
1258
|
-
"description": "Decrease saturation",
|
|
1259
|
-
"input": [
|
|
1260
|
-
"#ff6600",
|
|
1261
|
-
-0.1
|
|
1262
|
-
],
|
|
1263
|
-
"output": "Less saturated orange"
|
|
1264
|
-
}
|
|
1265
|
-
],
|
|
1266
|
-
"slug": "adjust_chroma"
|
|
1267
|
-
},
|
|
1268
|
-
{
|
|
1269
|
-
"name": "adjust_hue",
|
|
1270
|
-
"type": "function",
|
|
1271
|
-
"description": "Adjusts a color's hue by a relative amount in degrees. Positive values rotate clockwise, negative values counter-clockwise. Unlike set_hue which sets an absolute value, adjust_hue adds to the current hue. Uses OKLCH for perceptually uniform hue rotation.",
|
|
1272
|
-
"keyword": "adjust_hue",
|
|
1273
|
-
"requirements": [
|
|
1274
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1275
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
1276
|
-
],
|
|
1277
|
-
"schema": {
|
|
1278
|
-
"type": "object",
|
|
1279
|
-
"properties": {
|
|
1280
|
-
"input": {
|
|
1281
|
-
"type": "array",
|
|
1282
|
-
"items": [
|
|
1283
|
-
{
|
|
1284
|
-
"description": "Color to adjust",
|
|
1285
|
-
"type": "color"
|
|
1286
|
-
},
|
|
1287
|
-
{
|
|
1288
|
-
"description": "Degrees to rotate hue (+ or -)",
|
|
1289
|
-
"type": "number"
|
|
1290
|
-
}
|
|
1291
|
-
],
|
|
1292
|
-
"minItems": 2,
|
|
1293
|
-
"maxItems": 2
|
|
1294
|
-
}
|
|
1295
|
-
},
|
|
1296
|
-
"required": [
|
|
1297
|
-
"input"
|
|
1298
|
-
]
|
|
1299
|
-
},
|
|
1300
|
-
"returns": {
|
|
1301
|
-
"type": "color",
|
|
1302
|
-
"description": "Color with adjusted hue"
|
|
1303
|
-
},
|
|
1304
|
-
"script": {
|
|
1305
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1306
|
-
"script": "// adjust_hue: Rotate hue by a relative angle\n//\n// Adds the specified degrees to the color's hue, wrapping\n// around the 360° color wheel. Positive values rotate clockwise,\n// negative values rotate counter-clockwise.\n//\n// Uses OKLCH for perceptually uniform hue rotation.\n// Preserves lightness and chroma.\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable degrees: Number = input.get(1);\n\n// Calculate new hue\nvariable new_h: Number = color.h + degrees;\n\n// Normalize to 0-360\nwhile (new_h < 0) [\n new_h = new_h + 360;\n];\nwhile (new_h >= 360) [\n new_h = new_h - 360;\n];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = new_h;\n\nreturn result;"
|
|
1307
|
-
},
|
|
1308
|
-
"examples": [
|
|
1309
|
-
{
|
|
1310
|
-
"description": "Shift red toward orange",
|
|
1311
|
-
"input": [
|
|
1312
|
-
"#ff0000",
|
|
1313
|
-
30
|
|
1314
|
-
],
|
|
1315
|
-
"output": "Orange color"
|
|
1316
|
-
},
|
|
1317
|
-
{
|
|
1318
|
-
"description": "Shift blue toward green",
|
|
1319
|
-
"input": [
|
|
1320
|
-
"#0000ff",
|
|
1321
|
-
-60
|
|
1322
|
-
],
|
|
1323
|
-
"output": "Cyan color"
|
|
1324
|
-
}
|
|
1325
|
-
],
|
|
1326
|
-
"slug": "adjust_hue"
|
|
1327
|
-
},
|
|
1328
|
-
{
|
|
1329
|
-
"name": "adjust_lightness",
|
|
1330
|
-
"type": "function",
|
|
1331
|
-
"description": "Adjusts a color's lightness by a relative amount. Positive values make it lighter, negative values make it darker. Unlike set_lightness which sets an absolute value, adjust_lightness adds to the current lightness. Result is clamped to valid range [0, 1].",
|
|
1332
|
-
"keyword": "adjust_lightness",
|
|
1333
|
-
"requirements": [
|
|
1334
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1335
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
1336
|
-
],
|
|
1337
|
-
"schema": {
|
|
1338
|
-
"type": "object",
|
|
1339
|
-
"properties": {
|
|
1340
|
-
"input": {
|
|
1341
|
-
"type": "array",
|
|
1342
|
-
"items": [
|
|
1343
|
-
{
|
|
1344
|
-
"description": "Color to adjust",
|
|
1345
|
-
"type": "color"
|
|
1346
|
-
},
|
|
1347
|
-
{
|
|
1348
|
-
"description": "Amount to adjust lightness (+/-)",
|
|
1349
|
-
"type": "number"
|
|
1350
|
-
}
|
|
1351
|
-
],
|
|
1352
|
-
"minItems": 2,
|
|
1353
|
-
"maxItems": 2
|
|
1354
|
-
}
|
|
1355
|
-
},
|
|
1356
|
-
"required": [
|
|
1357
|
-
"input"
|
|
1358
|
-
]
|
|
1359
|
-
},
|
|
1360
|
-
"returns": {
|
|
1361
|
-
"type": "color",
|
|
1362
|
-
"description": "Color with adjusted lightness"
|
|
1363
|
-
},
|
|
1364
|
-
"script": {
|
|
1365
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1366
|
-
"script": "// adjust_lightness: Adjust lightness by a relative amount\n//\n// Modifies the lightness of a color by adding the specified\n// amount. Positive values lighten, negative values darken.\n// Result is clamped to [0, 1].\n//\n// Preserves hue and chroma.\n//\n// Input: Any color space (converted to OKLCH internally)\n// Output: OKLCH (working space)\n// To get sRGB: adjust_lightness(color, 0.2).to.srgb()\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable amount: Number = input.get(1);\n\n// Calculate new lightness\nvariable new_l: Number = color.l + amount;\n\n// Clamp to valid range\nif (new_l < 0) [ new_l = 0; ];\nif (new_l > 1) [ new_l = 1; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = new_l;\nresult.c = color.c;\nresult.h = color.h;\n\nreturn result;"
|
|
1367
|
-
},
|
|
1368
|
-
"examples": [
|
|
1369
|
-
{
|
|
1370
|
-
"description": "Lighten a color",
|
|
1371
|
-
"input": [
|
|
1372
|
-
"#808080",
|
|
1373
|
-
0.2
|
|
1374
|
-
],
|
|
1375
|
-
"output": "Lighter gray"
|
|
1376
|
-
},
|
|
1377
|
-
{
|
|
1378
|
-
"description": "Darken a color",
|
|
1379
|
-
"input": [
|
|
1380
|
-
"#808080",
|
|
1381
|
-
-0.3
|
|
1382
|
-
],
|
|
1383
|
-
"output": "Darker gray"
|
|
1384
|
-
}
|
|
1385
|
-
],
|
|
1386
|
-
"slug": "adjust_lightness"
|
|
1387
|
-
},
|
|
1388
|
-
{
|
|
1389
|
-
"name": "adjust_to_contrast",
|
|
1390
|
-
"type": "function",
|
|
1391
|
-
"description": "Adjusts a foreground color's lightness to achieve a target WCAG contrast ratio against a background. Preserves hue and chroma while finding the optimal lightness. Uses binary search in OKLCH space for perceptually even adjustments. If target contrast cannot be achieved (e.g., trying to get 21:1 with mid-gray), returns the best possible result.",
|
|
1392
|
-
"keyword": "adjust_to_contrast",
|
|
1393
|
-
"requirements": [
|
|
1394
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1395
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
1396
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/"
|
|
1397
|
-
],
|
|
1398
|
-
"schema": {
|
|
1399
|
-
"type": "object",
|
|
1400
|
-
"properties": {
|
|
1401
|
-
"input": {
|
|
1402
|
-
"type": "array",
|
|
1403
|
-
"items": [
|
|
1404
|
-
{
|
|
1405
|
-
"description": "Foreground color to adjust",
|
|
1406
|
-
"type": "color"
|
|
1407
|
-
},
|
|
1408
|
-
{
|
|
1409
|
-
"description": "Background color (fixed)",
|
|
1410
|
-
"type": "color"
|
|
1411
|
-
},
|
|
1412
|
-
{
|
|
1413
|
-
"description": "Target contrast ratio (default 4.5 for WCAG AA)",
|
|
1414
|
-
"type": "number"
|
|
1415
|
-
}
|
|
1416
|
-
],
|
|
1417
|
-
"minItems": 2,
|
|
1418
|
-
"maxItems": 3
|
|
1419
|
-
}
|
|
1420
|
-
},
|
|
1421
|
-
"required": [
|
|
1422
|
-
"input"
|
|
1423
|
-
]
|
|
1424
|
-
},
|
|
1425
|
-
"returns": {
|
|
1426
|
-
"type": "color",
|
|
1427
|
-
"description": "Adjusted foreground color meeting the target contrast"
|
|
1428
|
-
},
|
|
1429
|
-
"script": {
|
|
1430
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1431
|
-
"script": "// adjust_to_contrast: Adjust foreground to meet target WCAG contrast ratio\n// Reference: WCAG 2.1 Contrast Ratio Definition\n// Reference: https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio\n//\n// Modifies the foreground color's lightness (in OKLCH) to achieve\n// the specified contrast ratio against the background, while\n// preserving hue and chroma.\n//\n// Parameters:\n// foreground - Color to adjust\n// background - Reference background color\n// target_ratio - Minimum contrast ratio (default: 4.5 for WCAG AA)\n//\n// Uses binary search in OKLCH lightness for convergence.\n//\n// Input: Any color space (converted to OKLCH internally)\n// Output: OKLCH (working space)\n// To get sRGB: adjust_to_contrast(fg, bg, 4.5).to.srgb()\n\nvariable input: List = {input};\nvariable foreground: Color.OKLCH = input.get(0).to.oklch();\nvariable bg_xyz: Color.XYZD65 = input.get(1).to.xyzd65();\n\n// Default to WCAG AA for normal text\nvariable target_ratio: Number = 4.5;\nif (input.length() > 2) [\n target_ratio = input.get(2);\n];\n\n// Get background luminance\nvariable bg_lum: Number = bg_xyz.y;\n\n// Determine if we need to go lighter or darker\n// Test with current foreground\nvariable test_oklch: Color.OKLCH;\ntest_oklch.l = foreground.l;\ntest_oklch.c = foreground.c;\ntest_oklch.h = foreground.h;\n\nvariable test_xyz: Color.XYZD65 = test_oklch.to.srgb().to.xyzd65();\nvariable fg_lum: Number = test_xyz.y;\n\n// Calculate current contrast\nvariable l1: Number = fg_lum;\nvariable l2: Number = bg_lum;\nif (l2 > l1) [\n l1 = bg_lum;\n l2 = fg_lum;\n];\nvariable current_ratio: Number = (l1 + 0.05) / (l2 + 0.05);\n\n// If already meets target, return as-is (in OKLCH working space)\nif (current_ratio >= target_ratio) [\n return foreground;\n];\n\n// Determine direction: if background is dark, we need lighter foreground and vice versa\nvariable search_min: Number = 0;\nvariable search_max: Number = 1;\n\n// Binary search for correct lightness\nvariable epsilon: Number = 0.001;\nvariable iterations: Number = 0;\nvariable max_iterations: Number = 30;\nvariable best_l: Number = foreground.l;\nvariable best_ratio: Number = current_ratio;\n\nvariable diff: Number = search_max - search_min;\nvariable mid_l: Number = 0.5;\nvariable test_ratio: Number = 1;\n\nwhile (diff > epsilon) [\n if (iterations >= max_iterations) [\n diff = 0; // Force exit\n ];\n mid_l = (search_min + search_max) / 2;\n \n // Test this lightness\n test_oklch.l = mid_l;\n test_oklch.c = foreground.c;\n test_oklch.h = foreground.h;\n \n // Convert to get luminance\n test_xyz = test_oklch.to.srgb().to.xyzd65();\n fg_lum = test_xyz.y;\n \n // Calculate contrast\n l1 = fg_lum;\n l2 = bg_lum;\n if (l2 > l1) [\n l1 = bg_lum;\n l2 = fg_lum;\n ];\n test_ratio = (l1 + 0.05) / (l2 + 0.05);\n \n // Track best result\n if (test_ratio >= target_ratio) [\n // This lightness works, but can we get closer to original?\n // If background is dark (low lum), we want darker foreground closer to original\n // If background is light (high lum), we want lighter foreground closer to original\n if (bg_lum < 0.18) [\n // Dark background: search lower (darker)\n search_max = mid_l;\n ] else [\n // Light background: search higher (lighter)\n search_min = mid_l;\n ];\n best_l = mid_l;\n best_ratio = test_ratio;\n ] else [\n // Not enough contrast, need more separation\n if (bg_lum < 0.18) [\n // Dark background: need lighter foreground\n search_min = mid_l;\n ] else [\n // Light background: need darker foreground\n search_max = mid_l;\n ];\n ];\n \n iterations = iterations + 1;\n diff = search_max - search_min;\n];\n\n// Apply best lightness found, return in OKLCH working space\nvariable result: Color.OKLCH;\nresult.l = best_l;\nresult.c = foreground.c;\nresult.h = foreground.h;\n\nreturn result;"
|
|
1432
|
-
},
|
|
1433
|
-
"examples": [
|
|
1434
|
-
{
|
|
1435
|
-
"description": "Adjust light gray to meet AA on white",
|
|
1436
|
-
"input": [
|
|
1437
|
-
"#cccccc",
|
|
1438
|
-
"#ffffff",
|
|
1439
|
-
4.5
|
|
1440
|
-
],
|
|
1441
|
-
"output": "Darkened gray meeting 4.5:1"
|
|
1442
|
-
},
|
|
1443
|
-
{
|
|
1444
|
-
"description": "Adjust brand color for accessibility",
|
|
1445
|
-
"input": [
|
|
1446
|
-
"#6750a4",
|
|
1447
|
-
"#ffffff",
|
|
1448
|
-
4.5
|
|
1449
|
-
],
|
|
1450
|
-
"output": "Adjusted purple meeting 4.5:1"
|
|
1451
|
-
}
|
|
1452
|
-
],
|
|
1453
|
-
"slug": "adjust_to_contrast"
|
|
1454
|
-
},
|
|
1455
|
-
{
|
|
1456
|
-
"name": "Alpha Blend",
|
|
1457
|
-
"type": "function",
|
|
1458
|
-
"description": "Performs Porter-Duff 'over' compositing to blend a foreground color over a background color using the foreground's alpha. Formula: result = fg × α + bg × (1-α). Standard alpha compositing used in CSS and graphics software.",
|
|
1459
|
-
"keyword": "alpha_blend",
|
|
1460
|
-
"input": {
|
|
1461
|
-
"type": "object",
|
|
1462
|
-
"properties": {
|
|
1463
|
-
"foreground": {
|
|
1464
|
-
"type": "color",
|
|
1465
|
-
"description": "Foreground (top) color"
|
|
1466
|
-
},
|
|
1467
|
-
"background": {
|
|
1468
|
-
"type": "color",
|
|
1469
|
-
"description": "Background (bottom) color"
|
|
1470
|
-
},
|
|
1471
|
-
"alpha": {
|
|
1472
|
-
"type": "number",
|
|
1473
|
-
"description": "Opacity of foreground (0-1, default: 0.5)"
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
},
|
|
1477
|
-
"script": {
|
|
1478
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1479
|
-
"script": "// Alpha Blend (Porter-Duff Over Compositing)\n// Reference: Porter & Duff \"Compositing Digital Images\" (1984)\n// Reference: W3C CSS Color Level 4 - Alpha Compositing\n//\n// Formula: result = foreground × α + background × (1 - α)\n// \n// This is the standard \"over\" operation:\n// - α = 0: result is entirely background\n// - α = 1: result is entirely foreground\n// - 0 < α < 1: blend of both colors\n//\n// Used in:\n// - CSS opacity and rgba\n// - Semi-transparent overlays\n// - Gradient stops\n// - Glass/frosted effects\n\nvariable input: List = {input};\nvariable fg: Color.SRGB = input.get(0).to.srgb();\nvariable bg: Color.SRGB = input.get(1).to.srgb();\n\n// Default alpha is 0.5 (50% opacity)\nvariable alpha: Number = 0.5;\nif (input.length() > 2) [\n alpha = input.get(2);\n];\n\n// Clamp alpha to valid range\nif (alpha < 0) [ alpha = 0; ];\nif (alpha > 1) [ alpha = 1; ];\n\n// Porter-Duff \"over\" compositing\nvariable inv_alpha: Number = 1 - alpha;\n\nvariable r: Number = fg.r * alpha + bg.r * inv_alpha;\nvariable g: Number = fg.g * alpha + bg.g * inv_alpha;\nvariable b: Number = fg.b * alpha + bg.b * inv_alpha;\n\n// Create result\nvariable result: Color.SRGB;\nresult.r = r;\nresult.g = g;\nresult.b = b;\n\nreturn result;"
|
|
1480
|
-
},
|
|
1481
|
-
"requirements": [
|
|
1482
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1483
|
-
],
|
|
1484
|
-
"slug": "alpha_blend"
|
|
1485
|
-
},
|
|
1486
|
-
{
|
|
1487
|
-
"name": "Alpha Scale",
|
|
1488
|
-
"type": "function",
|
|
1489
|
-
"description": "Generates transparency variants of a color. Returns colors with progressively lower opacity. Perfect for overlays, shadows, and layered UI elements.",
|
|
1490
|
-
"keyword": "alpha_scale",
|
|
1491
|
-
"input": {
|
|
1492
|
-
"type": "object",
|
|
1493
|
-
"properties": {
|
|
1494
|
-
"color": {
|
|
1495
|
-
"type": "color",
|
|
1496
|
-
"description": "Base color"
|
|
1497
|
-
},
|
|
1498
|
-
"count": {
|
|
1499
|
-
"type": "number",
|
|
1500
|
-
"description": "Number of alpha steps. Default is 10"
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
},
|
|
1504
|
-
"script": {
|
|
1505
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1506
|
-
"script": "// Generate alpha/transparency scale\n// Returns colors from 100% opacity down to nearly transparent\n//\n// Default 10 steps: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%\n// Useful for: overlays, shadows, backgrounds, hover states\n\nvariable input: List = {input};\nvariable base: Color.SRGB = input.get(0).to.srgb();\n\nvariable count: Number = 10;\nif (input.length() > 1) [\n count = input.get(1);\n];\n\nvariable result: List;\nvariable i: Number = 0;\nvariable step_alpha: Number = 0;\nvariable step_color: Color.SRGB;\n\nwhile (i < count) [\n // Alpha from 1.0 down to 0.1\n step_alpha = 1 - (i / count);\n \n // Create color with alpha\n // Note: This creates the color value; actual alpha handling\n // depends on how the consuming system processes the output\n step_color.r = base.r;\n step_color.g = base.g;\n step_color.b = base.b;\n \n // Store alpha as 4th value (RGBA convention)\n // Use .to.srgb() to create a new color instance for each step\n result = result, step_color.to.srgb(), step_alpha;\n i = i + 1;\n];\n\nreturn result;"
|
|
1507
|
-
},
|
|
1508
|
-
"requirements": [
|
|
1509
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1510
|
-
],
|
|
1511
|
-
"slug": "alpha_scale"
|
|
1512
|
-
},
|
|
1513
|
-
{
|
|
1514
|
-
"name": "Analogous",
|
|
1515
|
-
"type": "function",
|
|
1516
|
-
"description": "Generates analogous colors - hues adjacent on the color wheel. Creates harmonious, low-contrast palettes. Perfect for backgrounds and subtle variations.",
|
|
1517
|
-
"keyword": "analogous",
|
|
1518
|
-
"input": {
|
|
1519
|
-
"type": "object",
|
|
1520
|
-
"properties": {
|
|
1521
|
-
"color": {
|
|
1522
|
-
"type": "color",
|
|
1523
|
-
"description": "Base color"
|
|
1524
|
-
},
|
|
1525
|
-
"count": {
|
|
1526
|
-
"type": "number",
|
|
1527
|
-
"description": "Number of colors (odd recommended). Default is 5"
|
|
1528
|
-
},
|
|
1529
|
-
"spread": {
|
|
1530
|
-
"type": "number",
|
|
1531
|
-
"description": "Total angle spread in degrees. Default is 60"
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
},
|
|
1535
|
-
"script": {
|
|
1536
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1537
|
-
"script": "// Generate analogous colors (adjacent hues)\n// Colors are spread evenly around the base hue\n//\n// Use case: Harmonious, subtle palettes\n// Example: 5 colors with 60° spread = -30°, -15°, 0°, +15°, +30°\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\nvariable count: Number = 5;\nif (input.length() > 1) [\n count = input.get(1);\n];\n\nvariable spread: Number = 60;\nif (input.length() > 2) [\n spread = input.get(2);\n];\n\n// Calculate step size and starting offset\nvariable step: Number = spread / (count - 1);\nvariable start_offset: Number = 0 - spread / 2;\n\nvariable result: List;\nvariable i: Number = 0;\nvariable hue_offset: Number = 0;\nvariable new_hue: Number = 0;\nvariable color: Color.OKLCH;\n\nwhile (i < count) [\n hue_offset = start_offset + i * step;\n new_hue = base.h + hue_offset;\n \n // Normalize hue to 0-360\n if (new_hue < 0) [ new_hue = new_hue + 360; ];\n if (new_hue >= 360) [ new_hue = new_hue - 360; ];\n \n color.l = base.l;\n color.c = base.c;\n color.h = new_hue;\n \n result = result, color.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
1538
|
-
},
|
|
1539
|
-
"requirements": [
|
|
1540
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
1541
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1542
|
-
],
|
|
1543
|
-
"slug": "analogous"
|
|
1544
|
-
},
|
|
1545
|
-
{
|
|
1546
|
-
"name": "APCA Contrast",
|
|
1547
|
-
"type": "function",
|
|
1548
|
-
"description": "Calculates APCA (Accessible Perceptual Contrast Algorithm) contrast between text and background colors. APCA is part of WCAG 3.0 draft and provides perceptually uniform contrast values. Returns Lc value from -108 to +106. Positive = light text on dark, negative = dark text on light. |Lc| ≥ 60 recommended for body text.",
|
|
1549
|
-
"keyword": "apca_contrast",
|
|
1550
|
-
"input": {
|
|
1551
|
-
"type": "object",
|
|
1552
|
-
"properties": {
|
|
1553
|
-
"text": {
|
|
1554
|
-
"type": "color",
|
|
1555
|
-
"description": "Text/foreground color"
|
|
1556
|
-
},
|
|
1557
|
-
"background": {
|
|
1558
|
-
"type": "color",
|
|
1559
|
-
"description": "Background color"
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
},
|
|
1563
|
-
"script": {
|
|
1564
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1565
|
-
"script": "// APCA Contrast (Accessible Perceptual Contrast Algorithm)\n// Version: APCA 0.0.98G (W3C/WCAG 3.0 Draft)\n// Reference: https://github.com/Myndex/apca-w3\n// Reference: https://www.w3.org/TR/wcag-3.0/#visual-contrast-of-text\n//\n// Returns Lc (Lightness contrast) value:\n// - Range: approximately -108 to +106\n// - Positive values: light text on dark background\n// - Negative values: dark text on light background\n// - |Lc| >= 75: Preferred for body text\n// - |Lc| >= 60: Minimum for body text\n// - |Lc| >= 45: Minimum for large text (≥24px)\n// - |Lc| >= 30: Minimum for non-text elements\n//\n// Algorithm:\n// 1. Convert to sRGB and linearize with simple 2.4 gamma\n// 2. Calculate screen luminance Y with sRGB coefficients\n// 3. Apply soft black clamp for flare compensation\n// 4. Calculate contrast with asymmetric formula (BoW vs WoB)\n// 5. Apply low clip and offset\n\nvariable input: List = {input};\nvariable text: Color.SRGB = input.get(0).to.srgb();\nvariable bg: Color.SRGB = input.get(1).to.srgb();\n\n// APCA Constants (from specification)\n// Exponents\nvariable norm_bg: Number = 0.56;\nvariable norm_txt: Number = 0.57;\nvariable rev_txt: Number = 0.62;\nvariable rev_bg: Number = 0.65;\n\n// Soft black clamp constants\nvariable blk_thrs: Number = 0.022;\nvariable blk_clmp: Number = 1.414;\n\n// Low clip (noise gate)\nvariable lo_clip: Number = 0.1;\nvariable delta_y_min: Number = 0.0005;\n\n// Scalers and offset\nvariable scale_bow: Number = 1.14;\nvariable scale_wob: Number = 1.14;\nvariable lo_offset: Number = 0.027;\n\n// Linearize sRGB with simple 2.4 gamma (NOT full sRGB transfer function)\n// This is per APCA specification which uses simplified gamma\nvariable text_r: Number = text.r;\nvariable text_g: Number = text.g;\nvariable text_b: Number = text.b;\nvariable bg_r: Number = bg.r;\nvariable bg_g: Number = bg.g;\nvariable bg_b: Number = bg.b;\n\n// Handle negative values (out of gamut)\nif (text_r < 0) [ text_r = 0 - text_r; ];\nif (text_g < 0) [ text_g = 0 - text_g; ];\nif (text_b < 0) [ text_b = 0 - text_b; ];\nif (bg_r < 0) [ bg_r = 0 - bg_r; ];\nif (bg_g < 0) [ bg_g = 0 - bg_g; ];\nif (bg_b < 0) [ bg_b = 0 - bg_b; ];\n\n// Linearize with 2.4 gamma\nvariable lin_text_r: Number = pow(text_r, 2.4);\nvariable lin_text_g: Number = pow(text_g, 2.4);\nvariable lin_text_b: Number = pow(text_b, 2.4);\nvariable lin_bg_r: Number = pow(bg_r, 2.4);\nvariable lin_bg_g: Number = pow(bg_g, 2.4);\nvariable lin_bg_b: Number = pow(bg_b, 2.4);\n\n// Calculate screen luminance Y using sRGB coefficients\n// Coefficients from Myndex/APCA spec (via Lindbloom)\nvariable y_text: Number = 0.2126729 * lin_text_r + 0.7151522 * lin_text_g + 0.0721750 * lin_text_b;\nvariable y_bg: Number = 0.2126729 * lin_bg_r + 0.7151522 * lin_bg_g + 0.0721750 * lin_bg_b;\n\n// Soft clamp for flare (low luminance adjustment)\n// If Y >= threshold, use Y as-is\n// Otherwise: Y + (threshold - Y)^1.414\nvariable y_txt_clamped: Number = y_text;\nvariable y_bg_clamped: Number = y_bg;\n\nif (y_text < blk_thrs) [\n variable diff_txt: Number = blk_thrs - y_text;\n y_txt_clamped = y_text + pow(diff_txt, blk_clmp);\n];\n\nif (y_bg < blk_thrs) [\n variable diff_bg: Number = blk_thrs - y_bg;\n y_bg_clamped = y_bg + pow(diff_bg, blk_clmp);\n];\n\n// Determine polarity: BoW (dark on light) or WoB (light on dark)\nvariable sapc: Number = 0;\nvariable s_contrast: Number = 0;\n\n// Noise gate check\nvariable y_diff: Number = y_bg_clamped - y_txt_clamped;\nif (y_diff < 0) [ y_diff = 0 - y_diff; ];\n\n// Negative lo_clip for comparison (avoid parsing issues)\nvariable neg_lo_clip: Number = 0 - lo_clip;\n\nif (y_diff >= delta_y_min) [\n if (y_bg_clamped > y_txt_clamped) [\n // BoW: Dark text on light background (returns POSITIVE per APCA spec)\n s_contrast = pow(y_bg_clamped, norm_bg) - pow(y_txt_clamped, norm_txt);\n sapc = s_contrast * scale_bow;\n \n if (sapc < lo_clip) [\n sapc = 0;\n ] else [\n sapc = sapc - lo_offset;\n ];\n ] else [\n // WoB: Light text on dark background (returns NEGATIVE per APCA spec)\n s_contrast = pow(y_bg_clamped, rev_bg) - pow(y_txt_clamped, rev_txt);\n sapc = s_contrast * scale_wob;\n \n if (sapc > neg_lo_clip) [\n sapc = 0;\n ] else [\n sapc = sapc + lo_offset;\n ];\n ];\n];\n\n// Return Lc * 100 (standard APCA output format)\nreturn sapc * 100;"
|
|
1566
|
-
},
|
|
1567
|
-
"requirements": [
|
|
1568
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1569
|
-
],
|
|
1570
|
-
"slug": "apca_contrast"
|
|
1571
|
-
},
|
|
1572
|
-
{
|
|
1573
|
-
"name": "are_similar",
|
|
1574
|
-
"type": "function",
|
|
1575
|
-
"description": "Determines if two colors are perceptually similar using Delta E OK (Euclidean distance in OKLab space). Returns true if the colors are within the specified threshold. Default threshold of 0.02 corresponds to a 'just noticeable difference' (JND). Based on Björn Ottosson's OKLab color space which provides excellent perceptual uniformity.",
|
|
1576
|
-
"keyword": "are_similar",
|
|
1577
|
-
"requirements": [
|
|
1578
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1579
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/"
|
|
1580
|
-
],
|
|
1581
|
-
"schema": {
|
|
1582
|
-
"type": "object",
|
|
1583
|
-
"properties": {
|
|
1584
|
-
"input": {
|
|
1585
|
-
"type": "array",
|
|
1586
|
-
"items": [
|
|
1587
|
-
{
|
|
1588
|
-
"description": "First color",
|
|
1589
|
-
"type": "color"
|
|
1590
|
-
},
|
|
1591
|
-
{
|
|
1592
|
-
"description": "Second color",
|
|
1593
|
-
"type": "color"
|
|
1594
|
-
},
|
|
1595
|
-
{
|
|
1596
|
-
"description": "Delta E threshold (default 0.02 ≈ JND)",
|
|
1597
|
-
"type": "number"
|
|
1598
|
-
}
|
|
1599
|
-
],
|
|
1600
|
-
"minItems": 2,
|
|
1601
|
-
"maxItems": 3
|
|
1602
|
-
}
|
|
1603
|
-
},
|
|
1604
|
-
"required": [
|
|
1605
|
-
"input"
|
|
1606
|
-
]
|
|
1607
|
-
},
|
|
1608
|
-
"returns": {
|
|
1609
|
-
"type": "boolean",
|
|
1610
|
-
"description": "True if colors are perceptually similar (within threshold)"
|
|
1611
|
-
},
|
|
1612
|
-
"script": {
|
|
1613
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1614
|
-
"script": "// are_similar: Check if two colors are perceptually similar\n// Uses Delta E OK (OKLab Euclidean distance) for perceptual comparison\n// Default threshold 0.02 approximates \"just noticeable difference\"\n\nvariable input: List = {input};\nvariable color1: Color.OKLab = input.get(0).to.oklab();\nvariable color2: Color.OKLab = input.get(1).to.oklab();\n\n// Default threshold for JND (just noticeable difference)\nvariable threshold: Number = 0.02;\nif (input.length() > 2) [\n threshold = input.get(2);\n];\n\n// Calculate Delta E OK (Euclidean distance in OKLab)\nvariable delta_l: Number = color1.l - color2.l;\nvariable delta_a: Number = color1.a - color2.a;\nvariable delta_b: Number = color1.b - color2.b;\n\nvariable delta_e: Number = sqrt(delta_l * delta_l + delta_a * delta_a + delta_b * delta_b);\n\n// Return true if within threshold\nreturn delta_e <= threshold;"
|
|
1615
|
-
},
|
|
1616
|
-
"examples": [
|
|
1617
|
-
{
|
|
1618
|
-
"description": "Nearly identical colors",
|
|
1619
|
-
"input": [
|
|
1620
|
-
"#ff0000",
|
|
1621
|
-
"#ff0001"
|
|
1622
|
-
],
|
|
1623
|
-
"output": true
|
|
1624
|
-
},
|
|
1625
|
-
{
|
|
1626
|
-
"description": "Obviously different colors",
|
|
1627
|
-
"input": [
|
|
1628
|
-
"#ff0000",
|
|
1629
|
-
"#00ff00"
|
|
1630
|
-
],
|
|
1631
|
-
"output": false
|
|
1632
|
-
},
|
|
1633
|
-
{
|
|
1634
|
-
"description": "Custom threshold for stricter comparison",
|
|
1635
|
-
"input": [
|
|
1636
|
-
"#808080",
|
|
1637
|
-
"#818181",
|
|
1638
|
-
0.01
|
|
1639
|
-
],
|
|
1640
|
-
"output": true
|
|
1641
|
-
}
|
|
1642
|
-
],
|
|
1643
|
-
"slug": "are_similar"
|
|
1644
|
-
},
|
|
1645
|
-
{
|
|
1646
|
-
"name": "auto_text_color",
|
|
1647
|
-
"type": "function",
|
|
1648
|
-
"description": "Returns black or white text color for optimal contrast against a background. Uses WCAG relative luminance to determine if the background is light or dark, then returns the opposite for maximum readability. Essential for generating accessible text colors in design systems.",
|
|
1649
|
-
"keyword": "auto_text_color",
|
|
1650
|
-
"requirements": [
|
|
1651
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1652
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/"
|
|
1653
|
-
],
|
|
1654
|
-
"schema": {
|
|
1655
|
-
"type": "object",
|
|
1656
|
-
"properties": {
|
|
1657
|
-
"input": {
|
|
1658
|
-
"type": "array",
|
|
1659
|
-
"items": [
|
|
1660
|
-
{
|
|
1661
|
-
"description": "Background color to test",
|
|
1662
|
-
"type": "color"
|
|
1663
|
-
},
|
|
1664
|
-
{
|
|
1665
|
-
"description": "Optional threshold (0-1, default 0.179 per WCAG). Higher values bias toward white text.",
|
|
1666
|
-
"type": "number"
|
|
1667
|
-
}
|
|
1668
|
-
],
|
|
1669
|
-
"minItems": 1,
|
|
1670
|
-
"maxItems": 2
|
|
1671
|
-
}
|
|
1672
|
-
},
|
|
1673
|
-
"required": [
|
|
1674
|
-
"input"
|
|
1675
|
-
]
|
|
1676
|
-
},
|
|
1677
|
-
"returns": {
|
|
1678
|
-
"type": "color",
|
|
1679
|
-
"description": "Either black (#000000) or white (#ffffff) for optimal contrast"
|
|
1680
|
-
},
|
|
1681
|
-
"script": {
|
|
1682
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1683
|
-
"script": "// auto_text_color: Select black or white text for optimal contrast\n// Reference: WCAG 2.1 Relative Luminance\n// Reference: https://www.w3.org/TR/WCAG21/#dfn-relative-luminance\n//\n// Returns black (#000) or white (#fff) depending on which provides\n// better contrast against the input background color.\n//\n// Default threshold: 0.179 (geometric mean of WCAG luminance range)\n// - sqrt(1.05 * 0.05) - 0.05 ≈ 0.179\n// - Above threshold: light background → black text\n// - Below threshold: dark background → white text\n\nvariable input: List = {input};\n\n// Default threshold based on WCAG luminance midpoint\nvariable threshold: Number = 0.179;\nif (input.length() > 1) [\n threshold = input.get(1);\n];\n\n// Convert to XYZ-D65 to get relative luminance (Y component)\nvariable xyz: Color.XYZD65 = input.get(0).to.xyzd65();\nvariable luminance: Number = xyz.y;\n\n// If luminance is above threshold, background is \"light\" -> use black text\n// If luminance is below threshold, background is \"dark\" -> use white text\nvariable result: Color.SRGB;\n\nif (luminance > threshold) [\n // Light background: return black\n result.r = 0;\n result.g = 0;\n result.b = 0;\n] else [\n // Dark background: return white\n result.r = 1;\n result.g = 1;\n result.b = 1;\n];\n\nreturn result;"
|
|
1684
|
-
},
|
|
1685
|
-
"examples": [
|
|
1686
|
-
{
|
|
1687
|
-
"description": "Dark background returns white text",
|
|
1688
|
-
"input": [
|
|
1689
|
-
"#1a1a1a"
|
|
1690
|
-
],
|
|
1691
|
-
"output": "#ffffff"
|
|
1692
|
-
},
|
|
1693
|
-
{
|
|
1694
|
-
"description": "Light background returns black text",
|
|
1695
|
-
"input": [
|
|
1696
|
-
"#f0f0f0"
|
|
1697
|
-
],
|
|
1698
|
-
"output": "#000000"
|
|
1699
|
-
},
|
|
1700
|
-
{
|
|
1701
|
-
"description": "Mid-tone with custom threshold",
|
|
1702
|
-
"input": [
|
|
1703
|
-
"#808080",
|
|
1704
|
-
0.5
|
|
1705
|
-
],
|
|
1706
|
-
"output": "#ffffff"
|
|
1707
|
-
}
|
|
1708
|
-
],
|
|
1709
|
-
"slug": "auto_text_color"
|
|
1710
|
-
},
|
|
1711
|
-
{
|
|
1712
|
-
"name": "Best Contrast",
|
|
1713
|
-
"type": "function",
|
|
1714
|
-
"description": "Selects the color with highest contrast against a background from a list of candidates. Uses WCAG 2.1 contrast ratio. Perfect for choosing readable text colors.",
|
|
1715
|
-
"keyword": "best_contrast",
|
|
1716
|
-
"input": {
|
|
1717
|
-
"type": "object",
|
|
1718
|
-
"properties": {
|
|
1719
|
-
"background": {
|
|
1720
|
-
"type": "color",
|
|
1721
|
-
"description": "The background color to contrast against"
|
|
1722
|
-
},
|
|
1723
|
-
"candidates": {
|
|
1724
|
-
"type": "list",
|
|
1725
|
-
"description": "List of candidate colors to choose from. Default is [black, white]"
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
},
|
|
1729
|
-
"script": {
|
|
1730
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1731
|
-
"script": "// Select the most contrasting color from a list of candidates\n// Uses WCAG 2.1 contrast ratio based on relative luminance\n//\n// Algorithm:\n// 1. Calculate relative luminance: L = 0.2126*R + 0.7152*G + 0.0722*B (linear RGB)\n// 2. Contrast ratio: (L_lighter + 0.05) / (L_darker + 0.05)\n// 3. Return candidate with highest contrast ratio\n//\n// Default candidates: black (#000) and white (#fff)\n\nvariable input: List = {input};\nvariable bg: Color.LinearSRGB = input.get(0).to.linearsrgb();\n\n// Calculate background luminance\nvariable bg_lum: Number = 0.2126 * bg.r + 0.7152 * bg.g + 0.0722 * bg.b;\n\n// Get candidates list, default to black and white\nvariable candidates: List;\nif (input.length() > 1) [\n candidates = input.get(1);\n] else [\n // Default: black and white\n variable black: Color.SRGB;\n black.r = 0; black.g = 0; black.b = 0;\n variable white: Color.SRGB;\n white.r = 1; white.g = 1; white.b = 1;\n candidates = black, white;\n];\n\n// Find candidate with highest contrast\nvariable best_color: Color.SRGB = candidates.get(0).to.srgb();\nvariable best_contrast: Number = 0;\nvariable candidate: Color.LinearSRGB;\nvariable cand_lum: Number = 0;\nvariable lighter: Number = 0;\nvariable darker: Number = 0;\nvariable contrast: Number = 0;\n\nvariable i: Number = 0;\nwhile (i < candidates.length()) [\n candidate = candidates.get(i).to.linearsrgb();\n \n // Calculate candidate luminance\n cand_lum = 0.2126 * candidate.r + 0.7152 * candidate.g + 0.0722 * candidate.b;\n \n // Calculate contrast ratio (lighter / darker)\n lighter = bg_lum;\n darker = cand_lum;\n if (cand_lum > bg_lum) [\n lighter = cand_lum;\n darker = bg_lum;\n ];\n \n contrast = (lighter + 0.05) / (darker + 0.05);\n \n // Track best\n if (contrast > best_contrast) [\n best_contrast = contrast;\n best_color = candidates.get(i).to.srgb();\n ];\n \n i = i + 1;\n];\n\nreturn best_color;"
|
|
1732
|
-
},
|
|
1733
|
-
"requirements": [
|
|
1734
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/",
|
|
1735
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1736
|
-
],
|
|
1737
|
-
"slug": "best_contrast"
|
|
1738
|
-
},
|
|
1739
|
-
{
|
|
1740
|
-
"name": "chroma",
|
|
1741
|
-
"type": "function",
|
|
1742
|
-
"description": "Extracts the chroma (colorfulness/saturation) value from any color using OKLCH. Returns a value from 0 (gray) to approximately 0.4 (most saturated colors). Chroma in OKLCH represents the colorfulness independent of lightness - higher values mean more vivid colors. Essential for creating consistent color scales and checking color vibrancy.",
|
|
1743
|
-
"keyword": "chroma",
|
|
1744
|
-
"requirements": [
|
|
1745
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1746
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
1747
|
-
],
|
|
1748
|
-
"schema": {
|
|
1749
|
-
"type": "object",
|
|
1750
|
-
"properties": {
|
|
1751
|
-
"input": {
|
|
1752
|
-
"type": "array",
|
|
1753
|
-
"items": [
|
|
1754
|
-
{
|
|
1755
|
-
"description": "Color to extract chroma from",
|
|
1756
|
-
"type": "color"
|
|
1757
|
-
}
|
|
1758
|
-
],
|
|
1759
|
-
"minItems": 1,
|
|
1760
|
-
"maxItems": 1
|
|
1761
|
-
}
|
|
1762
|
-
},
|
|
1763
|
-
"required": [
|
|
1764
|
-
"input"
|
|
1765
|
-
]
|
|
1766
|
-
},
|
|
1767
|
-
"returns": {
|
|
1768
|
-
"type": "number",
|
|
1769
|
-
"description": "Chroma value (0 to ~0.4)"
|
|
1770
|
-
},
|
|
1771
|
-
"script": {
|
|
1772
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1773
|
-
"script": "// chroma: Extract chroma (colorfulness) from a color\n// Reference: OKLCH Color Space (Björn Ottosson)\n// Reference: https://bottosson.github.io/posts/oklab/\n//\n// Returns the C component from OKLCH, which represents\n// colorfulness/saturation. Range: 0 (gray) to ~0.4 (most vivid).\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\nreturn color.c;"
|
|
1774
|
-
},
|
|
1775
|
-
"examples": [
|
|
1776
|
-
{
|
|
1777
|
-
"description": "Gray has chroma 0",
|
|
1778
|
-
"input": [
|
|
1779
|
-
"#808080"
|
|
1780
|
-
],
|
|
1781
|
-
"output": 0
|
|
1782
|
-
},
|
|
1783
|
-
{
|
|
1784
|
-
"description": "Saturated red has high chroma",
|
|
1785
|
-
"input": [
|
|
1786
|
-
"#ff0000"
|
|
1787
|
-
],
|
|
1788
|
-
"output": 0.26
|
|
1789
|
-
}
|
|
1790
|
-
],
|
|
1791
|
-
"slug": "chroma"
|
|
1792
|
-
},
|
|
1793
|
-
{
|
|
1794
|
-
"name": "clamp_chroma",
|
|
1795
|
-
"type": "function",
|
|
1796
|
-
"description": "Constrains a color's chroma (saturation) to a specified range while preserving hue and lightness. Useful for ensuring colors stay within brand guidelines or design constraints. Uses OKLCH for perceptually uniform clamping.",
|
|
1797
|
-
"keyword": "clamp_chroma",
|
|
1798
|
-
"requirements": [
|
|
1799
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1800
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
1801
|
-
],
|
|
1802
|
-
"schema": {
|
|
1803
|
-
"type": "object",
|
|
1804
|
-
"properties": {
|
|
1805
|
-
"input": {
|
|
1806
|
-
"type": "array",
|
|
1807
|
-
"items": [
|
|
1808
|
-
{
|
|
1809
|
-
"description": "Color to clamp",
|
|
1810
|
-
"type": "color"
|
|
1811
|
-
},
|
|
1812
|
-
{
|
|
1813
|
-
"description": "Minimum chroma (0+)",
|
|
1814
|
-
"type": "number"
|
|
1815
|
-
},
|
|
1816
|
-
{
|
|
1817
|
-
"description": "Maximum chroma (typically 0-0.4)",
|
|
1818
|
-
"type": "number"
|
|
1819
|
-
}
|
|
1820
|
-
],
|
|
1821
|
-
"minItems": 3,
|
|
1822
|
-
"maxItems": 3
|
|
1823
|
-
}
|
|
1824
|
-
},
|
|
1825
|
-
"required": [
|
|
1826
|
-
"input"
|
|
1827
|
-
]
|
|
1828
|
-
},
|
|
1829
|
-
"returns": {
|
|
1830
|
-
"type": "color",
|
|
1831
|
-
"description": "Color with clamped chroma"
|
|
1832
|
-
},
|
|
1833
|
-
"script": {
|
|
1834
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1835
|
-
"script": "// clamp_chroma: Constrain chroma to a range\n// Preserves hue and lightness\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable min_c: Number = input.get(1);\nvariable max_c: Number = input.get(2);\n\n// Clamp chroma\nvariable new_c: Number = color.c;\nif (new_c < min_c) [ new_c = min_c; ];\nif (new_c > max_c) [ new_c = max_c; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
1836
|
-
},
|
|
1837
|
-
"examples": [
|
|
1838
|
-
{
|
|
1839
|
-
"description": "Limit maximum saturation",
|
|
1840
|
-
"input": [
|
|
1841
|
-
"#ff0000",
|
|
1842
|
-
0,
|
|
1843
|
-
0.15
|
|
1844
|
-
],
|
|
1845
|
-
"output": "Muted red with chroma 0.15"
|
|
1846
|
-
},
|
|
1847
|
-
{
|
|
1848
|
-
"description": "Ensure minimum saturation",
|
|
1849
|
-
"input": [
|
|
1850
|
-
"#808080",
|
|
1851
|
-
0.05,
|
|
1852
|
-
0.3
|
|
1853
|
-
],
|
|
1854
|
-
"output": "Slightly colored gray"
|
|
1855
|
-
}
|
|
1856
|
-
],
|
|
1857
|
-
"slug": "clamp_chroma"
|
|
1858
|
-
},
|
|
1859
|
-
{
|
|
1860
|
-
"name": "clamp_lightness",
|
|
1861
|
-
"type": "function",
|
|
1862
|
-
"description": "Constrains a color's lightness to a specified range while preserving hue and chroma. Useful for ensuring colors stay within readable/accessible bounds. Uses OKLCH for perceptually uniform clamping.",
|
|
1863
|
-
"keyword": "clamp_lightness",
|
|
1864
|
-
"requirements": [
|
|
1865
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
1866
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
1867
|
-
],
|
|
1868
|
-
"schema": {
|
|
1869
|
-
"type": "object",
|
|
1870
|
-
"properties": {
|
|
1871
|
-
"input": {
|
|
1872
|
-
"type": "array",
|
|
1873
|
-
"items": [
|
|
1874
|
-
{
|
|
1875
|
-
"description": "Color to clamp",
|
|
1876
|
-
"type": "color"
|
|
1877
|
-
},
|
|
1878
|
-
{
|
|
1879
|
-
"description": "Minimum lightness (0-1)",
|
|
1880
|
-
"type": "number"
|
|
1881
|
-
},
|
|
1882
|
-
{
|
|
1883
|
-
"description": "Maximum lightness (0-1)",
|
|
1884
|
-
"type": "number"
|
|
1885
|
-
}
|
|
1886
|
-
],
|
|
1887
|
-
"minItems": 3,
|
|
1888
|
-
"maxItems": 3
|
|
1889
|
-
}
|
|
1890
|
-
},
|
|
1891
|
-
"required": [
|
|
1892
|
-
"input"
|
|
1893
|
-
]
|
|
1894
|
-
},
|
|
1895
|
-
"returns": {
|
|
1896
|
-
"type": "color",
|
|
1897
|
-
"description": "Color with clamped lightness"
|
|
1898
|
-
},
|
|
1899
|
-
"script": {
|
|
1900
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1901
|
-
"script": "// clamp_lightness: Constrain lightness to a range\n// Preserves hue and chroma\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable min_l: Number = input.get(1);\nvariable max_l: Number = input.get(2);\n\n// Clamp lightness\nvariable new_l: Number = color.l;\nif (new_l < min_l) [ new_l = min_l; ];\nif (new_l > max_l) [ new_l = max_l; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = new_l;\nresult.c = color.c;\nresult.h = color.h;\n\nreturn result;"
|
|
1902
|
-
},
|
|
1903
|
-
"examples": [
|
|
1904
|
-
{
|
|
1905
|
-
"description": "Clamp too-dark color to minimum",
|
|
1906
|
-
"input": [
|
|
1907
|
-
"#111111",
|
|
1908
|
-
0.3,
|
|
1909
|
-
0.9
|
|
1910
|
-
],
|
|
1911
|
-
"output": "Color with lightness 0.3"
|
|
1912
|
-
},
|
|
1913
|
-
{
|
|
1914
|
-
"description": "Clamp too-light color to maximum",
|
|
1915
|
-
"input": [
|
|
1916
|
-
"#ffffff",
|
|
1917
|
-
0.2,
|
|
1918
|
-
0.8
|
|
1919
|
-
],
|
|
1920
|
-
"output": "Color with lightness 0.8"
|
|
1921
|
-
}
|
|
1922
|
-
],
|
|
1923
|
-
"slug": "clamp_lightness"
|
|
1924
|
-
},
|
|
1925
|
-
{
|
|
1926
|
-
"name": "Clamp to Gamut",
|
|
1927
|
-
"type": "function",
|
|
1928
|
-
"description": "Clips a color to the sRGB gamut by clamping each RGB channel to [0,1]. This is a simple but lossy approach - for perceptually better results, consider using gamut mapping algorithms that reduce chroma first.",
|
|
1929
|
-
"keyword": "clamp_to_gamut",
|
|
1930
|
-
"input": {
|
|
1931
|
-
"type": "object",
|
|
1932
|
-
"properties": {
|
|
1933
|
-
"color": {
|
|
1934
|
-
"type": "color",
|
|
1935
|
-
"description": "Color to clamp"
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
},
|
|
1939
|
-
"script": {
|
|
1940
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1941
|
-
"script": "// Clamp to Gamut (Simple Clipping)\n// Clips out-of-gamut sRGB values to [0, 1] range\n// Reference: Color.js toGamut with method=\"clip\"\n//\n// This is the simplest gamut mapping approach:\n// - Fast and deterministic\n// - Preserves hue when only one channel clips\n// - Can cause hue shifts when multiple channels clip\n// - May lose saturation detail\n//\n// For perceptually better results, use toGamut() which\n// reduces chroma in OKLCH before clipping.\n\nvariable input: List = {input};\nvariable color: Color.SRGB = input.get(0).to.srgb();\n\n// Get RGB values\nvariable r: Number = color.r;\nvariable g: Number = color.g;\nvariable b: Number = color.b;\n\n// Clamp R\nif (r < 0) [ r = 0; ];\nif (r > 1) [ r = 1; ];\n\n// Clamp G\nif (g < 0) [ g = 0; ];\nif (g > 1) [ g = 1; ];\n\n// Clamp B\nif (b < 0) [ b = 0; ];\nif (b > 1) [ b = 1; ];\n\n// Create clamped result\nvariable result: Color.SRGB;\nresult.r = r;\nresult.g = g;\nresult.b = b;\n\nreturn result;"
|
|
1942
|
-
},
|
|
1943
|
-
"requirements": [
|
|
1944
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1945
|
-
],
|
|
1946
|
-
"slug": "clamp_to_gamut"
|
|
1947
|
-
},
|
|
1948
|
-
{
|
|
1949
|
-
"name": "Complement",
|
|
1950
|
-
"type": "function",
|
|
1951
|
-
"description": "Returns the complementary color by rotating hue 180° in OKLCH space.",
|
|
1952
|
-
"keyword": "complement",
|
|
1953
|
-
"input": {
|
|
1954
|
-
"type": "object",
|
|
1955
|
-
"properties": {
|
|
1956
|
-
"color": {
|
|
1957
|
-
"type": "color",
|
|
1958
|
-
"description": "The color to find the complement of"
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
},
|
|
1962
|
-
"script": {
|
|
1963
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1964
|
-
"script": "// Get the complementary color by rotating hue 180° in OKLCH\n// Preserves lightness and chroma for perceptually balanced results\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Rotate hue by 180°\nvariable new_h: Number = color.h + 180;\nif (new_h >= 360) [ new_h = new_h - 360; ];\n\n// Create complementary color\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = new_h;\n\nreturn result;"
|
|
1965
|
-
},
|
|
1966
|
-
"requirements": [
|
|
1967
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
1968
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
1969
|
-
],
|
|
1970
|
-
"slug": "complement"
|
|
1971
|
-
},
|
|
1972
|
-
{
|
|
1973
|
-
"name": "Contrast Ratio",
|
|
1974
|
-
"type": "function",
|
|
1975
|
-
"description": "Calculates the WCAG 2.1 contrast ratio between two colors. Returns a value from 1 (no contrast) to 21 (black/white). WCAG AA requires 4.5:1 for normal text, 3:1 for large text.",
|
|
1976
|
-
"keyword": "contrast_ratio",
|
|
1977
|
-
"input": {
|
|
1978
|
-
"type": "object",
|
|
1979
|
-
"properties": {
|
|
1980
|
-
"color1": {
|
|
1981
|
-
"type": "color",
|
|
1982
|
-
"description": "First color (e.g., background)"
|
|
1983
|
-
},
|
|
1984
|
-
"color2": {
|
|
1985
|
-
"type": "color",
|
|
1986
|
-
"description": "Second color (e.g., foreground)"
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
},
|
|
1990
|
-
"script": {
|
|
1991
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
1992
|
-
"script": "// Calculate WCAG 2.1 contrast ratio between two colors\n//\n// Returns: 1.0 (identical) to 21.0 (black vs white)\n//\n// WCAG Guidelines:\n// - AA Normal text: ≥ 4.5:1\n// - AA Large text: ≥ 3.0:1\n// - AAA Normal text: ≥ 7.0:1\n// - AAA Large text: ≥ 4.5:1\n//\n// Algorithm:\n// 1. Convert to linear RGB\n// 2. Calculate relative luminance: L = 0.2126*R + 0.7152*G + 0.0722*B\n// 3. Contrast ratio: (L_lighter + 0.05) / (L_darker + 0.05)\n\nvariable input: List = {input};\nvariable color1: Color.LinearSRGB = input.get(0).to.linearsrgb();\nvariable color2: Color.LinearSRGB = input.get(1).to.linearsrgb();\n\n// Calculate relative luminance for each color\nvariable lum1: Number = 0.2126 * color1.r + 0.7152 * color1.g + 0.0722 * color1.b;\nvariable lum2: Number = 0.2126 * color2.r + 0.7152 * color2.g + 0.0722 * color2.b;\n\n// Determine lighter and darker\nvariable lighter: Number = lum1;\nvariable darker: Number = lum2;\nif (lum2 > lum1) [\n lighter = lum2;\n darker = lum1;\n];\n\n// Calculate contrast ratio\nvariable ratio: Number = (lighter + 0.05) / (darker + 0.05);\n\nreturn ratio;"
|
|
1993
|
-
},
|
|
1994
|
-
"requirements": [
|
|
1995
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/"
|
|
1996
|
-
],
|
|
1997
|
-
"slug": "contrast_ratio"
|
|
1998
|
-
},
|
|
1999
|
-
{
|
|
2000
|
-
"name": "cooler",
|
|
2001
|
-
"type": "function",
|
|
2002
|
-
"description": "Shifts a color's hue towards cool colors (blue, ~260° in OKLCH). The amount parameter controls how much to shift, from 0 (no change) to 1 (fully cool). Preserves lightness and chroma.",
|
|
2003
|
-
"keyword": "cooler",
|
|
2004
|
-
"requirements": [
|
|
2005
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2006
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2007
|
-
],
|
|
2008
|
-
"schema": {
|
|
2009
|
-
"type": "object",
|
|
2010
|
-
"properties": {
|
|
2011
|
-
"input": {
|
|
2012
|
-
"type": "array",
|
|
2013
|
-
"items": [
|
|
2014
|
-
{
|
|
2015
|
-
"description": "Color to cool",
|
|
2016
|
-
"type": "color"
|
|
2017
|
-
},
|
|
2018
|
-
{
|
|
2019
|
-
"description": "Amount to shift (0-1), default 0.25",
|
|
2020
|
-
"type": "number"
|
|
2021
|
-
}
|
|
2022
|
-
],
|
|
2023
|
-
"minItems": 1,
|
|
2024
|
-
"maxItems": 2
|
|
2025
|
-
}
|
|
2026
|
-
},
|
|
2027
|
-
"required": [
|
|
2028
|
-
"input"
|
|
2029
|
-
]
|
|
2030
|
-
},
|
|
2031
|
-
"returns": {
|
|
2032
|
-
"type": "color",
|
|
2033
|
-
"description": "Color shifted towards cool hues"
|
|
2034
|
-
},
|
|
2035
|
-
"script": {
|
|
2036
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2037
|
-
"script": "// cooler: Shift hue towards cool colors\n//\n// Cool colors are centered around blue (~260° in OKLCH).\n// The function interpolates the hue towards this target via\n// the shortest angular path on the hue wheel.\n//\n// Parameters:\n// color - Input color\n// amount - Shift amount (0 = no change, 1 = fully cool), default 0.25\n//\n// Preserves lightness and chroma.\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable amount: Number = 0.25;\n\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Cool hue target (blue in OKLCH is around 260°)\nvariable cool_hue: Number = 260;\n\n// Calculate shortest path hue difference\nvariable hue_diff: Number = cool_hue - color.h;\n\n// Wrap to shortest path\nif (hue_diff > 180) [ hue_diff = hue_diff - 360; ];\nif (hue_diff < -180) [ hue_diff = hue_diff + 360; ];\n\n// Apply interpolation\nvariable new_hue: Number = color.h + (hue_diff * amount);\n\n// Normalize to 0-360\nif (new_hue < 0) [ new_hue = new_hue + 360; ];\nif (new_hue >= 360) [ new_hue = new_hue - 360; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = new_hue;\n\nreturn result;"
|
|
2038
|
-
},
|
|
2039
|
-
"examples": [
|
|
2040
|
-
{
|
|
2041
|
-
"description": "Make orange cooler",
|
|
2042
|
-
"input": [
|
|
2043
|
-
"#ff6600",
|
|
2044
|
-
0.5
|
|
2045
|
-
],
|
|
2046
|
-
"output": "Orange shifted towards neutral"
|
|
2047
|
-
}
|
|
2048
|
-
],
|
|
2049
|
-
"slug": "cooler"
|
|
2050
|
-
},
|
|
2051
|
-
{
|
|
2052
|
-
"name": "Darken",
|
|
2053
|
-
"type": "function",
|
|
2054
|
-
"description": "Makes a color darker by decreasing its lightness in OKLab space. Amount is 0-1 where 0.25 = 25% darker.",
|
|
2055
|
-
"keyword": "darken",
|
|
2056
|
-
"input": {
|
|
2057
|
-
"type": "object",
|
|
2058
|
-
"properties": {
|
|
2059
|
-
"color": {
|
|
2060
|
-
"type": "color",
|
|
2061
|
-
"description": "The color to darken"
|
|
2062
|
-
},
|
|
2063
|
-
"amount": {
|
|
2064
|
-
"type": "number",
|
|
2065
|
-
"description": "Amount to darken (0-1). Default is 0.25"
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
},
|
|
2069
|
-
"script": {
|
|
2070
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2071
|
-
"script": "// darken: Decrease lightness proportionally towards black\n// Amount: 0-1 where 0.25 means 25% closer to black\n//\n// Algorithm: L' = L * (1 - amount)\n// This ensures we approach black (L=0) proportionally.\n//\n// Input: Any color space (converted to OKLab internally)\n// Output: OKLCH (working space)\n// To get sRGB: darken(color, 0.25).to.srgb()\n\nvariable input: List = {input};\nvariable color: Color.OKLab = input.get(0).to.oklab();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new lightness (move toward 0)\nvariable current_l: Number = color.l;\nvariable new_l: Number = current_l * (1 - amount);\n\n// Clamp to valid range\nif (new_l < 0) [ new_l = 0; ];\n\n// Create output in OKLab, return as OKLCH (working space)\nvariable result: Color.OKLab;\nresult.l = new_l;\nresult.a = color.a;\nresult.b = color.b;\n\nreturn result.to.oklch();"
|
|
2072
|
-
},
|
|
2073
|
-
"requirements": [
|
|
2074
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
2075
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2076
|
-
],
|
|
2077
|
-
"slug": "darken"
|
|
2078
|
-
},
|
|
2079
|
-
{
|
|
2080
|
-
"name": "Delta E 2000",
|
|
2081
|
-
"type": "function",
|
|
2082
|
-
"description": "Calculates perceptual color difference using CIEDE2000 (ΔE00), the CIE-recommended color difference formula. More accurate than CIE76 for small differences. Industry standard for color quality control. Uses parametric weighting factors kL, kC, kH (all default to 1).",
|
|
2083
|
-
"keyword": "delta_e_2000",
|
|
2084
|
-
"input": {
|
|
2085
|
-
"type": "object",
|
|
2086
|
-
"properties": {
|
|
2087
|
-
"color1": {
|
|
2088
|
-
"type": "color",
|
|
2089
|
-
"description": "First color (reference)"
|
|
2090
|
-
},
|
|
2091
|
-
"color2": {
|
|
2092
|
-
"type": "color",
|
|
2093
|
-
"description": "Second color (sample)"
|
|
2094
|
-
},
|
|
2095
|
-
"kL": {
|
|
2096
|
-
"type": "number",
|
|
2097
|
-
"description": "Lightness weight (default 1)"
|
|
2098
|
-
},
|
|
2099
|
-
"kC": {
|
|
2100
|
-
"type": "number",
|
|
2101
|
-
"description": "Chroma weight (default 1)"
|
|
2102
|
-
},
|
|
2103
|
-
"kH": {
|
|
2104
|
-
"type": "number",
|
|
2105
|
-
"description": "Hue weight (default 1)"
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
},
|
|
2109
|
-
"script": {
|
|
2110
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2111
|
-
"script": "// CIEDE2000 Color Difference Formula (ΔE00)\n// Reference: CIE 142-2001 (Technical Report)\n// Reference: Color.js deltaE2000 implementation\n// Reference: Sharma, Wu, Dalal \"The CIEDE2000 Color-Difference Formula\" (2005)\n//\n// This is the CIE-recommended color difference formula, providing\n// better correlation with visual perception than CIE76 or CIE94.\n//\n// Parametric factors (kL, kC, kH):\n// - All default to 1.0 for reference conditions\n// - kL can be increased for lightness texture/noise\n// - kC can be increased for chroma noise\n//\n// Interpretation (approximate):\n// 0.0 - Identical colors\n// < 1.0 - Not perceptible by human eye\n// 1.0 - 2.0 - Perceptible through close observation\n// 2.0 - 10.0 - Perceptible at a glance\n// > 10.0 - Colors are obviously different\n\nvariable input: List = {input};\n\n// Convert both colors to CIE Lab\nvariable color1: Color.Lab = input.get(0).to.lab();\nvariable color2: Color.Lab = input.get(1).to.lab();\n\n// Get parametric weighting factors (default to 1)\nvariable k_l: Number = 1;\nvariable k_c: Number = 1;\nvariable k_h: Number = 1;\nif (input.length() > 2) [ k_l = input.get(2); ];\nif (input.length() > 3) [ k_c = input.get(3); ];\nif (input.length() > 4) [ k_h = input.get(4); ];\n\n// Constants\nvariable pi: Number = pi();\nvariable r2d: Number = 180 / pi;\nvariable d2r: Number = pi / 180;\nvariable g_factor: Number = 6103515625; // 25^7\nvariable e: Number = 2.718281828459045; // Euler's number\n\n// Get Lab values\nvariable l1: Number = color1.l;\nvariable a1: Number = color1.a;\nvariable b1: Number = color1.b;\n\nvariable l2: Number = color2.l;\nvariable a2: Number = color2.a;\nvariable b2: Number = color2.b;\n\n// Calculate C* (chroma) for both colors\nvariable c1: Number = sqrt(a1 * a1 + b1 * b1);\nvariable c2: Number = sqrt(a2 * a2 + b2 * b2);\n\n// Ensure non-negative chroma\nif (c1 < 0) [ c1 = 0; ];\nif (c2 < 0) [ c2 = 0; ];\n\n// Mean chroma\nvariable c_bar: Number = (c1 + c2) / 2;\n\n// Calculate G (a-axis asymmetry factor)\nvariable c7: Number = c_bar * c_bar * c_bar * c_bar * c_bar * c_bar * c_bar;\nvariable g: Number = 0.5 * (1 - sqrt(c7 / (c7 + g_factor)));\n\n// Scale a* values by asymmetry factor\nvariable a_prime1: Number = (1 + g) * a1;\nvariable a_prime2: Number = (1 + g) * a2;\n\n// Calculate C' from scaled a' and original b\nvariable c_prime1: Number = sqrt(a_prime1 * a_prime1 + b1 * b1);\nvariable c_prime2: Number = sqrt(a_prime2 * a_prime2 + b2 * b2);\n\n// Calculate h' (hue angles in degrees)\nvariable h1: Number = 0;\nif (a_prime1 != 0) [ h1 = atan2(b1, a_prime1); ];\nif (b1 != 0) [ h1 = atan2(b1, a_prime1); ];\nif (a_prime1 == 0) [\n if (b1 == 0) [ h1 = 0; ] else [ h1 = atan2(b1, a_prime1); ];\n];\nh1 = h1 * r2d;\nif (h1 < 0) [ h1 = h1 + 360; ];\n\nvariable h2: Number = 0;\nif (a_prime2 != 0) [ h2 = atan2(b2, a_prime2); ];\nif (b2 != 0) [ h2 = atan2(b2, a_prime2); ];\nif (a_prime2 == 0) [\n if (b2 == 0) [ h2 = 0; ] else [ h2 = atan2(b2, a_prime2); ];\n];\nh2 = h2 * r2d;\nif (h2 < 0) [ h2 = h2 + 360; ];\n\n// Lightness and Chroma differences\nvariable delta_l: Number = l2 - l1;\nvariable delta_c: Number = c_prime2 - c_prime1;\n\n// Hue difference (getting the sign correct)\nvariable h_diff: Number = h2 - h1;\nvariable h_sum: Number = h1 + h2;\nvariable h_abs: Number = h_diff;\nif (h_abs < 0) [ h_abs = 0 - h_abs; ];\n\nvariable delta_h: Number = 0;\nvariable c_product: Number = c_prime1 * c_prime2;\n\nif (c_product == 0) [\n delta_h = 0;\n] else [\n if (h_abs <= 180) [\n delta_h = h_diff;\n ] else [\n if (h_diff > 180) [\n delta_h = h_diff - 360;\n ] else [\n if (h_diff < -180) [\n delta_h = h_diff + 360;\n ] else [\n delta_h = h_diff;\n ];\n ];\n ];\n];\n\n// Weighted hue difference (ΔH')\nvariable delta_h_prime: Number = 2 * sqrt(c_prime2 * c_prime1) * sin(delta_h * d2r / 2);\n\n// Mean lightness and chroma\nvariable l_bar: Number = (l1 + l2) / 2;\nvariable c_bar_prime: Number = (c_prime1 + c_prime2) / 2;\nvariable c_bar_prime7: Number = c_bar_prime * c_bar_prime * c_bar_prime * c_bar_prime * c_bar_prime * c_bar_prime * c_bar_prime;\n\n// Mean hue (handling the 0/360 wraparound)\nvariable h_bar: Number = 0;\nif (c_product == 0) [\n h_bar = h_sum;\n] else [\n if (h_abs <= 180) [\n h_bar = h_sum / 2;\n ] else [\n if (h_sum < 360) [\n h_bar = (h_sum + 360) / 2;\n ] else [\n h_bar = (h_sum - 360) / 2;\n ];\n ];\n];\n\n// SL - Lightness crispening factor (assumes L=50 background)\nvariable l_bar_minus_50: Number = l_bar - 50;\nvariable l_sq: Number = l_bar_minus_50 * l_bar_minus_50;\nvariable s_l: Number = 1 + (0.015 * l_sq) / sqrt(20 + l_sq);\n\n// SC - Chroma factor\nvariable s_c: Number = 1 + 0.045 * c_bar_prime;\n\n// T - Cross term for blue non-linearity\nvariable t: Number = 1;\nt = t - 0.17 * cos((h_bar - 30) * d2r);\nt = t + 0.24 * cos(2 * h_bar * d2r);\nt = t + 0.32 * cos((3 * h_bar + 6) * d2r);\nt = t - 0.20 * cos((4 * h_bar - 63) * d2r);\n\n// SH - Hue factor\nvariable s_h: Number = 1 + 0.015 * c_bar_prime * t;\n\n// RT - Hue rotation term (for blue region 225-315 degrees)\nvariable h_bar_minus_275: Number = h_bar - 275;\nvariable exp_arg: Number = -1 * (h_bar_minus_275 / 25) * (h_bar_minus_275 / 25);\nvariable delta_theta: Number = 30 * pow(e, exp_arg);\nvariable r_c: Number = 2 * sqrt(c_bar_prime7 / (c_bar_prime7 + g_factor));\nvariable r_t: Number = -1 * sin(2 * delta_theta * d2r) * r_c;\n\n// Calculate final ΔE00\nvariable term_l: Number = delta_l / (k_l * s_l);\nvariable term_c: Number = delta_c / (k_c * s_c);\nvariable term_h: Number = delta_h_prime / (k_h * s_h);\n\nvariable de: Number = term_l * term_l + term_c * term_c + term_h * term_h;\nde = de + r_t * (delta_c / (k_c * s_c)) * (delta_h_prime / (k_h * s_h));\n\nvariable result: Number = sqrt(de);\n\nreturn result;"
|
|
2112
|
-
},
|
|
2113
|
-
"requirements": [
|
|
2114
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/",
|
|
2115
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lch-color/0/"
|
|
2116
|
-
],
|
|
2117
|
-
"slug": "delta_e_2000"
|
|
2118
|
-
},
|
|
2119
|
-
{
|
|
2120
|
-
"name": "Delta E 76",
|
|
2121
|
-
"type": "function",
|
|
2122
|
-
"description": "Calculates color difference using CIE76 formula (ΔE*ab). Simple Euclidean distance in CIE Lab space. Less accurate than Delta E 2000 for small differences, but faster and adequate for many applications. Formula: sqrt((L1-L2)² + (a1-a2)² + (b1-b2)²).",
|
|
2123
|
-
"keyword": "delta_e_76",
|
|
2124
|
-
"input": {
|
|
2125
|
-
"type": "object",
|
|
2126
|
-
"properties": {
|
|
2127
|
-
"color1": {
|
|
2128
|
-
"type": "color",
|
|
2129
|
-
"description": "First color (reference)"
|
|
2130
|
-
},
|
|
2131
|
-
"color2": {
|
|
2132
|
-
"type": "color",
|
|
2133
|
-
"description": "Second color (sample)"
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
},
|
|
2137
|
-
"script": {
|
|
2138
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2139
|
-
"script": "// Delta E 76 (CIE76 Color Difference)\n// Reference: CIE Publication 15.2 (1986)\n// Reference: ASTM E308 Standard\n//\n// The original CIE color difference formula from 1976.\n// Simple Euclidean distance in CIE Lab space.\n//\n// Formula: ΔE*ab = sqrt((L1-L2)² + (a1-a2)² + (b1-b2)²)\n//\n// Interpretation:\n// 0 - 1: Not perceptible by human eye\n// 1 - 2: Perceptible through close observation\n// 2 - 10: Perceptible at a glance\n// 11 - 49: Colors are more similar than opposite\n// 100+: Colors are exact opposite\n//\n// Limitations:\n// - Lab is not perfectly perceptually uniform\n// - Overestimates differences for saturated colors\n// - Use Delta E 2000 for critical color matching\n\nvariable input: List = {input};\nvariable color1: Color.Lab = input.get(0).to.lab();\nvariable color2: Color.Lab = input.get(1).to.lab();\n\n// Get Lab components\nvariable l1: Number = color1.l;\nvariable a1: Number = color1.a;\nvariable b1: Number = color1.b;\n\nvariable l2: Number = color2.l;\nvariable a2: Number = color2.a;\nvariable b2: Number = color2.b;\n\n// Calculate differences\nvariable delta_l: Number = l1 - l2;\nvariable delta_a: Number = a1 - a2;\nvariable delta_b: Number = b1 - b2;\n\n// Euclidean distance\nvariable sum_squares: Number = delta_l * delta_l + delta_a * delta_a + delta_b * delta_b;\nvariable result: Number = sqrt(sum_squares);\n\nreturn result;"
|
|
2140
|
-
},
|
|
2141
|
-
"requirements": [
|
|
2142
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/"
|
|
2143
|
-
],
|
|
2144
|
-
"slug": "delta_e_76"
|
|
2145
|
-
},
|
|
2146
|
-
{
|
|
2147
|
-
"name": "Delta E OK",
|
|
2148
|
-
"type": "function",
|
|
2149
|
-
"description": "Calculates perceptual color difference using the OKLab color space (ΔE_OK). This is the Euclidean distance in OKLab space, providing excellent perceptual uniformity. Values: 0 = identical, <0.02 = imperceptible, <0.05 = very close, <0.1 = noticeable, >0.5 = very different.",
|
|
2150
|
-
"keyword": "delta_e_ok",
|
|
2151
|
-
"input": {
|
|
2152
|
-
"type": "object",
|
|
2153
|
-
"properties": {
|
|
2154
|
-
"color1": {
|
|
2155
|
-
"type": "color",
|
|
2156
|
-
"description": "First color (reference)"
|
|
2157
|
-
},
|
|
2158
|
-
"color2": {
|
|
2159
|
-
"type": "color",
|
|
2160
|
-
"description": "Second color (sample)"
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
},
|
|
2164
|
-
"script": {
|
|
2165
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2166
|
-
"script": "// Delta E OK (ΔE_OK)\n// Perceptual color difference in OKLab space\n// Reference: Björn Ottosson's OKLab specification\n// Reference: Color.js deltaEOK implementation\n//\n// This is simply the Euclidean distance in OKLab space.\n// OKLab is designed to be perceptually uniform, making this\n// a reliable measure of perceived color difference.\n//\n// Interpretation:\n// 0.00 - Identical colors\n// < 0.02 - Imperceptible difference (JND threshold)\n// < 0.05 - Very close colors\n// < 0.1 - Noticeable but small difference\n// 0.1 - 0.5 - Moderate difference\n// > 0.5 - Very different colors\n//\n// Formula: sqrt((L1-L2)² + (a1-a2)² + (b1-b2)²)\n\nvariable input: List = {input};\nvariable color1: Color.OKLab = input.get(0).to.oklab();\nvariable color2: Color.OKLab = input.get(1).to.oklab();\n\n// Get OKLab components\nvariable l1: Number = color1.l;\nvariable a1: Number = color1.a;\nvariable b1: Number = color1.b;\n\nvariable l2: Number = color2.l;\nvariable a2: Number = color2.a;\nvariable b2: Number = color2.b;\n\n// Calculate differences\nvariable delta_l: Number = l1 - l2;\nvariable delta_a: Number = a1 - a2;\nvariable delta_b: Number = b1 - b2;\n\n// Calculate Euclidean distance (sqrt of sum of squares)\nvariable sum_squares: Number = delta_l * delta_l + delta_a * delta_a + delta_b * delta_b;\nvariable result: Number = sqrt(sum_squares);\n\nreturn result;"
|
|
2167
|
-
},
|
|
2168
|
-
"requirements": [
|
|
2169
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/"
|
|
2170
|
-
],
|
|
2171
|
-
"slug": "delta_e_ok"
|
|
2172
|
-
},
|
|
2173
|
-
{
|
|
2174
|
-
"name": "Desaturate",
|
|
2175
|
-
"type": "function",
|
|
2176
|
-
"description": "Decreases color saturation by reducing chroma in OKLCH space. Amount is 0-1 where 0.25 = 25% less saturated.",
|
|
2177
|
-
"keyword": "desaturate",
|
|
2178
|
-
"input": {
|
|
2179
|
-
"type": "object",
|
|
2180
|
-
"properties": {
|
|
2181
|
-
"color": {
|
|
2182
|
-
"type": "color",
|
|
2183
|
-
"description": "The color to desaturate"
|
|
2184
|
-
},
|
|
2185
|
-
"amount": {
|
|
2186
|
-
"type": "number",
|
|
2187
|
-
"description": "Amount to desaturate (0-1). Default is 0.25"
|
|
2188
|
-
}
|
|
2189
|
-
}
|
|
2190
|
-
},
|
|
2191
|
-
"script": {
|
|
2192
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2193
|
-
"script": "// Desaturate a color by decreasing its OKLCH chroma\n// Amount: 0-1 where 0.25 means 25% decrease, 1.0 = full grayscale\n//\n// Algorithm: C' = C * (1 - amount)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new chroma\nvariable current_c: Number = color.c;\nvariable new_c: Number = current_c * (1 - amount);\n\n// Clamp to valid range\nif (new_c < 0) [ new_c = 0; ];\n\n// Create output in OKLCH, return as sRGB\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
2194
|
-
},
|
|
2195
|
-
"requirements": [
|
|
2196
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
2197
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2198
|
-
],
|
|
2199
|
-
"slug": "desaturate"
|
|
2200
|
-
},
|
|
2201
|
-
{
|
|
2202
|
-
"name": "Distributed",
|
|
2203
|
-
"type": "function",
|
|
2204
|
-
"description": "Creates evenly distributed colors around the OKLCH hue wheel. Perfect for categorical data where each category needs a distinct, visually balanced color.",
|
|
2205
|
-
"keyword": "distributed",
|
|
2206
|
-
"input": {
|
|
2207
|
-
"type": "object",
|
|
2208
|
-
"properties": {
|
|
2209
|
-
"count": {
|
|
2210
|
-
"type": "number",
|
|
2211
|
-
"description": "Number of colors to generate. Default is 6"
|
|
2212
|
-
},
|
|
2213
|
-
"lightness": {
|
|
2214
|
-
"type": "number",
|
|
2215
|
-
"description": "Lightness for all colors (0-1). Default is 0.7"
|
|
2216
|
-
},
|
|
2217
|
-
"chroma": {
|
|
2218
|
-
"type": "number",
|
|
2219
|
-
"description": "Chroma/saturation for all colors (0-0.4). Default is 0.15"
|
|
2220
|
-
},
|
|
2221
|
-
"start_hue": {
|
|
2222
|
-
"type": "number",
|
|
2223
|
-
"description": "Starting hue angle (0-360). Default is 30 (orange)"
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
},
|
|
2227
|
-
"script": {
|
|
2228
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2229
|
-
"script": "// Create evenly distributed categorical colors using OKLCH\n// Colors are spaced equally around the hue wheel\n//\n// Ideal for: pie charts, bar charts, legends, category labels\n// Uses golden angle offset for better distinction with many colors\n\nvariable input: List = {input};\n\n// Default count is 6\nvariable count: Number = 6;\nif (input.length() > 0) [\n count = input.get(0);\n];\n\n// Default lightness is 0.7 (good for both light/dark backgrounds)\nvariable lightness: Number = 0.7;\nif (input.length() > 1) [\n lightness = input.get(1);\n];\n\n// Default chroma is 0.15 (vivid but not overwhelming)\nvariable chroma: Number = 0.15;\nif (input.length() > 2) [\n chroma = input.get(2);\n];\n\n// Default start hue is 30 (orange - visually distinct starting point)\nvariable start_hue: Number = 30;\nif (input.length() > 3) [\n start_hue = input.get(3);\n];\n\n// Calculate hue step (360° / count for even distribution)\nvariable hue_step: Number = 360 / count;\n\nvariable result: List;\nvariable i: Number = 0;\nvariable current_hue: Number = 0;\nvariable color: Color.OKLCH;\n\nwhile (i < count) [\n current_hue = start_hue + i * hue_step;\n if (current_hue >= 360) [ current_hue = current_hue - 360; ];\n \n color.l = lightness;\n color.c = chroma;\n color.h = current_hue;\n \n result = result, color.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
2230
|
-
},
|
|
2231
|
-
"requirements": [
|
|
2232
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
2233
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2234
|
-
],
|
|
2235
|
-
"slug": "distributed"
|
|
2236
|
-
},
|
|
2237
|
-
{
|
|
2238
|
-
"name": "Diverging",
|
|
2239
|
-
"type": "function",
|
|
2240
|
-
"description": "Creates a diverging color palette for heatmaps. Goes from color1 through a neutral midpoint to color2. Perfect for data that diverges from a center value (e.g., -1 to 0 to +1).",
|
|
2241
|
-
"keyword": "diverging",
|
|
2242
|
-
"input": {
|
|
2243
|
-
"type": "object",
|
|
2244
|
-
"properties": {
|
|
2245
|
-
"color1": {
|
|
2246
|
-
"type": "color",
|
|
2247
|
-
"description": "Color for negative/low end (e.g., blue)"
|
|
2248
|
-
},
|
|
2249
|
-
"color2": {
|
|
2250
|
-
"type": "color",
|
|
2251
|
-
"description": "Color for positive/high end (e.g., red)"
|
|
2252
|
-
},
|
|
2253
|
-
"count": {
|
|
2254
|
-
"type": "number",
|
|
2255
|
-
"description": "Number of steps (should be odd for clear midpoint). Default is 9"
|
|
2256
|
-
},
|
|
2257
|
-
"neutral": {
|
|
2258
|
-
"type": "number",
|
|
2259
|
-
"description": "Lightness of neutral midpoint (0-1). Default is 0.95 (near white)"
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
},
|
|
2263
|
-
"script": {
|
|
2264
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2265
|
-
"script": "// Create a diverging color palette for data visualization\n// Goes: color1 → neutral → color2\n//\n// Ideal for: heatmaps, correlation matrices, deviation from mean\n// Example: blue → white → red for temperature anomalies\n\nvariable input: List = {input};\nvariable color1: Color.OKLCH = input.get(0).to.oklch();\nvariable color2: Color.OKLCH = input.get(1).to.oklch();\n\n// Default count is 9 (gives clear midpoint at index 4)\nvariable count: Number = 9;\nif (input.length() > 2) [\n count = input.get(2);\n];\n\n// Default neutral lightness is 0.95 (near white)\nvariable neutral_l: Number = 0.95;\nif (input.length() > 3) [\n neutral_l = input.get(3);\n];\n\n// Calculate midpoint index\nvariable mid: Number = (count - 1) / 2;\n\nvariable result: List;\nvariable i: Number = 0;\nvariable t: Number = 0;\nvariable step_color: Color.OKLCH;\n\nwhile (i < count) [\n if (i < mid) [\n // First half: color1 → neutral\n t = i / mid;\n step_color.l = color1.l + (neutral_l - color1.l) * t;\n step_color.c = color1.c * (1 - t);\n step_color.h = color1.h;\n ] else [\n if (i == mid) [\n // Midpoint: neutral (achromatic)\n step_color.l = neutral_l;\n step_color.c = 0;\n step_color.h = 0;\n ] else [\n // Second half: neutral → color2\n t = (i - mid) / mid;\n step_color.l = neutral_l + (color2.l - neutral_l) * t;\n step_color.c = color2.c * t;\n step_color.h = color2.h;\n ];\n ];\n \n result = result, step_color.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
2266
|
-
},
|
|
2267
|
-
"requirements": [
|
|
2268
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
2269
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2270
|
-
],
|
|
2271
|
-
"slug": "diverging"
|
|
2272
|
-
},
|
|
2273
|
-
{
|
|
2274
|
-
"name": "Grayscale",
|
|
2275
|
-
"type": "function",
|
|
2276
|
-
"description": "Converts a color to grayscale by removing all chroma in OKLCH space. Preserves perceptual lightness.",
|
|
2277
|
-
"keyword": "grayscale",
|
|
2278
|
-
"input": {
|
|
2279
|
-
"type": "object",
|
|
2280
|
-
"properties": {
|
|
2281
|
-
"color": {
|
|
2282
|
-
"type": "color",
|
|
2283
|
-
"description": "The color to convert to grayscale"
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
},
|
|
2287
|
-
"script": {
|
|
2288
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2289
|
-
"script": "// Convert a color to grayscale by setting OKLCH chroma to 0\n// This preserves perceptual lightness (unlike simple RGB averaging)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Create grayscale by removing chroma\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = 0;\nresult.h = 0;\n\nreturn result;"
|
|
2290
|
-
},
|
|
2291
|
-
"requirements": [
|
|
2292
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
2293
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2294
|
-
],
|
|
2295
|
-
"slug": "grayscale"
|
|
2296
|
-
},
|
|
2297
|
-
{
|
|
2298
|
-
"name": "harmonize",
|
|
2299
|
-
"type": "function",
|
|
2300
|
-
"description": "Harmonizes a color by shifting its hue toward a source color. Inspired by Material Design 3's color harmonization algorithm. This creates visual cohesion in design systems by making colors feel related while preserving their identity. The amount parameter controls how much the hue shifts (0 = no change, 1 = full shift to source hue).",
|
|
2301
|
-
"keyword": "harmonize",
|
|
2302
|
-
"requirements": [
|
|
2303
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2304
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2305
|
-
],
|
|
2306
|
-
"schema": {
|
|
2307
|
-
"type": "object",
|
|
2308
|
-
"properties": {
|
|
2309
|
-
"input": {
|
|
2310
|
-
"type": "array",
|
|
2311
|
-
"items": [
|
|
2312
|
-
{
|
|
2313
|
-
"description": "Color to harmonize",
|
|
2314
|
-
"type": "color"
|
|
2315
|
-
},
|
|
2316
|
-
{
|
|
2317
|
-
"description": "Source color (hue target)",
|
|
2318
|
-
"type": "color"
|
|
2319
|
-
},
|
|
2320
|
-
{
|
|
2321
|
-
"description": "Amount of harmonization (0-1, default 0.5)",
|
|
2322
|
-
"type": "number"
|
|
2323
|
-
}
|
|
2324
|
-
],
|
|
2325
|
-
"minItems": 2,
|
|
2326
|
-
"maxItems": 3
|
|
2327
|
-
}
|
|
2328
|
-
},
|
|
2329
|
-
"required": [
|
|
2330
|
-
"input"
|
|
2331
|
-
]
|
|
2332
|
-
},
|
|
2333
|
-
"returns": {
|
|
2334
|
-
"type": "color",
|
|
2335
|
-
"description": "Harmonized color with hue shifted toward source"
|
|
2336
|
-
},
|
|
2337
|
-
"script": {
|
|
2338
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2339
|
-
"script": "// harmonize: Shift hue towards a source color for visual harmony\n// Reference: Material Design 3 Color System\n// Reference: https://m3.material.io/styles/color/dynamic-color\n//\n// Shifts the input color's hue towards a source color by a specified\n// amount, creating a harmonized palette. Uses OKLCH for perceptually\n// uniform hue interpolation via the shortest angular path.\n//\n// Parameters:\n// color - Color to harmonize\n// source - Target hue source\n// amount - Shift amount (0 = no change, 1 = match source), default 0.5\n//\n// Input: Any color space (converted to OKLCH internally)\n// Output: OKLCH (working space)\n// To get sRGB: harmonize(color, source, 0.5).to.srgb()\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable source: Color.OKLCH = input.get(1).to.oklch();\n\n// Default harmonization amount (0.5 = 50% shift)\nvariable amount: Number = 0.5;\nif (input.length() > 2) [\n amount = input.get(2);\n];\n\n// Get hues\nvariable color_hue: Number = color.h;\nvariable source_hue: Number = source.h;\n\n// Calculate shortest path around the hue circle\nvariable diff: Number = source_hue - color_hue;\n\n// Normalize to -180 to 180 range for shortest path\nif (diff > 180) [\n diff = diff - 360;\n];\nif (diff < -180) [\n diff = diff + 360;\n];\n\n// Apply harmonization: shift hue by amount * difference\nvariable new_hue: Number = color_hue + (diff * amount);\n\n// Normalize hue to 0-360\nif (new_hue < 0) [\n new_hue = new_hue + 360;\n];\nif (new_hue >= 360) [\n new_hue = new_hue - 360;\n];\n\n// Create result with shifted hue, preserving lightness and chroma\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = new_hue;\n\nreturn result;"
|
|
2340
|
-
},
|
|
2341
|
-
"examples": [
|
|
2342
|
-
{
|
|
2343
|
-
"description": "Harmonize red toward blue",
|
|
2344
|
-
"input": [
|
|
2345
|
-
"#ff0000",
|
|
2346
|
-
"#0000ff",
|
|
2347
|
-
0.3
|
|
2348
|
-
],
|
|
2349
|
-
"output": "Color with hue shifted toward blue"
|
|
2350
|
-
},
|
|
2351
|
-
{
|
|
2352
|
-
"description": "Harmonize green toward brand color",
|
|
2353
|
-
"input": [
|
|
2354
|
-
"#00ff00",
|
|
2355
|
-
"#6750a4"
|
|
2356
|
-
],
|
|
2357
|
-
"output": "Green shifted toward purple brand"
|
|
2358
|
-
}
|
|
2359
|
-
],
|
|
2360
|
-
"slug": "harmonize"
|
|
2361
|
-
},
|
|
2362
|
-
{
|
|
2363
|
-
"name": "hue",
|
|
2364
|
-
"type": "function",
|
|
2365
|
-
"description": "Extracts the hue angle from any color using OKLCH. Returns a value from 0 to 360 degrees representing the color's position on the hue wheel. Hue in OKLCH is perceptually uniform, meaning equal angular steps produce equally perceived hue changes. Essential for color harmony calculations and palette generation.",
|
|
2366
|
-
"keyword": "hue",
|
|
2367
|
-
"requirements": [
|
|
2368
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2369
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2370
|
-
],
|
|
2371
|
-
"schema": {
|
|
2372
|
-
"type": "object",
|
|
2373
|
-
"properties": {
|
|
2374
|
-
"input": {
|
|
2375
|
-
"type": "array",
|
|
2376
|
-
"items": [
|
|
2377
|
-
{
|
|
2378
|
-
"description": "Color to extract hue from",
|
|
2379
|
-
"type": "color"
|
|
2380
|
-
}
|
|
2381
|
-
],
|
|
2382
|
-
"minItems": 1,
|
|
2383
|
-
"maxItems": 1
|
|
2384
|
-
}
|
|
2385
|
-
},
|
|
2386
|
-
"required": [
|
|
2387
|
-
"input"
|
|
2388
|
-
]
|
|
2389
|
-
},
|
|
2390
|
-
"returns": {
|
|
2391
|
-
"type": "number",
|
|
2392
|
-
"description": "Hue angle (0-360 degrees)"
|
|
2393
|
-
},
|
|
2394
|
-
"script": {
|
|
2395
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2396
|
-
"script": "// hue: Extract hue angle from a color\n// Reference: OKLCH Color Space (Björn Ottosson)\n// Reference: https://bottosson.github.io/posts/oklab/\n//\n// Returns the H component from OKLCH, representing the\n// perceptually uniform hue angle. Range: 0-360 degrees.\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\nreturn color.h;"
|
|
2397
|
-
},
|
|
2398
|
-
"examples": [
|
|
2399
|
-
{
|
|
2400
|
-
"description": "Red has hue around 30° in OKLCH",
|
|
2401
|
-
"input": [
|
|
2402
|
-
"#ff0000"
|
|
2403
|
-
],
|
|
2404
|
-
"output": 29
|
|
2405
|
-
},
|
|
2406
|
-
{
|
|
2407
|
-
"description": "Cyan has hue around 195°",
|
|
2408
|
-
"input": [
|
|
2409
|
-
"#00ffff"
|
|
2410
|
-
],
|
|
2411
|
-
"output": 195
|
|
2412
|
-
}
|
|
2413
|
-
],
|
|
2414
|
-
"slug": "hue"
|
|
2415
|
-
},
|
|
2416
|
-
{
|
|
2417
|
-
"name": "Hue Difference",
|
|
2418
|
-
"type": "function",
|
|
2419
|
-
"description": "Calculates the angular difference between two color hues. Returns the shortest path around the hue circle (0-180°). Useful for determining how different two colors are in terms of hue alone, ignoring lightness and chroma.",
|
|
2420
|
-
"keyword": "hue_difference",
|
|
2421
|
-
"input": {
|
|
2422
|
-
"type": "object",
|
|
2423
|
-
"properties": {
|
|
2424
|
-
"color1": {
|
|
2425
|
-
"type": "color",
|
|
2426
|
-
"description": "First color"
|
|
2427
|
-
},
|
|
2428
|
-
"color2": {
|
|
2429
|
-
"type": "color",
|
|
2430
|
-
"description": "Second color"
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
},
|
|
2434
|
-
"script": {
|
|
2435
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2436
|
-
"script": "// Hue Difference\n// Calculates angular hue difference using shortest path\n//\n// The hue circle wraps at 360°, so we need to find\n// the shortest angular distance between two hues.\n//\n// Examples:\n// hue_diff(350°, 10°) = 20° (not 340°)\n// hue_diff(0°, 180°) = 180° (maximum difference)\n// hue_diff(90°, 90°) = 0° (same hue)\n//\n// Returns value in range [0, 180]\n\nvariable input: List = {input};\nvariable color1: Color.OKLCH = input.get(0).to.oklch();\nvariable color2: Color.OKLCH = input.get(1).to.oklch();\n\n// Get hue values\nvariable h1: Number = color1.h;\nvariable h2: Number = color2.h;\n\n// Normalize to 0-360\nif (h1 < 0) [ h1 = h1 + 360; ];\nif (h1 >= 360) [ h1 = h1 - 360; ];\nif (h2 < 0) [ h2 = h2 + 360; ];\nif (h2 >= 360) [ h2 = h2 - 360; ];\n\n// Calculate raw difference\nvariable diff: Number = h1 - h2;\n\n// Make positive\nif (diff < 0) [\n diff = 0 - diff;\n];\n\n// Take shortest path around circle\nif (diff > 180) [\n diff = 360 - diff;\n];\n\nreturn diff;"
|
|
2437
|
-
},
|
|
2438
|
-
"requirements": [
|
|
2439
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2440
|
-
],
|
|
2441
|
-
"slug": "hue_difference"
|
|
2442
|
-
},
|
|
2443
|
-
{
|
|
2444
|
-
"name": "In Gamut",
|
|
2445
|
-
"type": "function",
|
|
2446
|
-
"description": "Checks if a color is within the sRGB gamut. Returns true if all RGB components are in [0,1] range, false otherwise. Useful for checking if colors from wider gamuts (P3, OKLab) can be displayed on standard monitors.",
|
|
2447
|
-
"keyword": "in_gamut",
|
|
2448
|
-
"input": {
|
|
2449
|
-
"type": "object",
|
|
2450
|
-
"properties": {
|
|
2451
|
-
"color": {
|
|
2452
|
-
"type": "color",
|
|
2453
|
-
"description": "Color to check"
|
|
2454
|
-
},
|
|
2455
|
-
"epsilon": {
|
|
2456
|
-
"type": "number",
|
|
2457
|
-
"description": "Tolerance for near-gamut colors (default 0.000075)"
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
},
|
|
2461
|
-
"script": {
|
|
2462
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2463
|
-
"script": "// In Gamut Check\n// Checks if a color is within the sRGB gamut\n// Reference: Color.js inGamut implementation\n//\n// sRGB gamut is defined by all RGB components being in [0, 1]\n// This is critical for determining if colors can be displayed\n// on standard monitors without clipping.\n//\n// An epsilon tolerance is used to handle floating-point precision\n// issues near the gamut boundary.\n\nvariable input: List = {input};\nvariable color: Color.SRGB = input.get(0).to.srgb();\n\n// Default epsilon from Color.js\nvariable epsilon: Number = 0.000075;\nif (input.length() > 1) [\n epsilon = input.get(1);\n];\n\n// Get RGB values\nvariable r: Number = color.r;\nvariable g: Number = color.g;\nvariable b: Number = color.b;\n\n// Check if within gamut bounds with epsilon tolerance\nvariable min_bound: Number = 0 - epsilon;\nvariable max_bound: Number = 1 + epsilon;\n\n// All channels must be within bounds\nvariable r_in: Boolean = r >= min_bound;\nif (r_in) [ r_in = r <= max_bound; ];\n\nvariable g_in: Boolean = g >= min_bound;\nif (g_in) [ g_in = g <= max_bound; ];\n\nvariable b_in: Boolean = b >= min_bound;\nif (b_in) [ b_in = b <= max_bound; ];\n\n// Return true only if all channels are in gamut\nvariable result: Boolean = false;\nif (r_in) [\n if (g_in) [\n if (b_in) [\n result = true;\n ];\n ];\n];\n\nreturn result;"
|
|
2464
|
-
},
|
|
2465
|
-
"requirements": [
|
|
2466
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2467
|
-
],
|
|
2468
|
-
"slug": "in_gamut"
|
|
2469
|
-
},
|
|
2470
|
-
{
|
|
2471
|
-
"name": "interpolate",
|
|
2472
|
-
"type": "function",
|
|
2473
|
-
"description": "Interpolates between two colors at a given position in perceptual OKLCH space. Position of 0 returns the first color, 1 returns the second color, 0.5 returns the midpoint. Uses shortest-path hue interpolation for natural color transitions.",
|
|
2474
|
-
"keyword": "interpolate",
|
|
2475
|
-
"requirements": [
|
|
2476
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2477
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2478
|
-
],
|
|
2479
|
-
"schema": {
|
|
2480
|
-
"type": "object",
|
|
2481
|
-
"properties": {
|
|
2482
|
-
"input": {
|
|
2483
|
-
"type": "array",
|
|
2484
|
-
"items": [
|
|
2485
|
-
{
|
|
2486
|
-
"description": "Start color",
|
|
2487
|
-
"type": "color"
|
|
2488
|
-
},
|
|
2489
|
-
{
|
|
2490
|
-
"description": "End color",
|
|
2491
|
-
"type": "color"
|
|
2492
|
-
},
|
|
2493
|
-
{
|
|
2494
|
-
"description": "Position (0-1, default 0.5)",
|
|
2495
|
-
"type": "number"
|
|
2496
|
-
}
|
|
2497
|
-
],
|
|
2498
|
-
"minItems": 2,
|
|
2499
|
-
"maxItems": 3
|
|
2500
|
-
}
|
|
2501
|
-
},
|
|
2502
|
-
"required": [
|
|
2503
|
-
"input"
|
|
2504
|
-
]
|
|
2505
|
-
},
|
|
2506
|
-
"returns": {
|
|
2507
|
-
"type": "color",
|
|
2508
|
-
"description": "Interpolated color"
|
|
2509
|
-
},
|
|
2510
|
-
"script": {
|
|
2511
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2512
|
-
"script": "// interpolate: Blend between two colors in perceptual space\n// Reference: CSS Color Level 4 Color Interpolation\n// Reference: https://www.w3.org/TR/css-color-4/#interpolation\n//\n// Performs linear interpolation in OKLCH space with shortest-path\n// hue interpolation. OKLCH provides perceptually uniform results.\n//\n// Parameters:\n// start - First color (t=0)\n// end - Second color (t=1)\n// t - Interpolation factor [0, 1], default 0.5\n//\n// Input: Any color space (converted to OKLCH internally)\n// Output: OKLCH (working space)\n// To get sRGB: interpolate(start, end, 0.5).to.srgb()\n\nvariable input: List = {input};\nvariable start: Color.OKLCH = input.get(0).to.oklch();\nvariable end: Color.OKLCH = input.get(1).to.oklch();\n\n// Default to midpoint\nvariable t: Number = 0.5;\nif (input.length() > 2) [\n t = input.get(2);\n];\n\n// Clamp t to valid range\nif (t < 0) [ t = 0; ];\nif (t > 1) [ t = 1; ];\n\n// Interpolate lightness and chroma linearly\nvariable result_l: Number = start.l + (end.l - start.l) * t;\nvariable result_c: Number = start.c + (end.c - start.c) * t;\n\n// Interpolate hue using shortest path\nvariable h1: Number = start.h;\nvariable h2: Number = end.h;\nvariable delta_h: Number = h2 - h1;\n\n// Normalize to shortest path around the circle\nif (delta_h > 180) [\n delta_h = delta_h - 360;\n];\nif (delta_h < -180) [\n delta_h = delta_h + 360;\n];\n\nvariable result_h: Number = h1 + delta_h * t;\n\n// Normalize result hue to 0-360\nif (result_h < 0) [ result_h = result_h + 360; ];\nif (result_h >= 360) [ result_h = result_h - 360; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = result_l;\nresult.c = result_c;\nresult.h = result_h;\n\nreturn result;"
|
|
2513
|
-
},
|
|
2514
|
-
"examples": [
|
|
2515
|
-
{
|
|
2516
|
-
"description": "Midpoint between red and blue",
|
|
2517
|
-
"input": [
|
|
2518
|
-
"#ff0000",
|
|
2519
|
-
"#0000ff",
|
|
2520
|
-
0.5
|
|
2521
|
-
],
|
|
2522
|
-
"output": "Purple-ish color in OKLCH"
|
|
2523
|
-
},
|
|
2524
|
-
{
|
|
2525
|
-
"description": "Quarter way from black to white",
|
|
2526
|
-
"input": [
|
|
2527
|
-
"#000000",
|
|
2528
|
-
"#ffffff",
|
|
2529
|
-
0.25
|
|
2530
|
-
],
|
|
2531
|
-
"output": "Dark gray"
|
|
2532
|
-
}
|
|
2533
|
-
],
|
|
2534
|
-
"slug": "interpolate"
|
|
2535
|
-
},
|
|
2536
|
-
{
|
|
2537
|
-
"name": "Invert Color",
|
|
2538
|
-
"type": "function",
|
|
2539
|
-
"description": "Inverts a color by inverting each RGB channel (R' = 255 - R, G' = 255 - G, B' = 255 - B).",
|
|
2540
|
-
"keyword": "invert",
|
|
2541
|
-
"input": {
|
|
2542
|
-
"type": "object",
|
|
2543
|
-
"properties": {
|
|
2544
|
-
"color": {
|
|
2545
|
-
"type": "color",
|
|
2546
|
-
"description": "The color to invert."
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
},
|
|
2550
|
-
"script": {
|
|
2551
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2552
|
-
"script": "// Invert Color Function\n// Returns the inverse/negative of a color by subtracting each RGB channel from 255\n//\n// Algorithm:\n// R' = 255 - R\n// G' = 255 - G \n// B' = 255 - B\n//\n// Use cases:\n// - Creating negative images\n// - High contrast accessibility variants\n// - Artistic color effects\n//\n// Input: Any color (converted to RGB internally)\n// Output: Color.Rgb with inverted channels\n\nvariable input: List = {input};\n\n// Convert input to RGB (0-255 range)\nvariable rgb_color: Color.Rgb = input.get(0).to.rgb();\n\n// Invert each channel by subtracting from 255\nvariable inverted_r: Number = 255 - rgb_color.r;\nvariable inverted_g: Number = 255 - rgb_color.g;\nvariable inverted_b: Number = 255 - rgb_color.b;\n\n// Create and return the inverted color\nvariable inverted_color: Color.Rgb = rgb(inverted_r, inverted_g, inverted_b);\nreturn inverted_color;"
|
|
2553
|
-
},
|
|
2554
|
-
"requirements": [
|
|
2555
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/"
|
|
2556
|
-
],
|
|
2557
|
-
"slug": "invert"
|
|
2558
|
-
},
|
|
2559
|
-
{
|
|
2560
|
-
"name": "Is Cool",
|
|
2561
|
-
"type": "function",
|
|
2562
|
-
"description": "Checks if a color is perceptually cool. Cool colors have hues in the green-blue-purple range (roughly 130-320° in OKLCH). Returns true for cool colors (green, cyan, blue, purple), false for warm colors. Achromatic colors are considered neutral/cool.",
|
|
2563
|
-
"keyword": "is_cool",
|
|
2564
|
-
"input": {
|
|
2565
|
-
"type": "object",
|
|
2566
|
-
"properties": {
|
|
2567
|
-
"color": {
|
|
2568
|
-
"type": "color",
|
|
2569
|
-
"description": "Color to check"
|
|
2570
|
-
}
|
|
2571
|
-
}
|
|
2572
|
-
},
|
|
2573
|
-
"script": {
|
|
2574
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2575
|
-
"script": "// Is Cool Color\n// Determines if a color is perceptually cool\n//\n// Cool colors are associated with:\n// - Water, ice, sky\n// - Calm, professional, trustworthy feelings\n// - Receding visual perception (appear further away)\n//\n// OKLCH hue ranges:\n// - Cool: 130-320° (yellow-green through purple)\n// - Includes: green, cyan, blue, purple\n//\n// Achromatic colors (low chroma) are considered cool/neutral\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Get hue and chroma\nvariable h: Number = color.h;\nvariable c: Number = color.c;\n\n// Achromatic colors (very low chroma) are considered cool/neutral\nif (c < 0.02) [\n return true;\n];\n\n// Normalize hue to 0-360\nif (h < 0) [ h = h + 360; ];\nif (h >= 360) [ h = h - 360; ];\n\n// Cool hue range: 130-320° (green through purple)\nvariable is_cool: Boolean = false;\n\nif (h > 130) [\n if (h < 320) [\n is_cool = true;\n ];\n];\n\nreturn is_cool;"
|
|
2576
|
-
},
|
|
2577
|
-
"requirements": [
|
|
2578
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2579
|
-
],
|
|
2580
|
-
"slug": "is_cool"
|
|
2581
|
-
},
|
|
2582
|
-
{
|
|
2583
|
-
"name": "Is Dark",
|
|
2584
|
-
"type": "function",
|
|
2585
|
-
"description": "Returns true if a color is perceptually dark (lightness <= threshold). Uses OKLCH lightness for perceptual accuracy. Default threshold is 0.6.",
|
|
2586
|
-
"keyword": "is_dark",
|
|
2587
|
-
"input": {
|
|
2588
|
-
"type": "object",
|
|
2589
|
-
"properties": {
|
|
2590
|
-
"color": {
|
|
2591
|
-
"type": "color",
|
|
2592
|
-
"description": "The color to check"
|
|
2593
|
-
},
|
|
2594
|
-
"threshold": {
|
|
2595
|
-
"type": "number",
|
|
2596
|
-
"description": "Lightness threshold (0-1). Default is 0.6"
|
|
2597
|
-
}
|
|
2598
|
-
}
|
|
2599
|
-
},
|
|
2600
|
-
"script": {
|
|
2601
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2602
|
-
"script": "// Check if a color is perceptually dark\n// Uses OKLCH lightness for accurate perception matching\n//\n// Returns: true if L <= threshold, false otherwise\n// Default threshold: 0.6 (slightly above middle)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\nvariable threshold: Number = 0.6;\nif (input.length() > 1) [\n threshold = input.get(1);\n];\n\nreturn color.l <= threshold;"
|
|
2603
|
-
},
|
|
2604
|
-
"requirements": [
|
|
2605
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2606
|
-
],
|
|
2607
|
-
"slug": "is_dark"
|
|
2608
|
-
},
|
|
2609
|
-
{
|
|
2610
|
-
"name": "Is Light",
|
|
2611
|
-
"type": "function",
|
|
2612
|
-
"description": "Returns true if a color is perceptually light (lightness > threshold). Uses OKLCH lightness for perceptual accuracy. Default threshold is 0.6.",
|
|
2613
|
-
"keyword": "is_light",
|
|
2614
|
-
"input": {
|
|
2615
|
-
"type": "object",
|
|
2616
|
-
"properties": {
|
|
2617
|
-
"color": {
|
|
2618
|
-
"type": "color",
|
|
2619
|
-
"description": "The color to check"
|
|
2620
|
-
},
|
|
2621
|
-
"threshold": {
|
|
2622
|
-
"type": "number",
|
|
2623
|
-
"description": "Lightness threshold (0-1). Default is 0.6"
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
|
-
},
|
|
2627
|
-
"script": {
|
|
2628
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2629
|
-
"script": "// Check if a color is perceptually light\n// Uses OKLCH lightness for accurate perception matching\n//\n// Returns: true if L > threshold, false otherwise\n// Default threshold: 0.6 (slightly above middle)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\nvariable threshold: Number = 0.6;\nif (input.length() > 1) [\n threshold = input.get(1);\n];\n\nreturn color.l > threshold;"
|
|
2630
|
-
},
|
|
2631
|
-
"requirements": [
|
|
2632
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2633
|
-
],
|
|
2634
|
-
"slug": "is_light"
|
|
2635
|
-
},
|
|
2636
|
-
{
|
|
2637
|
-
"name": "is_neutral",
|
|
2638
|
-
"type": "function",
|
|
2639
|
-
"description": "Determines if a color is achromatic (neutral/gray). Uses OKLCH chroma value - colors with very low chroma are considered neutral regardless of their hue. Default threshold of 0.02 allows for near-neutral colors that appear gray to human observers.",
|
|
2640
|
-
"keyword": "is_neutral",
|
|
2641
|
-
"requirements": [
|
|
2642
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2643
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2644
|
-
],
|
|
2645
|
-
"schema": {
|
|
2646
|
-
"type": "object",
|
|
2647
|
-
"properties": {
|
|
2648
|
-
"input": {
|
|
2649
|
-
"type": "array",
|
|
2650
|
-
"items": [
|
|
2651
|
-
{
|
|
2652
|
-
"description": "Color to test",
|
|
2653
|
-
"type": "color"
|
|
2654
|
-
},
|
|
2655
|
-
{
|
|
2656
|
-
"description": "Chroma threshold (default 0.02). Lower = stricter.",
|
|
2657
|
-
"type": "number"
|
|
2658
|
-
}
|
|
2659
|
-
],
|
|
2660
|
-
"minItems": 1,
|
|
2661
|
-
"maxItems": 2
|
|
2662
|
-
}
|
|
2663
|
-
},
|
|
2664
|
-
"required": [
|
|
2665
|
-
"input"
|
|
2666
|
-
]
|
|
2667
|
-
},
|
|
2668
|
-
"returns": {
|
|
2669
|
-
"type": "boolean",
|
|
2670
|
-
"description": "True if color is achromatic/neutral"
|
|
2671
|
-
},
|
|
2672
|
-
"script": {
|
|
2673
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2674
|
-
"script": "// is_neutral: Check if a color is achromatic (gray)\n// Uses OKLCH chroma - low chroma means neutral\n// Threshold of 0.02 allows for near-neutral colors\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default chroma threshold\nvariable threshold: Number = 0.02;\nif (input.length() > 1) [\n threshold = input.get(1);\n];\n\n// Color is neutral if chroma is below threshold\nreturn color.c < threshold;"
|
|
2675
|
-
},
|
|
2676
|
-
"examples": [
|
|
2677
|
-
{
|
|
2678
|
-
"description": "Pure gray is neutral",
|
|
2679
|
-
"input": [
|
|
2680
|
-
"#808080"
|
|
2681
|
-
],
|
|
2682
|
-
"output": true
|
|
2683
|
-
},
|
|
2684
|
-
{
|
|
2685
|
-
"description": "Pure red is not neutral",
|
|
2686
|
-
"input": [
|
|
2687
|
-
"#ff0000"
|
|
2688
|
-
],
|
|
2689
|
-
"output": false
|
|
2690
|
-
},
|
|
2691
|
-
{
|
|
2692
|
-
"description": "Desaturated color may be neutral",
|
|
2693
|
-
"input": [
|
|
2694
|
-
"#7a7a7c"
|
|
2695
|
-
],
|
|
2696
|
-
"output": true
|
|
2697
|
-
}
|
|
2698
|
-
],
|
|
2699
|
-
"slug": "is_neutral"
|
|
2700
|
-
},
|
|
2701
|
-
{
|
|
2702
|
-
"name": "Is Warm",
|
|
2703
|
-
"type": "function",
|
|
2704
|
-
"description": "Checks if a color is perceptually warm. Uses OKLCH color space where warm colors have hues in the red-yellow range (roughly 0-130° and 320-360°). Returns true for warm colors (red, orange, yellow), false for cool colors (green, cyan, blue, purple). Achromatic colors are considered neutral/cool.",
|
|
2705
|
-
"keyword": "is_warm",
|
|
2706
|
-
"input": {
|
|
2707
|
-
"type": "object",
|
|
2708
|
-
"properties": {
|
|
2709
|
-
"color": {
|
|
2710
|
-
"type": "color",
|
|
2711
|
-
"description": "Color to check"
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
},
|
|
2715
|
-
"script": {
|
|
2716
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2717
|
-
"script": "// Is Warm Color\n// Determines if a color is perceptually warm or cool\n//\n// Color Temperature Psychology:\n// - Warm colors: Red, orange, yellow (associated with fire, sun)\n// - Cool colors: Blue, green, purple (associated with water, ice)\n//\n// Hue ranges (in OKLCH/HSL degrees):\n// - Warm: roughly 0-90° (red through yellow) and 330-360° (red-magenta)\n// - Cool: roughly 90-330° (yellow-green through purple)\n//\n// Achromatic colors (low chroma) are considered neutral\n// and return false (lean towards cool for safety in accessibility)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Get hue and chroma\nvariable h: Number = color.h;\nvariable c: Number = color.c;\n\n// Achromatic colors (very low chroma) are neutral\n// Return false (cool) for these\nif (c < 0.02) [\n return false;\n];\n\n// Normalize hue to 0-360\nif (h < 0) [ h = h + 360; ];\nif (h >= 360) [ h = h - 360; ];\n\n// OKLCH Warm hue ranges:\n// In OKLCH, hue angles differ from HSL:\n// - Red: ~29°\n// - Orange: ~53°\n// - Yellow: ~110°\n// - Yellow-green: ~130°\n// - Green: ~145°\n// - Cyan: ~195°\n// - Blue: ~265°\n// - Purple: ~300°\n// - Magenta: ~330°\n//\n// Warm colors: 0-130° (red through yellow-green) and 320-360° (magenta-red)\nvariable is_warm: Boolean = false;\n\n// Check 0-130 range (red through yellow to yellow-green)\nif (h >= 0) [\n if (h <= 130) [\n is_warm = true;\n ];\n];\n\n// Check 320-360 range (magenta-red)\nif (h >= 320) [\n if (h <= 360) [\n is_warm = true;\n ];\n];\n\nreturn is_warm;"
|
|
2718
|
-
},
|
|
2719
|
-
"requirements": [
|
|
2720
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2721
|
-
],
|
|
2722
|
-
"slug": "is_warm"
|
|
2723
|
-
},
|
|
2724
|
-
{
|
|
2725
|
-
"name": "Lighten",
|
|
2726
|
-
"type": "function",
|
|
2727
|
-
"description": "Makes a color lighter by increasing its lightness in OKLab space. Amount is 0-1 where 0.25 = 25% lighter.",
|
|
2728
|
-
"keyword": "lighten",
|
|
2729
|
-
"input": {
|
|
2730
|
-
"type": "object",
|
|
2731
|
-
"properties": {
|
|
2732
|
-
"color": {
|
|
2733
|
-
"type": "color",
|
|
2734
|
-
"description": "The color to lighten"
|
|
2735
|
-
},
|
|
2736
|
-
"amount": {
|
|
2737
|
-
"type": "number",
|
|
2738
|
-
"description": "Amount to lighten (0-1). Default is 0.25"
|
|
2739
|
-
}
|
|
2740
|
-
}
|
|
2741
|
-
},
|
|
2742
|
-
"script": {
|
|
2743
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2744
|
-
"script": "// lighten: Increase lightness proportionally towards white\n// Amount: 0-1 where 0.25 means 25% closer to white\n//\n// Algorithm: L' = L + (1 - L) * amount\n// This ensures we approach white (L=1) proportionally.\n//\n// Input: Any color space (converted to OKLab internally)\n// Output: OKLCH (working space)\n// To get sRGB: lighten(color, 0.25).to.srgb()\n\nvariable input: List = {input};\nvariable color: Color.OKLab = input.get(0).to.oklab();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new lightness (move toward 1)\nvariable current_l: Number = color.l;\nvariable new_l: Number = current_l + (1 - current_l) * amount;\n\n// Clamp to valid range\nif (new_l > 1) [ new_l = 1; ];\n\n// Create output in OKLab, return as OKLCH (working space)\nvariable result: Color.OKLab;\nresult.l = new_l;\nresult.a = color.a;\nresult.b = color.b;\n\nreturn result.to.oklch();"
|
|
2745
|
-
},
|
|
2746
|
-
"requirements": [
|
|
2747
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
|
|
2748
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2749
|
-
],
|
|
2750
|
-
"slug": "lighten"
|
|
2751
|
-
},
|
|
2752
|
-
{
|
|
2753
|
-
"name": "lightness",
|
|
2754
|
-
"type": "function",
|
|
2755
|
-
"description": "Extracts the perceptual lightness value from any color using OKLCH. Returns a value from 0 (black) to 1 (white). This is the 'L' in OKLCH, which is perceptually uniform - equal steps in L appear as equal steps in lightness to human vision. Essential for creating perceptually even color scales.",
|
|
2756
|
-
"keyword": "lightness",
|
|
2757
|
-
"requirements": [
|
|
2758
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2759
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2760
|
-
],
|
|
2761
|
-
"schema": {
|
|
2762
|
-
"type": "object",
|
|
2763
|
-
"properties": {
|
|
2764
|
-
"input": {
|
|
2765
|
-
"type": "array",
|
|
2766
|
-
"items": [
|
|
2767
|
-
{
|
|
2768
|
-
"description": "Color to extract lightness from",
|
|
2769
|
-
"type": "color"
|
|
2770
|
-
}
|
|
2771
|
-
],
|
|
2772
|
-
"minItems": 1,
|
|
2773
|
-
"maxItems": 1
|
|
2774
|
-
}
|
|
2775
|
-
},
|
|
2776
|
-
"required": [
|
|
2777
|
-
"input"
|
|
2778
|
-
]
|
|
2779
|
-
},
|
|
2780
|
-
"returns": {
|
|
2781
|
-
"type": "number",
|
|
2782
|
-
"description": "Perceptual lightness value (0-1)"
|
|
2783
|
-
},
|
|
2784
|
-
"script": {
|
|
2785
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2786
|
-
"script": "// lightness: Extract perceptual lightness from a color\n// Reference: OKLCH Color Space (Björn Ottosson)\n// Reference: https://bottosson.github.io/posts/oklab/\n//\n// Returns the L component from OKLCH, which represents\n// perceptually uniform lightness. Range: 0 (black) to 1 (white).\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\nreturn color.l;"
|
|
2787
|
-
},
|
|
2788
|
-
"examples": [
|
|
2789
|
-
{
|
|
2790
|
-
"description": "White has lightness 1",
|
|
2791
|
-
"input": [
|
|
2792
|
-
"#ffffff"
|
|
2793
|
-
],
|
|
2794
|
-
"output": 1
|
|
2795
|
-
},
|
|
2796
|
-
{
|
|
2797
|
-
"description": "Black has lightness 0",
|
|
2798
|
-
"input": [
|
|
2799
|
-
"#000000"
|
|
2800
|
-
],
|
|
2801
|
-
"output": 0
|
|
2802
|
-
},
|
|
2803
|
-
{
|
|
2804
|
-
"description": "Mid-gray has lightness ~0.6",
|
|
2805
|
-
"input": [
|
|
2806
|
-
"#808080"
|
|
2807
|
-
],
|
|
2808
|
-
"output": 0.6
|
|
2809
|
-
}
|
|
2810
|
-
],
|
|
2811
|
-
"slug": "lightness"
|
|
2812
|
-
},
|
|
2813
|
-
{
|
|
2814
|
-
"name": "Luminance",
|
|
2815
|
-
"type": "function",
|
|
2816
|
-
"description": "Returns the relative luminance of a color (0-1). Uses the WCAG formula based on linear sRGB. Useful for contrast calculations and accessibility.",
|
|
2817
|
-
"keyword": "luminance",
|
|
2818
|
-
"input": {
|
|
2819
|
-
"type": "object",
|
|
2820
|
-
"properties": {
|
|
2821
|
-
"color": {
|
|
2822
|
-
"type": "color",
|
|
2823
|
-
"description": "The color to measure"
|
|
2824
|
-
}
|
|
2825
|
-
}
|
|
2826
|
-
},
|
|
2827
|
-
"script": {
|
|
2828
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2829
|
-
"script": "// Get relative luminance of a color\n// Per WCAG 2.1: L = 0.2126*R + 0.7152*G + 0.0722*B (linear RGB)\n//\n// Returns: 0 (black) to 1 (white)\n// Used for contrast calculations\n\nvariable input: List = {input};\nvariable color: Color.LinearSRGB = input.get(0).to.linearsrgb();\n\nvariable lum: Number = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;\n\nreturn lum;"
|
|
2830
|
-
},
|
|
2831
|
-
"requirements": [
|
|
2832
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/"
|
|
2833
|
-
],
|
|
2834
|
-
"slug": "luminance"
|
|
2835
|
-
},
|
|
2836
|
-
{
|
|
2837
|
-
"name": "Meets Contrast",
|
|
2838
|
-
"type": "function",
|
|
2839
|
-
"description": "Checks if two colors meet WCAG 2.1 contrast requirements. Returns true if contrast ratio meets the specified level: 'AA' (4.5:1 for normal text, 3:1 for large text), 'AAA' (7:1 for normal text, 4.5:1 for large text), or a custom ratio.",
|
|
2840
|
-
"keyword": "meets_contrast",
|
|
2841
|
-
"input": {
|
|
2842
|
-
"type": "object",
|
|
2843
|
-
"properties": {
|
|
2844
|
-
"color1": {
|
|
2845
|
-
"type": "color",
|
|
2846
|
-
"description": "First color"
|
|
2847
|
-
},
|
|
2848
|
-
"color2": {
|
|
2849
|
-
"type": "color",
|
|
2850
|
-
"description": "Second color"
|
|
2851
|
-
},
|
|
2852
|
-
"threshold": {
|
|
2853
|
-
"type": "number",
|
|
2854
|
-
"description": "Minimum contrast ratio required (default: 4.5 for AA)"
|
|
2855
|
-
}
|
|
2856
|
-
}
|
|
2857
|
-
},
|
|
2858
|
-
"script": {
|
|
2859
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2860
|
-
"script": "// Meets Contrast (WCAG 2.1 Compliance Check)\n// Reference: WCAG 2.1 Success Criteria 1.4.3 and 1.4.6\n// Reference: https://www.w3.org/TR/WCAG21/#contrast-minimum\n//\n// WCAG 2.1 Contrast Levels:\n// - Level AA (normal text): 4.5:1\n// - Level AA (large text): 3:1\n// - Level AAA (normal text): 7:1\n// - Level AAA (large text): 4.5:1\n//\n// Large text is defined as:\n// - 14pt (18.66px) bold\n// - 18pt (24px) regular\n//\n// Returns true if the contrast ratio meets or exceeds the threshold.\n\nvariable input: List = {input};\nvariable color1: Color.LinearSRGB = input.get(0).to.linearsrgb();\nvariable color2: Color.LinearSRGB = input.get(1).to.linearsrgb();\n\n// Default threshold is 4.5 (WCAG AA for normal text)\nvariable threshold: Number = 4.5;\nif (input.length() > 2) [\n threshold = input.get(2);\n];\n\n// Calculate relative luminance for each color\n// Formula: L = 0.2126 * R + 0.7152 * G + 0.0722 * B\nvariable lum1: Number = 0.2126 * color1.r + 0.7152 * color1.g + 0.0722 * color1.b;\nvariable lum2: Number = 0.2126 * color2.r + 0.7152 * color2.g + 0.0722 * color2.b;\n\n// Ensure non-negative luminance\nif (lum1 < 0) [ lum1 = 0; ];\nif (lum2 < 0) [ lum2 = 0; ];\n\n// Determine lighter and darker luminance\nvariable lighter: Number = lum1;\nvariable darker: Number = lum2;\nif (lum2 > lum1) [\n lighter = lum2;\n darker = lum1;\n];\n\n// Calculate contrast ratio: (L1 + 0.05) / (L2 + 0.05)\nvariable ratio: Number = (lighter + 0.05) / (darker + 0.05);\n\n// Return whether it meets the threshold\nreturn ratio >= threshold;"
|
|
2861
|
-
},
|
|
2862
|
-
"requirements": [
|
|
2863
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/"
|
|
2864
|
-
],
|
|
2865
|
-
"slug": "meets_contrast"
|
|
2866
|
-
},
|
|
2867
|
-
{
|
|
2868
|
-
"name": "Mix",
|
|
2869
|
-
"type": "function",
|
|
2870
|
-
"description": "Blends two colors together in OKLCH space. Amount 0 = first color, 1 = second color, 0.5 = equal blend.",
|
|
2871
|
-
"keyword": "mix",
|
|
2872
|
-
"input": {
|
|
2873
|
-
"type": "object",
|
|
2874
|
-
"properties": {
|
|
2875
|
-
"color1": {
|
|
2876
|
-
"type": "color",
|
|
2877
|
-
"description": "First color"
|
|
2878
|
-
},
|
|
2879
|
-
"color2": {
|
|
2880
|
-
"type": "color",
|
|
2881
|
-
"description": "Second color"
|
|
2882
|
-
},
|
|
2883
|
-
"amount": {
|
|
2884
|
-
"type": "number",
|
|
2885
|
-
"description": "Blend amount (0-1). Default is 0.5"
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
},
|
|
2889
|
-
"script": {
|
|
2890
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2891
|
-
"script": "// mix: Blend two colors in perceptual space\n// Uses shortest hue path for natural color blending.\n//\n// Algorithm: Linear interpolation on L, C, and shortest-path H\n//\n// Input: Any color space (converted to OKLCH internally)\n// Output: OKLCH (working space)\n// To get sRGB: mix(color1, color2, 0.5).to.srgb()\n\nvariable input: List = {input};\nvariable color1: Color.OKLCH = input.get(0).to.oklch();\nvariable color2: Color.OKLCH = input.get(1).to.oklch();\n\n// Default amount is 0.5 (50/50 blend)\nvariable amount: Number = 0.5;\nif (input.length() > 2) [\n amount = input.get(2);\n];\n\n// Interpolate lightness and chroma linearly\nvariable new_l: Number = color1.l + (color2.l - color1.l) * amount;\nvariable new_c: Number = color1.c + (color2.c - color1.c) * amount;\n\n// Interpolate hue using shortest path\nvariable h1: Number = color1.h;\nvariable h2: Number = color2.h;\nvariable h_diff: Number = h2 - h1;\n\n// Normalize to shortest path (-180 to 180)\nif (h_diff > 180) [ h_diff = h_diff - 360; ];\nif (h_diff < -180) [ h_diff = h_diff + 360; ];\n\nvariable new_h: Number = h1 + h_diff * amount;\nif (new_h < 0) [ new_h = new_h + 360; ];\nif (new_h >= 360) [ new_h = new_h - 360; ];\n\n// Create mixed color\nvariable result: Color.OKLCH;\nresult.l = new_l;\nresult.c = new_c;\nresult.h = new_h;\n\nreturn result;"
|
|
2892
|
-
},
|
|
2893
|
-
"requirements": [
|
|
2894
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
2895
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2896
|
-
],
|
|
2897
|
-
"slug": "mix"
|
|
2898
|
-
},
|
|
2899
|
-
{
|
|
2900
|
-
"name": "Monochromatic",
|
|
2901
|
-
"type": "function",
|
|
2902
|
-
"description": "Generates a monochromatic color palette - variations of a single hue with different lightness and chroma. Useful for creating cohesive single-color schemes. Preserves hue, varies lightness from light to dark.",
|
|
2903
|
-
"keyword": "monochromatic",
|
|
2904
|
-
"input": {
|
|
2905
|
-
"type": "object",
|
|
2906
|
-
"properties": {
|
|
2907
|
-
"color": {
|
|
2908
|
-
"type": "color",
|
|
2909
|
-
"description": "Base color"
|
|
2910
|
-
},
|
|
2911
|
-
"count": {
|
|
2912
|
-
"type": "number",
|
|
2913
|
-
"description": "Number of colors to generate (default: 5)"
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
},
|
|
2917
|
-
"script": {
|
|
2918
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2919
|
-
"script": "// Monochromatic Color Palette\n// Generates variations of a single hue with different lightness\n//\n// A monochromatic scheme uses:\n// - Same hue throughout\n// - Varying lightness (from light tints to dark shades)\n// - Varying chroma (reduced at extremes for natural appearance)\n//\n// This creates harmonious, cohesive palettes ideal for:\n// - UI background/foreground variations\n// - Data visualization with single-variable emphasis\n// - Brand color systems\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\n// Default count is 5\nvariable count: Number = 5;\nif (input.length() > 1) [\n count = input.get(1);\n];\n\n// Preserve the hue\nvariable base_h: Number = base.h;\nvariable base_c: Number = base.c;\n\n// Define lightness range (from light to dark)\nvariable light_start: Number = 0.9;\nvariable light_end: Number = 0.3;\n\nvariable result: List;\nvariable i: Number = 0;\nvariable t: Number = 0;\nvariable step_l: Number = 0;\nvariable l_factor: Number = 0;\nvariable step_c: Number = 0;\nvariable color: Color.OKLCH;\n\nwhile (i < count) [\n // Calculate interpolation factor\n if (count > 1) [\n t = i / (count - 1);\n ] else [\n t = 0.5;\n ];\n \n // Interpolate lightness\n step_l = light_start + (light_end - light_start) * t;\n \n // Scale chroma - reduce at extremes for natural appearance\n // Peak chroma around middle lightness (0.6)\n l_factor = step_l - 0.6;\n if (l_factor < 0) [ l_factor = 0 - l_factor; ];\n l_factor = 1 - l_factor * 1.5;\n if (l_factor < 0.3) [ l_factor = 0.3; ];\n if (l_factor > 1) [ l_factor = 1; ];\n \n step_c = base_c * l_factor;\n \n // Clamp chroma to avoid out-of-gamut\n if (step_c > 0.35) [ step_c = 0.35; ];\n \n color.l = step_l;\n color.c = step_c;\n color.h = base_h;\n \n result = result, color.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
2920
|
-
},
|
|
2921
|
-
"requirements": [
|
|
2922
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
2923
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
2924
|
-
],
|
|
2925
|
-
"slug": "monochromatic"
|
|
2926
|
-
},
|
|
2927
|
-
{
|
|
2928
|
-
"name": "muted",
|
|
2929
|
-
"type": "function",
|
|
2930
|
-
"description": "Decreases a color's chroma toward neutral (gray). Preserves lightness and hue while reducing colorfulness. The optional amount parameter controls the intensity (0-1, default 0.5). Uses OKLCH for perceptually uniform chroma adjustment. Useful for creating subtle backgrounds, disabled states, or calming variations of vibrant colors.",
|
|
2931
|
-
"keyword": "muted",
|
|
2932
|
-
"requirements": [
|
|
2933
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2934
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2935
|
-
],
|
|
2936
|
-
"schema": {
|
|
2937
|
-
"type": "object",
|
|
2938
|
-
"properties": {
|
|
2939
|
-
"input": {
|
|
2940
|
-
"type": "array",
|
|
2941
|
-
"items": [
|
|
2942
|
-
{
|
|
2943
|
-
"description": "Color to mute",
|
|
2944
|
-
"type": "color"
|
|
2945
|
-
},
|
|
2946
|
-
{
|
|
2947
|
-
"description": "Amount (0-1, default 0.5). Higher = more muted.",
|
|
2948
|
-
"type": "number"
|
|
2949
|
-
}
|
|
2950
|
-
],
|
|
2951
|
-
"minItems": 1,
|
|
2952
|
-
"maxItems": 2
|
|
2953
|
-
}
|
|
2954
|
-
},
|
|
2955
|
-
"required": [
|
|
2956
|
-
"input"
|
|
2957
|
-
]
|
|
2958
|
-
},
|
|
2959
|
-
"returns": {
|
|
2960
|
-
"type": "color",
|
|
2961
|
-
"description": "More muted version of the color"
|
|
2962
|
-
},
|
|
2963
|
-
"script": {
|
|
2964
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
2965
|
-
"script": "// muted: Decrease chroma toward neutral (gray)\n// Preserves lightness and hue\n// Amount controls how much to reduce (0-1, 1 = fully gray)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default amount\nvariable amount: Number = 0.5;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Move chroma toward 0 by the specified amount\nvariable current_c: Number = color.c;\nvariable new_c: Number = current_c * (1 - amount);\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
2966
|
-
},
|
|
2967
|
-
"examples": [
|
|
2968
|
-
{
|
|
2969
|
-
"description": "Mute vibrant red",
|
|
2970
|
-
"input": [
|
|
2971
|
-
"#ff0000"
|
|
2972
|
-
],
|
|
2973
|
-
"output": "Desaturated red"
|
|
2974
|
-
},
|
|
2975
|
-
{
|
|
2976
|
-
"description": "Fully muted becomes gray",
|
|
2977
|
-
"input": [
|
|
2978
|
-
"#ff0000",
|
|
2979
|
-
1
|
|
2980
|
-
],
|
|
2981
|
-
"output": "Gray with same lightness as red"
|
|
2982
|
-
}
|
|
2983
|
-
],
|
|
2984
|
-
"slug": "muted"
|
|
2985
|
-
},
|
|
2986
|
-
{
|
|
2987
|
-
"name": "neutral_variant",
|
|
2988
|
-
"type": "function",
|
|
2989
|
-
"description": "Creates a neutral (gray) variant of a color while preserving its lightness. Optionally retains a hint of the original hue. Useful for creating subtle backgrounds, disabled states, or neutral UI elements that relate to a brand color. Uses OKLCH for accurate lightness preservation.",
|
|
2990
|
-
"keyword": "neutral_variant",
|
|
2991
|
-
"requirements": [
|
|
2992
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
2993
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
2994
|
-
],
|
|
2995
|
-
"schema": {
|
|
2996
|
-
"type": "object",
|
|
2997
|
-
"properties": {
|
|
2998
|
-
"input": {
|
|
2999
|
-
"type": "array",
|
|
3000
|
-
"items": [
|
|
3001
|
-
{
|
|
3002
|
-
"description": "Source color",
|
|
3003
|
-
"type": "color"
|
|
3004
|
-
},
|
|
3005
|
-
{
|
|
3006
|
-
"description": "Optional chroma retention (0-1, default 0 for pure gray)",
|
|
3007
|
-
"type": "number"
|
|
3008
|
-
}
|
|
3009
|
-
],
|
|
3010
|
-
"minItems": 1,
|
|
3011
|
-
"maxItems": 2
|
|
3012
|
-
}
|
|
3013
|
-
},
|
|
3014
|
-
"required": [
|
|
3015
|
-
"input"
|
|
3016
|
-
]
|
|
3017
|
-
},
|
|
3018
|
-
"returns": {
|
|
3019
|
-
"type": "color",
|
|
3020
|
-
"description": "Neutral variant with same lightness"
|
|
3021
|
-
},
|
|
3022
|
-
"script": {
|
|
3023
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3024
|
-
"script": "// neutral_variant: Create gray version of color preserving lightness\n// Optionally retain some chroma for subtle tinting\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default to pure gray (0 chroma retention)\nvariable retention: Number = 0;\nif (input.length() > 1) [\n retention = input.get(1);\n];\n\n// Calculate retained chroma\nvariable new_c: Number = color.c * retention;\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h; // Hue preserved for subtle tinting\n\nreturn result;"
|
|
3025
|
-
},
|
|
3026
|
-
"examples": [
|
|
3027
|
-
{
|
|
3028
|
-
"description": "Pure gray from blue",
|
|
3029
|
-
"input": [
|
|
3030
|
-
"#0066cc"
|
|
3031
|
-
],
|
|
3032
|
-
"output": "Gray with same lightness as blue"
|
|
3033
|
-
},
|
|
3034
|
-
{
|
|
3035
|
-
"description": "Slightly tinted gray",
|
|
3036
|
-
"input": [
|
|
3037
|
-
"#ff0000",
|
|
3038
|
-
0.02
|
|
3039
|
-
],
|
|
3040
|
-
"output": "Very subtle warm gray"
|
|
3041
|
-
}
|
|
3042
|
-
],
|
|
3043
|
-
"slug": "neutral_variant"
|
|
3044
|
-
},
|
|
3045
|
-
{
|
|
3046
|
-
"name": "relative_luminance",
|
|
3047
|
-
"type": "function",
|
|
3048
|
-
"description": "Returns the WCAG 2.1 relative luminance of a color. This is the Y value from the XYZ-D65 color space, ranging from 0 (black) to 1 (white). Used in contrast ratio calculations. Different from perceptual lightness - relative luminance is weighted toward green (which human eyes are most sensitive to). Essential for WCAG accessibility calculations.",
|
|
3049
|
-
"keyword": "relative_luminance",
|
|
3050
|
-
"requirements": [
|
|
3051
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3052
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/"
|
|
3053
|
-
],
|
|
3054
|
-
"schema": {
|
|
3055
|
-
"type": "object",
|
|
3056
|
-
"properties": {
|
|
3057
|
-
"input": {
|
|
3058
|
-
"type": "array",
|
|
3059
|
-
"items": [
|
|
3060
|
-
{
|
|
3061
|
-
"description": "Color to measure",
|
|
3062
|
-
"type": "color"
|
|
3063
|
-
}
|
|
3064
|
-
],
|
|
3065
|
-
"minItems": 1,
|
|
3066
|
-
"maxItems": 1
|
|
3067
|
-
}
|
|
3068
|
-
},
|
|
3069
|
-
"required": [
|
|
3070
|
-
"input"
|
|
3071
|
-
]
|
|
3072
|
-
},
|
|
3073
|
-
"returns": {
|
|
3074
|
-
"type": "number",
|
|
3075
|
-
"description": "Relative luminance (0-1)"
|
|
3076
|
-
},
|
|
3077
|
-
"script": {
|
|
3078
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3079
|
-
"script": "// relative_luminance: Get WCAG 2.1 relative luminance\n// Reference: WCAG 2.1 Definition of Relative Luminance\n// Reference: https://www.w3.org/TR/WCAG21/#dfn-relative-luminance\n//\n// Returns the Y component from CIE XYZ D65, which is the\n// relative luminance as defined by WCAG. Range is [0, 1]\n// where 0 = black and 1 = reference white.\n//\n// This value is used in WCAG contrast ratio calculations.\n\nvariable input: List = {input};\nvariable color: Color.XYZD65 = input.get(0).to.xyzd65();\n\nreturn color.y;"
|
|
3080
|
-
},
|
|
3081
|
-
"examples": [
|
|
3082
|
-
{
|
|
3083
|
-
"description": "White has luminance 1",
|
|
3084
|
-
"input": [
|
|
3085
|
-
"#ffffff"
|
|
3086
|
-
],
|
|
3087
|
-
"output": 1
|
|
3088
|
-
},
|
|
3089
|
-
{
|
|
3090
|
-
"description": "Black has luminance 0",
|
|
3091
|
-
"input": [
|
|
3092
|
-
"#000000"
|
|
3093
|
-
],
|
|
3094
|
-
"output": 0
|
|
3095
|
-
},
|
|
3096
|
-
{
|
|
3097
|
-
"description": "Pure green is brighter than pure blue",
|
|
3098
|
-
"input": [
|
|
3099
|
-
"#00ff00"
|
|
3100
|
-
],
|
|
3101
|
-
"output": 0.7152
|
|
3102
|
-
}
|
|
3103
|
-
],
|
|
3104
|
-
"slug": "relative_luminance"
|
|
3105
|
-
},
|
|
3106
|
-
{
|
|
3107
|
-
"name": "Rotate Hue",
|
|
3108
|
-
"type": "function",
|
|
3109
|
-
"description": "Rotates a color's hue by a specified number of degrees in OKLCH space. Positive values rotate clockwise, negative counterclockwise.",
|
|
3110
|
-
"keyword": "rotate_hue",
|
|
3111
|
-
"input": {
|
|
3112
|
-
"type": "object",
|
|
3113
|
-
"properties": {
|
|
3114
|
-
"color": {
|
|
3115
|
-
"type": "color",
|
|
3116
|
-
"description": "The color to rotate"
|
|
3117
|
-
},
|
|
3118
|
-
"degrees": {
|
|
3119
|
-
"type": "number",
|
|
3120
|
-
"description": "Degrees to rotate (-360 to 360)"
|
|
3121
|
-
}
|
|
3122
|
-
}
|
|
3123
|
-
},
|
|
3124
|
-
"script": {
|
|
3125
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3126
|
-
"script": "// Rotate a color's hue by specified degrees\n// Preserves lightness and chroma\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable degrees: Number = input.get(1);\n\nvariable new_h: Number = color.h + degrees;\n\n// Normalize to 0-360\nwhile (new_h >= 360) [ new_h = new_h - 360; ];\nwhile (new_h < 0) [ new_h = new_h + 360; ];\n\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = new_h;\n\nreturn result;"
|
|
3127
|
-
},
|
|
3128
|
-
"requirements": [
|
|
3129
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3130
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3131
|
-
],
|
|
3132
|
-
"slug": "rotate_hue"
|
|
3133
|
-
},
|
|
3134
|
-
{
|
|
3135
|
-
"name": "Saturate",
|
|
3136
|
-
"type": "function",
|
|
3137
|
-
"description": "Increases color saturation by boosting chroma in OKLCH space. Amount is 0-1 where 0.25 = 25% more saturated.",
|
|
3138
|
-
"keyword": "saturate",
|
|
3139
|
-
"input": {
|
|
3140
|
-
"type": "object",
|
|
3141
|
-
"properties": {
|
|
3142
|
-
"color": {
|
|
3143
|
-
"type": "color",
|
|
3144
|
-
"description": "The color to saturate"
|
|
3145
|
-
},
|
|
3146
|
-
"amount": {
|
|
3147
|
-
"type": "number",
|
|
3148
|
-
"description": "Amount to saturate (0-1). Default is 0.25"
|
|
3149
|
-
}
|
|
3150
|
-
}
|
|
3151
|
-
},
|
|
3152
|
-
"script": {
|
|
3153
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3154
|
-
"script": "// Saturate a color by increasing its OKLCH chroma\n// Amount: 0-1 where 0.25 means 25% increase\n//\n// Algorithm: C' = C * (1 + amount)\n// Chroma has no strict upper bound but ~0.4 is practical max for sRGB\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new chroma\nvariable current_c: Number = color.c;\nvariable new_c: Number = current_c * (1 + amount);\n\n// Soft clamp at 0.4 (approximate sRGB gamut max)\nif (new_c > 0.4) [ new_c = 0.4; ];\n\n// Create output in OKLCH, return as sRGB\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
3155
|
-
},
|
|
3156
|
-
"requirements": [
|
|
3157
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3158
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3159
|
-
],
|
|
3160
|
-
"slug": "saturate"
|
|
3161
|
-
},
|
|
3162
|
-
{
|
|
3163
|
-
"name": "scale_chroma",
|
|
3164
|
-
"type": "function",
|
|
3165
|
-
"description": "Scales a color's chroma (saturation) by a factor. Unlike set_chroma which sets an absolute value, scale_chroma multiplies the current chroma. A factor of 1.5 increases colorfulness by 50%, while 0.5 decreases it by 50%. Uses OKLCH for perceptually uniform scaling. Useful for creating more or less vivid versions of a color.",
|
|
3166
|
-
"keyword": "scale_chroma",
|
|
3167
|
-
"requirements": [
|
|
3168
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3169
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
3170
|
-
],
|
|
3171
|
-
"schema": {
|
|
3172
|
-
"type": "object",
|
|
3173
|
-
"properties": {
|
|
3174
|
-
"input": {
|
|
3175
|
-
"type": "array",
|
|
3176
|
-
"items": [
|
|
3177
|
-
{
|
|
3178
|
-
"description": "Color to scale",
|
|
3179
|
-
"type": "color"
|
|
3180
|
-
},
|
|
3181
|
-
{
|
|
3182
|
-
"description": "Scale factor (e.g., 1.5 for +50%, 0.5 for -50%)",
|
|
3183
|
-
"type": "number"
|
|
3184
|
-
}
|
|
3185
|
-
],
|
|
3186
|
-
"minItems": 2,
|
|
3187
|
-
"maxItems": 2
|
|
3188
|
-
}
|
|
3189
|
-
},
|
|
3190
|
-
"required": [
|
|
3191
|
-
"input"
|
|
3192
|
-
]
|
|
3193
|
-
},
|
|
3194
|
-
"returns": {
|
|
3195
|
-
"type": "color",
|
|
3196
|
-
"description": "Color with scaled chroma"
|
|
3197
|
-
},
|
|
3198
|
-
"script": {
|
|
3199
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3200
|
-
"script": "// scale_chroma: Multiply chroma by a factor\n// Uses OKLCH for perceptually uniform scaling\n// Chroma cannot be negative; clamps to 0\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable factor: Number = input.get(1);\n\n// Scale chroma\nvariable new_c: Number = color.c * factor;\n\n// Clamp to valid range (no negative chroma)\nif (new_c < 0) [ new_c = 0; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
3201
|
-
},
|
|
3202
|
-
"examples": [
|
|
3203
|
-
{
|
|
3204
|
-
"description": "Make color more vivid",
|
|
3205
|
-
"input": [
|
|
3206
|
-
"#cc8080",
|
|
3207
|
-
1.5
|
|
3208
|
-
],
|
|
3209
|
-
"output": "More saturated red"
|
|
3210
|
-
},
|
|
3211
|
-
{
|
|
3212
|
-
"description": "Desaturate color",
|
|
3213
|
-
"input": [
|
|
3214
|
-
"#ff0000",
|
|
3215
|
-
0.5
|
|
3216
|
-
],
|
|
3217
|
-
"output": "Muted red"
|
|
3218
|
-
}
|
|
3219
|
-
],
|
|
3220
|
-
"slug": "scale_chroma"
|
|
3221
|
-
},
|
|
3222
|
-
{
|
|
3223
|
-
"name": "scale_lightness",
|
|
3224
|
-
"type": "function",
|
|
3225
|
-
"description": "Scales a color's lightness by a factor. Unlike set_lightness which sets an absolute value, scale_lightness multiplies the current lightness. A factor of 1.2 increases lightness by 20%, while 0.8 decreases it by 20%. Uses OKLCH for perceptually uniform scaling. Result is clamped to valid lightness range [0, 1].",
|
|
3226
|
-
"keyword": "scale_lightness",
|
|
3227
|
-
"requirements": [
|
|
3228
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3229
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
3230
|
-
],
|
|
3231
|
-
"schema": {
|
|
3232
|
-
"type": "object",
|
|
3233
|
-
"properties": {
|
|
3234
|
-
"input": {
|
|
3235
|
-
"type": "array",
|
|
3236
|
-
"items": [
|
|
3237
|
-
{
|
|
3238
|
-
"description": "Color to scale",
|
|
3239
|
-
"type": "color"
|
|
3240
|
-
},
|
|
3241
|
-
{
|
|
3242
|
-
"description": "Scale factor (e.g., 1.2 for +20%, 0.8 for -20%)",
|
|
3243
|
-
"type": "number"
|
|
3244
|
-
}
|
|
3245
|
-
],
|
|
3246
|
-
"minItems": 2,
|
|
3247
|
-
"maxItems": 2
|
|
3248
|
-
}
|
|
3249
|
-
},
|
|
3250
|
-
"required": [
|
|
3251
|
-
"input"
|
|
3252
|
-
]
|
|
3253
|
-
},
|
|
3254
|
-
"returns": {
|
|
3255
|
-
"type": "color",
|
|
3256
|
-
"description": "Color with scaled lightness"
|
|
3257
|
-
},
|
|
3258
|
-
"script": {
|
|
3259
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3260
|
-
"script": "// scale_lightness: Multiply lightness by a factor\n// Uses OKLCH for perceptually uniform scaling\n// Clamps result to valid range [0, 1]\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable factor: Number = input.get(1);\n\n// Scale lightness\nvariable new_l: Number = color.l * factor;\n\n// Clamp to valid range\nif (new_l < 0) [ new_l = 0; ];\nif (new_l > 1) [ new_l = 1; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = new_l;\nresult.c = color.c;\nresult.h = color.h;\n\nreturn result;"
|
|
3261
|
-
},
|
|
3262
|
-
"examples": [
|
|
3263
|
-
{
|
|
3264
|
-
"description": "Brighten color by 20%",
|
|
3265
|
-
"input": [
|
|
3266
|
-
"#808080",
|
|
3267
|
-
1.2
|
|
3268
|
-
],
|
|
3269
|
-
"output": "Lighter gray"
|
|
3270
|
-
},
|
|
3271
|
-
{
|
|
3272
|
-
"description": "Dim color by 50%",
|
|
3273
|
-
"input": [
|
|
3274
|
-
"#ffffff",
|
|
3275
|
-
0.5
|
|
3276
|
-
],
|
|
3277
|
-
"output": "Mid-gray"
|
|
3278
|
-
}
|
|
3279
|
-
],
|
|
3280
|
-
"slug": "scale_lightness"
|
|
3281
|
-
},
|
|
3282
|
-
{
|
|
3283
|
-
"name": "sepia",
|
|
3284
|
-
"type": "function",
|
|
3285
|
-
"description": "Applies a sepia tone to a color, simulating the warm brownish tint of aged photographs. Uses the standard sepia matrix transformation. Amount parameter controls intensity from 0 (no effect) to 1 (full sepia).",
|
|
3286
|
-
"keyword": "sepia",
|
|
3287
|
-
"requirements": [
|
|
3288
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3289
|
-
],
|
|
3290
|
-
"schema": {
|
|
3291
|
-
"type": "object",
|
|
3292
|
-
"properties": {
|
|
3293
|
-
"input": {
|
|
3294
|
-
"type": "array",
|
|
3295
|
-
"items": [
|
|
3296
|
-
{
|
|
3297
|
-
"description": "Color to transform",
|
|
3298
|
-
"type": "color"
|
|
3299
|
-
},
|
|
3300
|
-
{
|
|
3301
|
-
"description": "Sepia intensity (0-1), default 1",
|
|
3302
|
-
"type": "number"
|
|
3303
|
-
}
|
|
3304
|
-
],
|
|
3305
|
-
"minItems": 1,
|
|
3306
|
-
"maxItems": 2
|
|
3307
|
-
}
|
|
3308
|
-
},
|
|
3309
|
-
"required": [
|
|
3310
|
-
"input"
|
|
3311
|
-
]
|
|
3312
|
-
},
|
|
3313
|
-
"returns": {
|
|
3314
|
-
"type": "color",
|
|
3315
|
-
"description": "Color with sepia tone applied"
|
|
3316
|
-
},
|
|
3317
|
-
"script": {
|
|
3318
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3319
|
-
"script": "// sepia: Apply sepia tone transformation\n// Reference: CSS Filter Effects Module Level 1\n// Reference: https://www.w3.org/TR/filter-effects-1/#sepiaEquivalent\n//\n// Uses the standard sepia matrix transformation as defined in CSS.\n// The amount parameter controls intensity (0 = no effect, 1 = full sepia).\n//\n// Matrix coefficients:\n// R' = 0.393R + 0.769G + 0.189B\n// G' = 0.349R + 0.686G + 0.168B\n// B' = 0.272R + 0.534G + 0.131B\n\nvariable input: List = {input};\nvariable color: Color.SRGB = input.get(0).to.srgb();\nvariable amount: Number = 1;\n\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Clamp amount\nif (amount < 0) [ amount = 0; ];\nif (amount > 1) [ amount = 1; ];\n\nvariable r: Number = color.r;\nvariable g: Number = color.g;\nvariable b: Number = color.b;\n\n// Standard sepia matrix transformation (CSS filter spec)\n// R' = 0.393R + 0.769G + 0.189B\n// G' = 0.349R + 0.686G + 0.168B\n// B' = 0.272R + 0.534G + 0.131B\nvariable sepia_r: Number = (r * 0.393) + (g * 0.769) + (b * 0.189);\nvariable sepia_g: Number = (r * 0.349) + (g * 0.686) + (b * 0.168);\nvariable sepia_b: Number = (r * 0.272) + (g * 0.534) + (b * 0.131);\n\n// Clamp sepia values to valid range\nif (sepia_r > 1) [ sepia_r = 1; ];\nif (sepia_g > 1) [ sepia_g = 1; ];\nif (sepia_b > 1) [ sepia_b = 1; ];\n\n// Interpolate between original and sepia based on amount\nvariable new_r: Number = r + (sepia_r - r) * amount;\nvariable new_g: Number = g + (sepia_g - g) * amount;\nvariable new_b: Number = b + (sepia_b - b) * amount;\n\n// Create result\nvariable result: Color.SRGB;\nresult.r = new_r;\nresult.g = new_g;\nresult.b = new_b;\n\nreturn result;"
|
|
3320
|
-
},
|
|
3321
|
-
"examples": [
|
|
3322
|
-
{
|
|
3323
|
-
"description": "Full sepia",
|
|
3324
|
-
"input": [
|
|
3325
|
-
"#808080",
|
|
3326
|
-
1
|
|
3327
|
-
],
|
|
3328
|
-
"output": "Warm brownish gray"
|
|
3329
|
-
},
|
|
3330
|
-
{
|
|
3331
|
-
"description": "Partial sepia",
|
|
3332
|
-
"input": [
|
|
3333
|
-
"#808080",
|
|
3334
|
-
0.5
|
|
3335
|
-
],
|
|
3336
|
-
"output": "Subtly warm gray"
|
|
3337
|
-
}
|
|
3338
|
-
],
|
|
3339
|
-
"slug": "sepia"
|
|
3340
|
-
},
|
|
3341
|
-
{
|
|
3342
|
-
"name": "Set Chroma",
|
|
3343
|
-
"type": "function",
|
|
3344
|
-
"description": "Sets a color's chroma (saturation) to a specific value in OKLCH space. Preserves hue and lightness.",
|
|
3345
|
-
"keyword": "set_chroma",
|
|
3346
|
-
"input": {
|
|
3347
|
-
"type": "object",
|
|
3348
|
-
"properties": {
|
|
3349
|
-
"color": {
|
|
3350
|
-
"type": "color",
|
|
3351
|
-
"description": "The color to modify"
|
|
3352
|
-
},
|
|
3353
|
-
"chroma": {
|
|
3354
|
-
"type": "number",
|
|
3355
|
-
"description": "Target chroma (0-0.4 typical for sRGB)"
|
|
3356
|
-
}
|
|
3357
|
-
}
|
|
3358
|
-
},
|
|
3359
|
-
"script": {
|
|
3360
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3361
|
-
"script": "// Set a color's chroma to a specific value\n// Preserves hue and lightness\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable target_c: Number = input.get(1);\n\n// Clamp to valid range (0 to ~0.4 for sRGB gamut)\nif (target_c < 0) [ target_c = 0; ];\nif (target_c > 0.4) [ target_c = 0.4; ];\n\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = target_c;\nresult.h = color.h;\n\nreturn result;"
|
|
3362
|
-
},
|
|
3363
|
-
"requirements": [
|
|
3364
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3365
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3366
|
-
],
|
|
3367
|
-
"slug": "set_chroma"
|
|
3368
|
-
},
|
|
3369
|
-
{
|
|
3370
|
-
"name": "Set Hue",
|
|
3371
|
-
"type": "function",
|
|
3372
|
-
"description": "Sets a color's hue to a specific angle in OKLCH space. Preserves lightness and chroma.",
|
|
3373
|
-
"keyword": "set_hue",
|
|
3374
|
-
"input": {
|
|
3375
|
-
"type": "object",
|
|
3376
|
-
"properties": {
|
|
3377
|
-
"color": {
|
|
3378
|
-
"type": "color",
|
|
3379
|
-
"description": "The color to modify"
|
|
3380
|
-
},
|
|
3381
|
-
"hue": {
|
|
3382
|
-
"type": "number",
|
|
3383
|
-
"description": "Target hue angle (0-360)"
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
3386
|
-
},
|
|
3387
|
-
"script": {
|
|
3388
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3389
|
-
"script": "// Set a color's hue to a specific angle\n// Preserves lightness and chroma\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable target_h: Number = input.get(1);\n\n// Normalize to 0-360\nwhile (target_h >= 360) [ target_h = target_h - 360; ];\nwhile (target_h < 0) [ target_h = target_h + 360; ];\n\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = target_h;\n\nreturn result;"
|
|
3390
|
-
},
|
|
3391
|
-
"requirements": [
|
|
3392
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3393
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3394
|
-
],
|
|
3395
|
-
"slug": "set_hue"
|
|
3396
|
-
},
|
|
3397
|
-
{
|
|
3398
|
-
"name": "Set Lightness",
|
|
3399
|
-
"type": "function",
|
|
3400
|
-
"description": "Sets a color's lightness to a specific value in OKLCH space. Preserves hue and chroma.",
|
|
3401
|
-
"keyword": "set_lightness",
|
|
3402
|
-
"input": {
|
|
3403
|
-
"type": "object",
|
|
3404
|
-
"properties": {
|
|
3405
|
-
"color": {
|
|
3406
|
-
"type": "color",
|
|
3407
|
-
"description": "The color to modify"
|
|
3408
|
-
},
|
|
3409
|
-
"lightness": {
|
|
3410
|
-
"type": "number",
|
|
3411
|
-
"description": "Target lightness (0-1)"
|
|
3412
|
-
}
|
|
3413
|
-
}
|
|
3414
|
-
},
|
|
3415
|
-
"script": {
|
|
3416
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3417
|
-
"script": "// Set a color's lightness to a specific value\n// Preserves hue and chroma\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable target_l: Number = input.get(1);\n\n// Clamp to valid range\nif (target_l < 0) [ target_l = 0; ];\nif (target_l > 1) [ target_l = 1; ];\n\nvariable result: Color.OKLCH;\nresult.l = target_l;\nresult.c = color.c;\nresult.h = color.h;\n\nreturn result;"
|
|
3418
|
-
},
|
|
3419
|
-
"requirements": [
|
|
3420
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3421
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3422
|
-
],
|
|
3423
|
-
"slug": "set_lightness"
|
|
3424
|
-
},
|
|
3425
|
-
{
|
|
3426
|
-
"name": "Shade Scale",
|
|
3427
|
-
"type": "function",
|
|
3428
|
-
"description": "Generates a Tailwind/Material-style shade scale from 50 (lightest) to 900 (darkest). The input color becomes the 500 (middle) value. Perfect for design system color tokens.",
|
|
3429
|
-
"keyword": "shade_scale",
|
|
3430
|
-
"input": {
|
|
3431
|
-
"type": "object",
|
|
3432
|
-
"properties": {
|
|
3433
|
-
"color": {
|
|
3434
|
-
"type": "color",
|
|
3435
|
-
"description": "Base color (becomes the 500 shade)"
|
|
3436
|
-
},
|
|
3437
|
-
"count": {
|
|
3438
|
-
"type": "number",
|
|
3439
|
-
"description": "Number of shades. Default is 10 (50-900)"
|
|
3440
|
-
}
|
|
3441
|
-
}
|
|
3442
|
-
},
|
|
3443
|
-
"script": {
|
|
3444
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3445
|
-
"script": "// Generate a design system shade scale (like Tailwind 50-900)\n// Input color becomes the middle (500) shade\n//\n// Lightness distribution:\n// 50: ~0.97 (very light)\n// 100: ~0.93\n// 200: ~0.87\n// 300: ~0.78\n// 400: ~0.67\n// 500: input lightness (base)\n// 600: ~0.52\n// 700: ~0.42\n// 800: ~0.32\n// 900: ~0.22 (very dark)\n//\n// Chroma is scaled to avoid washed-out lights and muddy darks\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\nvariable count: Number = 10;\nif (input.length() > 1) [\n count = input.get(1);\n];\n\n// Target lightness values for standard 10-step scale\n// These are perceptually optimized for OKLCH\nvariable lightness_targets: List = 0.97, 0.93, 0.87, 0.78, 0.67, 0.55, 0.45, 0.35, 0.27, 0.20;\n\nvariable base_h: Number = base.h;\nvariable base_c: Number = base.c;\n\nvariable result: List;\nvariable i: Number = 0;\nvariable target_l: Number = 0;\nvariable l_factor: Number = 0;\nvariable scaled_c: Number = 0;\nvariable shade: Color.OKLCH;\n\nwhile (i < count) [\n target_l = lightness_targets.get(i);\n \n // Scale chroma based on lightness\n // Chroma peaks in midtones, reduces at extremes\n l_factor = 1 - abs(target_l - 0.55) * 1.2;\n if (l_factor < 0.3) [ l_factor = 0.3; ];\n if (l_factor > 1.2) [ l_factor = 1.2; ];\n \n scaled_c = base_c * l_factor;\n if (scaled_c > 0.4) [ scaled_c = 0.4; ];\n \n shade.l = target_l;\n shade.c = scaled_c;\n shade.h = base_h;\n \n result = result, shade.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
3446
|
-
},
|
|
3447
|
-
"requirements": [
|
|
3448
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3449
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3450
|
-
],
|
|
3451
|
-
"slug": "shade_scale"
|
|
3452
|
-
},
|
|
3453
|
-
{
|
|
3454
|
-
"name": "Split Complement",
|
|
3455
|
-
"type": "function",
|
|
3456
|
-
"description": "Generates split-complementary colors - base color plus two colors adjacent to its complement. More nuanced than direct complement, less tension than triadic.",
|
|
3457
|
-
"keyword": "split_complement",
|
|
3458
|
-
"input": {
|
|
3459
|
-
"type": "object",
|
|
3460
|
-
"properties": {
|
|
3461
|
-
"color": {
|
|
3462
|
-
"type": "color",
|
|
3463
|
-
"description": "Base color"
|
|
3464
|
-
},
|
|
3465
|
-
"angle": {
|
|
3466
|
-
"type": "number",
|
|
3467
|
-
"description": "Degrees from complement for split colors. Default is 30"
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
|
-
},
|
|
3471
|
-
"script": {
|
|
3472
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3473
|
-
"script": "// Generate split-complementary colors\n// Returns [base, complement-angle, complement+angle]\n//\n// Use case: Balanced contrast with more variety than complement\n// Example: Blue → Orange-Red, Orange-Yellow (instead of just Orange)\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\nvariable angle: Number = 30;\nif (input.length() > 1) [\n angle = input.get(1);\n];\n\nvariable complement_h: Number = base.h + 180;\nif (complement_h >= 360) [ complement_h = complement_h - 360; ];\n\nvariable result: List;\n\n// Base color\nresult = result, base.to.srgb();\n\n// Complement - angle\nvariable h2: Number = complement_h - angle;\nif (h2 < 0) [ h2 = h2 + 360; ];\nvariable c2: Color.OKLCH;\nc2.l = base.l; c2.c = base.c; c2.h = h2;\nresult = result, c2.to.srgb();\n\n// Complement + angle\nvariable h3: Number = complement_h + angle;\nif (h3 >= 360) [ h3 = h3 - 360; ];\nvariable c3: Color.OKLCH;\nc3.l = base.l; c3.c = base.c; c3.h = h3;\nresult = result, c3.to.srgb();\n\nreturn result;"
|
|
3474
|
-
},
|
|
3475
|
-
"requirements": [
|
|
3476
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3477
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3478
|
-
],
|
|
3479
|
-
"slug": "split_complement"
|
|
3480
|
-
},
|
|
3481
|
-
{
|
|
3482
|
-
"name": "Steps",
|
|
3483
|
-
"type": "function",
|
|
3484
|
-
"description": "Generates a list of colors interpolated between two colors in OKLCH space. Perfect for gradient stops.",
|
|
3485
|
-
"keyword": "steps",
|
|
3486
|
-
"input": {
|
|
3487
|
-
"type": "object",
|
|
3488
|
-
"properties": {
|
|
3489
|
-
"color1": {
|
|
3490
|
-
"type": "color",
|
|
3491
|
-
"description": "Start color"
|
|
3492
|
-
},
|
|
3493
|
-
"color2": {
|
|
3494
|
-
"type": "color",
|
|
3495
|
-
"description": "End color"
|
|
3496
|
-
},
|
|
3497
|
-
"count": {
|
|
3498
|
-
"type": "number",
|
|
3499
|
-
"description": "Number of steps (including start and end). Default is 5"
|
|
3500
|
-
}
|
|
3501
|
-
}
|
|
3502
|
-
},
|
|
3503
|
-
"script": {
|
|
3504
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3505
|
-
"script": "// Generate gradient steps between two colors in OKLCH space\n// Returns a List of colors from start to end (inclusive)\n//\n// Uses perceptually uniform interpolation with shortest hue path\n\nvariable input: List = {input};\nvariable color1: Color.OKLCH = input.get(0).to.oklch();\nvariable color2: Color.OKLCH = input.get(1).to.oklch();\n\n// Default count is 5\nvariable count: Number = 5;\nif (input.length() > 2) [\n count = input.get(2);\n];\n\n// Calculate hue difference (shortest path)\nvariable h1: Number = color1.h;\nvariable h2: Number = color2.h;\nvariable h_diff: Number = h2 - h1;\nif (h_diff > 180) [ h_diff = h_diff - 360; ];\nif (h_diff < -180) [ h_diff = h_diff + 360; ];\n\n// Generate steps\nvariable result: List;\nvariable i: Number = 0;\nvariable t: Number = 0;\nvariable step_l: Number = 0;\nvariable step_c: Number = 0;\nvariable step_h: Number = 0;\nvariable step_color: Color.OKLCH;\n\nwhile (i < count) [\n t = i / (count - 1);\n \n // Interpolate each component\n step_l = color1.l + (color2.l - color1.l) * t;\n step_c = color1.c + (color2.c - color1.c) * t;\n step_h = h1 + h_diff * t;\n \n // Normalize hue\n if (step_h < 0) [ step_h = step_h + 360; ];\n if (step_h >= 360) [ step_h = step_h - 360; ];\n \n // Create color and add to list\n step_color.l = step_l;\n step_color.c = step_c;\n step_color.h = step_h;\n \n result = result, step_color.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
3506
|
-
},
|
|
3507
|
-
"requirements": [
|
|
3508
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3509
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3510
|
-
],
|
|
3511
|
-
"slug": "steps"
|
|
3512
|
-
},
|
|
3513
|
-
{
|
|
3514
|
-
"name": "Tetradic",
|
|
3515
|
-
"type": "function",
|
|
3516
|
-
"description": "Generates tetradic (square) colors - four hues equally spaced 90° apart. Creates rich, complex palettes with two complementary pairs.",
|
|
3517
|
-
"keyword": "tetradic",
|
|
3518
|
-
"input": {
|
|
3519
|
-
"type": "object",
|
|
3520
|
-
"properties": {
|
|
3521
|
-
"color": {
|
|
3522
|
-
"type": "color",
|
|
3523
|
-
"description": "Base color"
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
},
|
|
3527
|
-
"script": {
|
|
3528
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3529
|
-
"script": "// Generate tetradic (square) colors (90° apart)\n// Returns [base, base+90°, base+180°, base+270°]\n//\n// Use case: Rich, complex palettes\n// Contains two complementary pairs\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\nvariable result: List;\n\n// Base color (0°)\nresult = result, base.to.srgb();\n\n// +90°\nvariable h2: Number = base.h + 90;\nif (h2 >= 360) [ h2 = h2 - 360; ];\nvariable c2: Color.OKLCH;\nc2.l = base.l; c2.c = base.c; c2.h = h2;\nresult = result, c2.to.srgb();\n\n// +180° (complement)\nvariable h3: Number = base.h + 180;\nif (h3 >= 360) [ h3 = h3 - 360; ];\nvariable c3: Color.OKLCH;\nc3.l = base.l; c3.c = base.c; c3.h = h3;\nresult = result, c3.to.srgb();\n\n// +270°\nvariable h4: Number = base.h + 270;\nif (h4 >= 360) [ h4 = h4 - 360; ];\nvariable c4: Color.OKLCH;\nc4.l = base.l; c4.c = base.c; c4.h = h4;\nresult = result, c4.to.srgb();\n\nreturn result;"
|
|
3530
|
-
},
|
|
3531
|
-
"requirements": [
|
|
3532
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3533
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3534
|
-
],
|
|
3535
|
-
"slug": "tetradic"
|
|
3536
|
-
},
|
|
3537
|
-
{
|
|
3538
|
-
"name": "Tint Scale",
|
|
3539
|
-
"type": "function",
|
|
3540
|
-
"description": "Creates a sequential scale of tints from a base color. Varies lightness while preserving hue and adjusting chroma proportionally. Perfect for sequential/quantitative data visualization.",
|
|
3541
|
-
"keyword": "tint_scale",
|
|
3542
|
-
"input": {
|
|
3543
|
-
"type": "object",
|
|
3544
|
-
"properties": {
|
|
3545
|
-
"color": {
|
|
3546
|
-
"type": "color",
|
|
3547
|
-
"description": "Base color for the scale"
|
|
3548
|
-
},
|
|
3549
|
-
"count": {
|
|
3550
|
-
"type": "number",
|
|
3551
|
-
"description": "Number of tints to generate. Default is 9"
|
|
3552
|
-
},
|
|
3553
|
-
"light_start": {
|
|
3554
|
-
"type": "number",
|
|
3555
|
-
"description": "Lightness of lightest tint (0-1). Default is 0.95"
|
|
3556
|
-
},
|
|
3557
|
-
"light_end": {
|
|
3558
|
-
"type": "number",
|
|
3559
|
-
"description": "Lightness of darkest tint (0-1). Default is 0.25"
|
|
3560
|
-
}
|
|
3561
|
-
}
|
|
3562
|
-
},
|
|
3563
|
-
"script": {
|
|
3564
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3565
|
-
"script": "// Create a sequential tint scale from a base color\n// Varies lightness while preserving hue and scaling chroma\n//\n// Ideal for: choropleth maps, intensity scales, single-hue heatmaps\n// Chroma is scaled proportionally to maintain color harmony\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\n// Default count is 9\nvariable count: Number = 9;\nif (input.length() > 1) [\n count = input.get(1);\n];\n\n// Default light_start is 0.95 (nearly white)\nvariable light_start: Number = 0.95;\nif (input.length() > 2) [\n light_start = input.get(2);\n];\n\n// Default light_end is 0.25 (dark)\nvariable light_end: Number = 0.25;\nif (input.length() > 3) [\n light_end = input.get(3);\n];\n\n// Base color properties\nvariable base_h: Number = base.h;\nvariable base_c: Number = base.c;\nvariable base_l: Number = base.l;\n\n// Calculate chroma scaling factor (chroma decreases at extreme lightness)\nvariable max_c: Number = base_c * 1.2;\n\nvariable result: List;\nvariable i: Number = 0;\nvariable t: Number = 0;\nvariable step_l: Number = 0;\nvariable l_dist: Number = 0;\nvariable chroma_scale: Number = 0;\nvariable step_c: Number = 0;\nvariable color: Color.OKLCH;\n\nwhile (i < count) [\n t = i / (count - 1);\n \n // Interpolate lightness\n step_l = light_start + (light_end - light_start) * t;\n \n // Scale chroma based on lightness distance from optimal (~0.6)\n // Chroma is highest in midtones, lower at extremes\n l_dist = step_l - 0.6;\n if (l_dist < 0) [ l_dist = 0 - l_dist; ];\n chroma_scale = 1 - l_dist * 1.5;\n if (chroma_scale < 0.1) [ chroma_scale = 0.1; ];\n step_c = max_c * chroma_scale;\n \n // Clamp chroma\n if (step_c > 0.4) [ step_c = 0.4; ];\n \n color.l = step_l;\n color.c = step_c;\n color.h = base_h;\n \n result = result, color.to.srgb();\n i = i + 1;\n];\n\nreturn result;"
|
|
3566
|
-
},
|
|
3567
|
-
"requirements": [
|
|
3568
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3569
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3570
|
-
],
|
|
3571
|
-
"slug": "tint_scale"
|
|
3572
|
-
},
|
|
3573
|
-
{
|
|
3574
|
-
"name": "to_gamut",
|
|
3575
|
-
"type": "function",
|
|
3576
|
-
"description": "Maps a color into a target gamut using the CSS Color Level 4 gamut mapping algorithm. This preserves the perceptual lightness and hue while reducing chroma until the color fits within the target color space. Uses OKLCH for perceptually uniform mapping and binary search with Delta E OK for convergence. Essential for wide-gamut to sRGB conversion in design systems.",
|
|
3577
|
-
"keyword": "to_gamut",
|
|
3578
|
-
"requirements": [
|
|
3579
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3580
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
3581
|
-
],
|
|
3582
|
-
"schema": {
|
|
3583
|
-
"type": "object",
|
|
3584
|
-
"properties": {
|
|
3585
|
-
"input": {
|
|
3586
|
-
"type": "array",
|
|
3587
|
-
"items": [
|
|
3588
|
-
{
|
|
3589
|
-
"description": "Color to map into gamut",
|
|
3590
|
-
"type": "color"
|
|
3591
|
-
},
|
|
3592
|
-
{
|
|
3593
|
-
"description": "Target gamut/space (default 'srgb')",
|
|
3594
|
-
"type": "string"
|
|
3595
|
-
}
|
|
3596
|
-
],
|
|
3597
|
-
"minItems": 1,
|
|
3598
|
-
"maxItems": 2
|
|
3599
|
-
}
|
|
3600
|
-
},
|
|
3601
|
-
"required": [
|
|
3602
|
-
"input"
|
|
3603
|
-
]
|
|
3604
|
-
},
|
|
3605
|
-
"returns": {
|
|
3606
|
-
"type": "color",
|
|
3607
|
-
"description": "Color mapped into the target gamut"
|
|
3608
|
-
},
|
|
3609
|
-
"script": {
|
|
3610
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3611
|
-
"script": "// to_gamut: Perceptual gamut mapping (CSS Color Level 4 algorithm)\n// Reference: CSS Color Level 4, Section 13.2 Gamut Mapping\n// Reference: https://www.w3.org/TR/css-color-4/#gamut-mapping\n//\n// Maps out-of-gamut colors into sRGB while preserving perceptual\n// intent. Uses OKLCH space with binary search on chroma:\n// - Lightness and hue are preserved\n// - Chroma is reduced until the color fits in gamut\n//\n// This is the CSS Gamut Mapping Algorithm (GMA) used by browsers.\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Target space (default sRGB) - currently only supports sRGB\nvariable target_space: String = \"srgb\";\nif (input.length() > 1) [\n target_space = input.get(1);\n];\n\n// Constants\nvariable epsilon: Number = 0.0001;\nvariable neg_epsilon: Number = 0 - epsilon;\nvariable one_plus_eps: Number = 1 + epsilon;\n\n// Check if already in gamut by converting and checking bounds\nvariable test_color: Color.SRGB = color.to.srgb();\nvariable in_gamut: Boolean = true;\n\n// Check if sRGB values are in range [0, 1]\nif (test_color.r < neg_epsilon) [ in_gamut = false; ];\nif (test_color.r > one_plus_eps) [ in_gamut = false; ];\nif (test_color.g < neg_epsilon) [ in_gamut = false; ];\nif (test_color.g > one_plus_eps) [ in_gamut = false; ];\nif (test_color.b < neg_epsilon) [ in_gamut = false; ];\nif (test_color.b > one_plus_eps) [ in_gamut = false; ];\n\n// If already in gamut, return clamped version\nif (in_gamut) [\n variable clamped: Color.SRGB;\n clamped.r = min(1, max(0, test_color.r));\n clamped.g = min(1, max(0, test_color.g));\n clamped.b = min(1, max(0, test_color.b));\n return clamped;\n];\n\n// Binary search: reduce chroma until in gamut\nvariable min_c: Number = 0;\nvariable max_c: Number = color.c;\nvariable current: Color.OKLCH;\ncurrent.l = color.l;\ncurrent.h = color.h;\n\nvariable iterations: Number = 0;\nvariable max_iterations: Number = 25;\n\nwhile (max_c - min_c > epsilon) [\n if (iterations >= max_iterations) [\n max_c = min_c; // Force exit\n ];\n \n variable mid_c: Number = (min_c + max_c) / 2;\n current.c = mid_c;\n \n variable test: Color.SRGB = current.to.srgb();\n variable test_in_gamut: Boolean = true;\n \n if (test.r < neg_epsilon) [ test_in_gamut = false; ];\n if (test.r > one_plus_eps) [ test_in_gamut = false; ];\n if (test.g < neg_epsilon) [ test_in_gamut = false; ];\n if (test.g > one_plus_eps) [ test_in_gamut = false; ];\n if (test.b < neg_epsilon) [ test_in_gamut = false; ];\n if (test.b > one_plus_eps) [ test_in_gamut = false; ];\n \n if (test_in_gamut) [\n // We can try higher chroma\n min_c = mid_c;\n ] else [\n // Need to reduce chroma\n max_c = mid_c;\n ];\n \n iterations = iterations + 1;\n];\n\n// Use the last valid chroma\ncurrent.c = min_c;\nvariable result: Color.SRGB = current.to.srgb();\n\n// Final clamp for any floating point errors\nvariable final_result: Color.SRGB;\nfinal_result.r = min(1, max(0, result.r));\nfinal_result.g = min(1, max(0, result.g));\nfinal_result.b = min(1, max(0, result.b));\n\nreturn final_result;"
|
|
3612
|
-
},
|
|
3613
|
-
"examples": [
|
|
3614
|
-
{
|
|
3615
|
-
"description": "Map out-of-gamut P3 color to sRGB",
|
|
3616
|
-
"input": [
|
|
3617
|
-
"oklch(0.8 0.3 150)",
|
|
3618
|
-
"srgb"
|
|
3619
|
-
],
|
|
3620
|
-
"output": "Color with reduced chroma that fits sRGB"
|
|
3621
|
-
},
|
|
3622
|
-
{
|
|
3623
|
-
"description": "In-gamut color returns unchanged",
|
|
3624
|
-
"input": [
|
|
3625
|
-
"#ff0000"
|
|
3626
|
-
],
|
|
3627
|
-
"output": "#ff0000"
|
|
3628
|
-
}
|
|
3629
|
-
],
|
|
3630
|
-
"slug": "to_gamut"
|
|
3631
|
-
},
|
|
3632
|
-
{
|
|
3633
|
-
"name": "Triadic",
|
|
3634
|
-
"type": "function",
|
|
3635
|
-
"description": "Generates triadic colors - three hues equally spaced 120° apart on the color wheel. Creates vibrant, balanced palettes with high contrast.",
|
|
3636
|
-
"keyword": "triadic",
|
|
3637
|
-
"input": {
|
|
3638
|
-
"type": "object",
|
|
3639
|
-
"properties": {
|
|
3640
|
-
"color": {
|
|
3641
|
-
"type": "color",
|
|
3642
|
-
"description": "Base color"
|
|
3643
|
-
}
|
|
3644
|
-
}
|
|
3645
|
-
},
|
|
3646
|
-
"script": {
|
|
3647
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3648
|
-
"script": "// Generate triadic colors (120° apart)\n// Returns [base, base+120°, base+240°]\n//\n// Use case: Vibrant, balanced palettes\n// Example: Red → Green → Blue\n\nvariable input: List = {input};\nvariable base: Color.OKLCH = input.get(0).to.oklch();\n\nvariable result: List;\n\n// Base color\nresult = result, base.to.srgb();\n\n// +120°\nvariable h2: Number = base.h + 120;\nif (h2 >= 360) [ h2 = h2 - 360; ];\nvariable c2: Color.OKLCH;\nc2.l = base.l; c2.c = base.c; c2.h = h2;\nresult = result, c2.to.srgb();\n\n// +240°\nvariable h3: Number = base.h + 240;\nif (h3 >= 360) [ h3 = h3 - 360; ];\nvariable c3: Color.OKLCH;\nc3.l = base.l; c3.c = base.c; c3.h = h3;\nresult = result, c3.to.srgb();\n\nreturn result;"
|
|
3649
|
-
},
|
|
3650
|
-
"requirements": [
|
|
3651
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
3652
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
3653
|
-
],
|
|
3654
|
-
"slug": "triadic"
|
|
3655
|
-
},
|
|
3656
|
-
{
|
|
3657
|
-
"name": "vibrant",
|
|
3658
|
-
"type": "function",
|
|
3659
|
-
"description": "Increases a color's chroma toward a vibrant level. Preserves lightness and hue while boosting colorfulness. The optional amount parameter controls the intensity (0-1, default 0.5). Uses OKLCH for perceptually uniform chroma adjustment. Useful for creating attention-grabbing accents or active states from muted base colors.",
|
|
3660
|
-
"keyword": "vibrant",
|
|
3661
|
-
"requirements": [
|
|
3662
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3663
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
3664
|
-
],
|
|
3665
|
-
"schema": {
|
|
3666
|
-
"type": "object",
|
|
3667
|
-
"properties": {
|
|
3668
|
-
"input": {
|
|
3669
|
-
"type": "array",
|
|
3670
|
-
"items": [
|
|
3671
|
-
{
|
|
3672
|
-
"description": "Color to make vibrant",
|
|
3673
|
-
"type": "color"
|
|
3674
|
-
},
|
|
3675
|
-
{
|
|
3676
|
-
"description": "Amount (0-1, default 0.5). Higher = more vibrant.",
|
|
3677
|
-
"type": "number"
|
|
3678
|
-
}
|
|
3679
|
-
],
|
|
3680
|
-
"minItems": 1,
|
|
3681
|
-
"maxItems": 2
|
|
3682
|
-
}
|
|
3683
|
-
},
|
|
3684
|
-
"required": [
|
|
3685
|
-
"input"
|
|
3686
|
-
]
|
|
3687
|
-
},
|
|
3688
|
-
"returns": {
|
|
3689
|
-
"type": "color",
|
|
3690
|
-
"description": "More vibrant version of the color"
|
|
3691
|
-
},
|
|
3692
|
-
"script": {
|
|
3693
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3694
|
-
"script": "// vibrant: Increase chroma toward maximum colorfulness\n// Preserves lightness and hue\n// Amount controls how much to boost (0-1)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default amount\nvariable amount: Number = 0.5;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Target maximum chroma for sRGB at this lightness (approximate)\n// Real max varies by hue and lightness, but 0.3 is a reasonable target\nvariable max_chroma: Number = 0.3;\n\n// Increase chroma toward max\nvariable current_c: Number = color.c;\nvariable new_c: Number = current_c + (max_chroma - current_c) * amount;\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;"
|
|
3695
|
-
},
|
|
3696
|
-
"examples": [
|
|
3697
|
-
{
|
|
3698
|
-
"description": "Make muted blue more vibrant",
|
|
3699
|
-
"input": [
|
|
3700
|
-
"#6688aa"
|
|
3701
|
-
],
|
|
3702
|
-
"output": "More saturated blue"
|
|
3703
|
-
},
|
|
3704
|
-
{
|
|
3705
|
-
"description": "Full vibrancy",
|
|
3706
|
-
"input": [
|
|
3707
|
-
"#808080",
|
|
3708
|
-
1
|
|
3709
|
-
],
|
|
3710
|
-
"output": "Still gray (no hue to enhance)"
|
|
3711
|
-
}
|
|
3712
|
-
],
|
|
3713
|
-
"slug": "vibrant"
|
|
3714
|
-
},
|
|
3715
|
-
{
|
|
3716
|
-
"name": "warmer",
|
|
3717
|
-
"type": "function",
|
|
3718
|
-
"description": "Shifts a color's hue towards warm colors (orange, ~60° in OKLCH). The amount parameter controls how much to shift, from 0 (no change) to 1 (fully warm). Preserves lightness and chroma.",
|
|
3719
|
-
"keyword": "warmer",
|
|
3720
|
-
"requirements": [
|
|
3721
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3722
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
3723
|
-
],
|
|
3724
|
-
"schema": {
|
|
3725
|
-
"type": "object",
|
|
3726
|
-
"properties": {
|
|
3727
|
-
"input": {
|
|
3728
|
-
"type": "array",
|
|
3729
|
-
"items": [
|
|
3730
|
-
{
|
|
3731
|
-
"description": "Color to warm",
|
|
3732
|
-
"type": "color"
|
|
3733
|
-
},
|
|
3734
|
-
{
|
|
3735
|
-
"description": "Amount to shift (0-1), default 0.25",
|
|
3736
|
-
"type": "number"
|
|
3737
|
-
}
|
|
3738
|
-
],
|
|
3739
|
-
"minItems": 1,
|
|
3740
|
-
"maxItems": 2
|
|
3741
|
-
}
|
|
3742
|
-
},
|
|
3743
|
-
"required": [
|
|
3744
|
-
"input"
|
|
3745
|
-
]
|
|
3746
|
-
},
|
|
3747
|
-
"returns": {
|
|
3748
|
-
"type": "color",
|
|
3749
|
-
"description": "Color shifted towards warm hues"
|
|
3750
|
-
},
|
|
3751
|
-
"script": {
|
|
3752
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3753
|
-
"script": "// warmer: Shift hue towards warm colors\n//\n// Warm colors are centered around orange (~60° in OKLCH).\n// The function interpolates the hue towards this target via\n// the shortest angular path on the hue wheel.\n//\n// Parameters:\n// color - Input color\n// amount - Shift amount (0 = no change, 1 = fully warm), default 0.25\n//\n// Preserves lightness and chroma.\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\nvariable amount: Number = 0.25;\n\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Warm hue target (orange in OKLCH is around 60°)\nvariable warm_hue: Number = 60;\n\n// Calculate shortest path hue difference\nvariable hue_diff: Number = warm_hue - color.h;\n\n// Wrap to shortest path\nif (hue_diff > 180) [ hue_diff = hue_diff - 360; ];\nif (hue_diff < -180) [ hue_diff = hue_diff + 360; ];\n\n// Apply interpolation\nvariable new_hue: Number = color.h + (hue_diff * amount);\n\n// Normalize to 0-360\nif (new_hue < 0) [ new_hue = new_hue + 360; ];\nif (new_hue >= 360) [ new_hue = new_hue - 360; ];\n\n// Create result\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = color.c;\nresult.h = new_hue;\n\nreturn result;"
|
|
3754
|
-
},
|
|
3755
|
-
"examples": [
|
|
3756
|
-
{
|
|
3757
|
-
"description": "Make blue warmer",
|
|
3758
|
-
"input": [
|
|
3759
|
-
"#0000ff",
|
|
3760
|
-
0.5
|
|
3761
|
-
],
|
|
3762
|
-
"output": "Blue shifted towards purple/magenta"
|
|
3763
|
-
}
|
|
3764
|
-
],
|
|
3765
|
-
"slug": "warmer"
|
|
3766
|
-
},
|
|
3767
|
-
{
|
|
3768
|
-
"name": "wcag_level",
|
|
3769
|
-
"type": "function",
|
|
3770
|
-
"description": "Returns the WCAG 2.1 compliance level for a foreground/background color pair. Returns 'AAA' (ratio >= 7:1), 'AA' (ratio >= 4.5:1), 'AA-large' (ratio >= 3:1), or 'fail' (ratio < 3:1). Based on W3C WCAG 2.1 Success Criteria 1.4.3 and 1.4.6.",
|
|
3771
|
-
"keyword": "wcag_level",
|
|
3772
|
-
"requirements": [
|
|
3773
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
3774
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/"
|
|
3775
|
-
],
|
|
3776
|
-
"schema": {
|
|
3777
|
-
"type": "object",
|
|
3778
|
-
"properties": {
|
|
3779
|
-
"input": {
|
|
3780
|
-
"type": "array",
|
|
3781
|
-
"items": [
|
|
3782
|
-
{
|
|
3783
|
-
"description": "Foreground color (text)",
|
|
3784
|
-
"type": "color"
|
|
3785
|
-
},
|
|
3786
|
-
{
|
|
3787
|
-
"description": "Background color",
|
|
3788
|
-
"type": "color"
|
|
3789
|
-
}
|
|
3790
|
-
],
|
|
3791
|
-
"minItems": 2,
|
|
3792
|
-
"maxItems": 2
|
|
3793
|
-
}
|
|
3794
|
-
},
|
|
3795
|
-
"required": [
|
|
3796
|
-
"input"
|
|
3797
|
-
]
|
|
3798
|
-
},
|
|
3799
|
-
"returns": {
|
|
3800
|
-
"type": "string",
|
|
3801
|
-
"description": "WCAG level: 'AAA', 'AA', 'AA-large', or 'fail'"
|
|
3802
|
-
},
|
|
3803
|
-
"script": {
|
|
3804
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
3805
|
-
"script": "// wcag_level: Determine WCAG 2.1 compliance level for a color pair\n// Reference: WCAG 2.1 Success Criterion 1.4.3 (Contrast Minimum)\n// Reference: WCAG 2.1 Success Criterion 1.4.6 (Contrast Enhanced)\n// Reference: https://www.w3.org/TR/WCAG21/#contrast-minimum\n//\n// Returns one of:\n// \"AAA\" - Ratio >= 7:1 (enhanced contrast for normal text)\n// \"AA\" - Ratio >= 4.5:1 (minimum for normal text)\n// \"AA-large\" - Ratio >= 3:1 (minimum for large text: 18pt or 14pt bold)\n// \"fail\" - Ratio < 3:1 (does not meet any criterion)\n\nvariable input: List = {input};\nvariable fg_xyz: Color.XYZD65 = input.get(0).to.xyzd65();\nvariable bg_xyz: Color.XYZD65 = input.get(1).to.xyzd65();\n\nvariable l1: Number = fg_xyz.y;\nvariable l2: Number = bg_xyz.y;\n\n// Ensure l1 is the lighter of the two\nif (l2 > l1) [\n variable temp: Number = l1;\n l1 = l2;\n l2 = temp;\n];\n\n// Calculate contrast ratio per WCAG formula\n// Ratio = (L1 + 0.05) / (L2 + 0.05)\nvariable ratio: Number = (l1 + 0.05) / (l2 + 0.05);\n\n// Determine WCAG level\n// AAA: ratio >= 7:1 (enhanced contrast)\n// AA: ratio >= 4.5:1 (minimum for normal text)\n// AA-large: ratio >= 3:1 (minimum for large text, 18pt+ or 14pt+ bold)\n// fail: ratio < 3:1\n\nvariable result: String = \"fail\";\n\nif (ratio >= 7) [\n result = \"AAA\";\n] else [\n if (ratio >= 4.5) [\n result = \"AA\";\n ] else [\n if (ratio >= 3) [\n result = \"AA-large\";\n ];\n ];\n];\n\nreturn result;"
|
|
3806
|
-
},
|
|
3807
|
-
"examples": [
|
|
3808
|
-
{
|
|
3809
|
-
"description": "Black on white passes AAA",
|
|
3810
|
-
"input": [
|
|
3811
|
-
"#000000",
|
|
3812
|
-
"#ffffff"
|
|
3813
|
-
],
|
|
3814
|
-
"output": "AAA"
|
|
3815
|
-
},
|
|
3816
|
-
{
|
|
3817
|
-
"description": "Gray on white may only pass AA-large",
|
|
3818
|
-
"input": [
|
|
3819
|
-
"#767676",
|
|
3820
|
-
"#ffffff"
|
|
3821
|
-
],
|
|
3822
|
-
"output": "AA"
|
|
3823
|
-
}
|
|
3824
|
-
],
|
|
3825
|
-
"slug": "wcag_level"
|
|
3826
|
-
}
|
|
3827
|
-
],
|
|
3828
|
-
"metadata": {
|
|
3829
|
-
"generatedAt": "2026-01-07T09:20:35.633Z",
|
|
3830
|
-
"totalSchemas": 82,
|
|
3831
|
-
"generatedBy": "npx @tokens-studio/tokenscript-schemas bundle"
|
|
3832
|
-
}
|
|
3833
|
-
}
|