@tokens-studio/tokenscript-schemas 0.0.13 → 0.1.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.
Files changed (153) hide show
  1. package/README.md +21 -0
  2. package/bundled/functions/adjust_chroma.json +60 -0
  3. package/bundled/functions/adjust_hue.json +60 -0
  4. package/bundled/functions/adjust_lightness.json +60 -0
  5. package/bundled/functions/adjust_to_contrast.json +67 -0
  6. package/bundled/functions/alpha_blend.json +31 -0
  7. package/bundled/functions/alpha_scale.json +27 -0
  8. package/bundled/functions/analogous.json +32 -0
  9. package/bundled/functions/apca_contrast.json +27 -0
  10. package/bundled/functions/are_similar.json +73 -0
  11. package/bundled/functions/auto_text_color.json +66 -0
  12. package/bundled/functions/best_contrast.json +28 -0
  13. package/bundled/functions/chroma.json +54 -0
  14. package/bundled/functions/clamp_chroma.json +66 -0
  15. package/bundled/functions/clamp_lightness.json +66 -0
  16. package/bundled/functions/clamp_to_gamut.json +23 -0
  17. package/bundled/functions/complement.json +24 -0
  18. package/bundled/functions/contrast_ratio.json +27 -0
  19. package/bundled/functions/cooler.json +52 -0
  20. package/bundled/functions/darken.json +28 -0
  21. package/bundled/functions/delta_e_2000.json +40 -0
  22. package/bundled/functions/delta_e_76.json +27 -0
  23. package/bundled/functions/delta_e_ok.json +27 -0
  24. package/bundled/functions/desaturate.json +28 -0
  25. package/bundled/functions/distributed.json +36 -0
  26. package/bundled/functions/diverging.json +36 -0
  27. package/bundled/functions/grayscale.json +24 -0
  28. package/bundled/functions/harmonize.json +65 -0
  29. package/bundled/functions/hue.json +54 -0
  30. package/bundled/functions/hue_difference.json +27 -0
  31. package/bundled/functions/in_gamut.json +27 -0
  32. package/bundled/functions/interpolate.json +66 -0
  33. package/bundled/functions/invert.json +1 -1
  34. package/bundled/functions/is_cool.json +23 -0
  35. package/bundled/functions/is_dark.json +27 -0
  36. package/bundled/functions/is_light.json +27 -0
  37. package/bundled/functions/is_neutral.json +65 -0
  38. package/bundled/functions/is_warm.json +23 -0
  39. package/bundled/functions/lighten.json +28 -0
  40. package/bundled/functions/lightness.json +61 -0
  41. package/bundled/functions/luminance.json +23 -0
  42. package/bundled/functions/meets_contrast.json +31 -0
  43. package/bundled/functions/mix.json +32 -0
  44. package/bundled/functions/monochromatic.json +28 -0
  45. package/bundled/functions/muted.json +59 -0
  46. package/bundled/functions/neutral_variant.json +59 -0
  47. package/bundled/functions/relative_luminance.json +61 -0
  48. package/bundled/functions/rotate_hue.json +28 -0
  49. package/bundled/functions/saturate.json +28 -0
  50. package/bundled/functions/scale_chroma.json +60 -0
  51. package/bundled/functions/scale_lightness.json +60 -0
  52. package/bundled/functions/sepia.json +59 -0
  53. package/bundled/functions/set_chroma.json +28 -0
  54. package/bundled/functions/set_hue.json +28 -0
  55. package/bundled/functions/set_lightness.json +28 -0
  56. package/bundled/functions/shade_scale.json +28 -0
  57. package/bundled/functions/split_complement.json +28 -0
  58. package/bundled/functions/steps.json +32 -0
  59. package/bundled/functions/tetradic.json +24 -0
  60. package/bundled/functions/tint_scale.json +36 -0
  61. package/bundled/functions/to_gamut.json +59 -0
  62. package/bundled/functions/triadic.json +24 -0
  63. package/bundled/functions/vibrant.json +59 -0
  64. package/bundled/functions/warmer.json +52 -0
  65. package/bundled/functions/wcag_level.json +60 -0
  66. package/bundled/functions.json +2602 -6
  67. package/bundled/registry.json +3705 -84
  68. package/bundled/types/css-color.json +151 -0
  69. package/bundled/types/hsl-color.json +66 -0
  70. package/bundled/types/hsv-color.json +57 -0
  71. package/bundled/types/hwb-color.json +66 -0
  72. package/bundled/types/lab-color.json +57 -0
  73. package/bundled/types/lch-color.json +57 -0
  74. package/bundled/types/okhsl-color.json +57 -0
  75. package/bundled/types/okhsv-color.json +57 -0
  76. package/bundled/types/oklab-color.json +87 -0
  77. package/bundled/types/oklch-color.json +57 -0
  78. package/bundled/types/p3-color.json +57 -0
  79. package/bundled/types/p3-linear-color.json +57 -0
  80. package/bundled/types/rgb-color.json +12 -3
  81. package/bundled/types/srgb-color.json +77 -0
  82. package/bundled/types/srgb-linear-color.json +67 -0
  83. package/bundled/types/xyz-d50-color.json +57 -0
  84. package/bundled/types/xyz-d65-color.json +77 -0
  85. package/bundled/types.json +1067 -43
  86. package/dist/cli/index.cjs +231 -22
  87. package/dist/cli/index.cjs.map +1 -1
  88. package/dist/cli/index.js +231 -22
  89. package/dist/cli/index.js.map +1 -1
  90. package/dist/index.cjs +5 -2
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.cts +4 -1
  93. package/dist/index.d.ts +4 -1
  94. package/dist/index.js +5 -2
  95. package/dist/index.js.map +1 -1
  96. package/package.json +4 -2
  97. package/src/bundler/index.ts +7 -0
  98. package/src/bundler/presets/css.ts +21 -0
  99. package/src/bundler/presets/index.ts +47 -0
  100. package/src/bundler/presets/types.ts +10 -0
  101. package/src/bundler/selective-bundler.ts +9 -0
  102. package/src/bundler/types.ts +1 -0
  103. package/src/cli/commands/bundle.test.ts +34 -0
  104. package/src/cli/commands/bundle.ts +37 -11
  105. package/src/cli/commands/list.ts +36 -4
  106. package/src/cli/commands/presets.ts +81 -0
  107. package/src/cli/index.ts +12 -1
  108. package/src/cli/output-generator.ts +8 -2
  109. package/src/cli/version-info.ts +93 -0
  110. package/src/schemas/types/css-color/from-hsl-color.tokenscript +17 -4
  111. package/src/schemas/types/css-color/from-hwb-color.tokenscript +17 -4
  112. package/src/schemas/types/css-color/from-lab-color.tokenscript +17 -4
  113. package/src/schemas/types/css-color/from-lch-color.tokenscript +17 -4
  114. package/src/schemas/types/css-color/from-oklab-color.tokenscript +17 -4
  115. package/src/schemas/types/css-color/from-oklch-color.tokenscript +17 -4
  116. package/src/schemas/types/css-color/from-p3-color.tokenscript +17 -4
  117. package/src/schemas/types/css-color/from-rgb-color.tokenscript +17 -4
  118. package/src/schemas/types/css-color/from-srgb-color.tokenscript +17 -4
  119. package/src/schemas/types/css-color/from-xyz-d50-color.tokenscript +17 -4
  120. package/src/schemas/types/css-color/from-xyz-d65-color.tokenscript +17 -4
  121. package/src/schemas/types/css-color/unit.test.ts +216 -0
  122. package/src/schemas/types/hex-color/unit.test.ts +18 -0
  123. package/src/schemas/types/hsl-color/hsla-initializer.tokenscript +17 -0
  124. package/src/schemas/types/hsl-color/initializer.tokenscript +6 -1
  125. package/src/schemas/types/hsl-color/schema.json +9 -0
  126. package/src/schemas/types/hsl-color/unit.test.ts +95 -1
  127. package/src/schemas/types/hsv-color/initializer.tokenscript +6 -1
  128. package/src/schemas/types/hsv-color/unit.test.ts +44 -0
  129. package/src/schemas/types/hwb-color/hwba-initializer.tokenscript +17 -0
  130. package/src/schemas/types/hwb-color/initializer.tokenscript +6 -1
  131. package/src/schemas/types/hwb-color/schema.json +9 -0
  132. package/src/schemas/types/hwb-color/unit.test.ts +70 -0
  133. package/src/schemas/types/lab-color/initializer.tokenscript +6 -1
  134. package/src/schemas/types/lab-color/unit.test.ts +44 -0
  135. package/src/schemas/types/lch-color/initializer.tokenscript +6 -1
  136. package/src/schemas/types/lch-color/unit.test.ts +44 -0
  137. package/src/schemas/types/okhsl-color/initializer.tokenscript +8 -1
  138. package/src/schemas/types/okhsl-color/unit.test.ts +37 -0
  139. package/src/schemas/types/okhsv-color/initializer.tokenscript +8 -1
  140. package/src/schemas/types/okhsv-color/unit.test.ts +37 -0
  141. package/src/schemas/types/oklab-color/initializer.tokenscript +6 -1
  142. package/src/schemas/types/oklab-color/unit.test.ts +58 -0
  143. package/src/schemas/types/oklch-color/initializer.tokenscript +6 -1
  144. package/src/schemas/types/oklch-color/unit.test.ts +58 -0
  145. package/src/schemas/types/p3-color/initializer.tokenscript +6 -1
  146. package/src/schemas/types/p3-color/unit.test.ts +47 -0
  147. package/src/schemas/types/rgb-color/initializer.tokenscript +7 -1
  148. package/src/schemas/types/rgb-color/rgba-initializer.tokenscript +17 -0
  149. package/src/schemas/types/rgb-color/schema.json +9 -0
  150. package/src/schemas/types/rgb-color/unit.test.ts +110 -1
  151. package/src/schemas/types/srgb-color/initializer.tokenscript +6 -1
  152. package/src/schemas/types/srgb-color/unit.test.ts +89 -0
  153. package/bundled/types/rgba-color.json +0 -89
@@ -0,0 +1,151 @@
1
+ {
2
+ "name": "CSS",
3
+ "type": "color",
4
+ "description": "CSS color string representation. Outputs the appropriate CSS syntax for any color space.",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "value": {
9
+ "type": "string",
10
+ "description": "CSS color string (e.g., 'rgb(255 128 64)', 'oklch(0.7 0.15 180)')"
11
+ }
12
+ },
13
+ "required": [
14
+ "value"
15
+ ]
16
+ },
17
+ "initializers": [
18
+ {
19
+ "title": "CSS Color Initializer",
20
+ "keyword": "css",
21
+ "description": "Creates a CSS color from a string value",
22
+ "script": {
23
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
24
+ "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;"
25
+ }
26
+ }
27
+ ],
28
+ "conversions": [
29
+ {
30
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/",
31
+ "target": "$self",
32
+ "description": "Converts RGB (0-255) to CSS rgb() syntax",
33
+ "lossless": true,
34
+ "script": {
35
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
36
+ "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;"
37
+ }
38
+ },
39
+ {
40
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
41
+ "target": "$self",
42
+ "description": "Converts sRGB (0-1) to CSS color(srgb) syntax",
43
+ "lossless": true,
44
+ "script": {
45
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
46
+ "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;"
47
+ }
48
+ },
49
+ {
50
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/",
51
+ "target": "$self",
52
+ "description": "Converts HSL to CSS hsl() syntax",
53
+ "lossless": true,
54
+ "script": {
55
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
56
+ "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;"
57
+ }
58
+ },
59
+ {
60
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hwb-color/0/",
61
+ "target": "$self",
62
+ "description": "Converts HWB to CSS hwb() syntax",
63
+ "lossless": true,
64
+ "script": {
65
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
66
+ "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;"
67
+ }
68
+ },
69
+ {
70
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/",
71
+ "target": "$self",
72
+ "description": "Converts CIE Lab to CSS lab() syntax",
73
+ "lossless": true,
74
+ "script": {
75
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
76
+ "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;"
77
+ }
78
+ },
79
+ {
80
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lch-color/0/",
81
+ "target": "$self",
82
+ "description": "Converts CIE LCH to CSS lch() syntax",
83
+ "lossless": true,
84
+ "script": {
85
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
86
+ "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;"
87
+ }
88
+ },
89
+ {
90
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
91
+ "target": "$self",
92
+ "description": "Converts OKLab to CSS oklab() syntax",
93
+ "lossless": true,
94
+ "script": {
95
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
96
+ "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;"
97
+ }
98
+ },
99
+ {
100
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
101
+ "target": "$self",
102
+ "description": "Converts OKLCH to CSS oklch() syntax",
103
+ "lossless": true,
104
+ "script": {
105
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
106
+ "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;"
107
+ }
108
+ },
109
+ {
110
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/",
111
+ "target": "$self",
112
+ "description": "Converts Linear sRGB to CSS color(srgb-linear) syntax",
113
+ "lossless": true,
114
+ "script": {
115
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
116
+ "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;"
117
+ }
118
+ },
119
+ {
120
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/",
121
+ "target": "$self",
122
+ "description": "Converts XYZ-D65 to CSS color(xyz-d65) syntax",
123
+ "lossless": true,
124
+ "script": {
125
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
126
+ "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;"
127
+ }
128
+ },
129
+ {
130
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/",
131
+ "target": "$self",
132
+ "description": "Converts XYZ-D50 to CSS color(xyz-d50) syntax",
133
+ "lossless": true,
134
+ "script": {
135
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
136
+ "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;"
137
+ }
138
+ },
139
+ {
140
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-color/0/",
141
+ "target": "$self",
142
+ "description": "Converts Display-P3 to CSS color(display-p3) syntax",
143
+ "lossless": true,
144
+ "script": {
145
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
146
+ "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;"
147
+ }
148
+ }
149
+ ],
150
+ "slug": "css-color"
151
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "HSL",
3
+ "type": "color",
4
+ "description": "HSL color space - Hue, Saturation, Lightness. Popular for color pickers and CSS.",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "h": {
9
+ "type": "number",
10
+ "description": "Hue angle (0-360 degrees)"
11
+ },
12
+ "s": {
13
+ "type": "number",
14
+ "description": "Saturation (0-1)"
15
+ },
16
+ "l": {
17
+ "type": "number",
18
+ "description": "Lightness (0-1)"
19
+ }
20
+ },
21
+ "required": [
22
+ "h",
23
+ "s",
24
+ "l"
25
+ ],
26
+ "order": [
27
+ "h",
28
+ "s",
29
+ "l"
30
+ ],
31
+ "additionalProperties": false
32
+ },
33
+ "initializers": [
34
+ {
35
+ "title": "HSL Color Initializer",
36
+ "keyword": "hsl",
37
+ "description": "Creates an HSL color from H, S, L values",
38
+ "script": {
39
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
40
+ "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;"
41
+ }
42
+ },
43
+ {
44
+ "title": "HSLA Color Initializer",
45
+ "keyword": "hsla",
46
+ "description": "Creates an HSL color with alpha from H, S, L, A values",
47
+ "script": {
48
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
49
+ "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;"
50
+ }
51
+ }
52
+ ],
53
+ "conversions": [
54
+ {
55
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
56
+ "target": "$self",
57
+ "description": "Converts sRGB to HSL",
58
+ "lossless": true,
59
+ "script": {
60
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
61
+ "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;"
62
+ }
63
+ }
64
+ ],
65
+ "slug": "hsl-color"
66
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "HSV",
3
+ "type": "color",
4
+ "description": "HSV color space - Hue, Saturation, Value. Also known as HSB (Hue, Saturation, Brightness).",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "h": {
9
+ "type": "number",
10
+ "description": "Hue angle (0-360 degrees)"
11
+ },
12
+ "s": {
13
+ "type": "number",
14
+ "description": "Saturation (0-1)"
15
+ },
16
+ "v": {
17
+ "type": "number",
18
+ "description": "Value/Brightness (0-1)"
19
+ }
20
+ },
21
+ "required": [
22
+ "h",
23
+ "s",
24
+ "v"
25
+ ],
26
+ "order": [
27
+ "h",
28
+ "s",
29
+ "v"
30
+ ],
31
+ "additionalProperties": false
32
+ },
33
+ "initializers": [
34
+ {
35
+ "title": "HSV Color Initializer",
36
+ "keyword": "hsv",
37
+ "description": "Creates an HSV color from H, S, V values",
38
+ "script": {
39
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
40
+ "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;"
41
+ }
42
+ }
43
+ ],
44
+ "conversions": [
45
+ {
46
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
47
+ "target": "$self",
48
+ "description": "Converts sRGB to HSV",
49
+ "lossless": true,
50
+ "script": {
51
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
52
+ "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;"
53
+ }
54
+ }
55
+ ],
56
+ "slug": "hsv-color"
57
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "HWB",
3
+ "type": "color",
4
+ "description": "HWB color space - Hue, Whiteness, Blackness. CSS Color Level 4.",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "h": {
9
+ "type": "number",
10
+ "description": "Hue angle (0-360 degrees)"
11
+ },
12
+ "w": {
13
+ "type": "number",
14
+ "description": "Whiteness (0-1)"
15
+ },
16
+ "b": {
17
+ "type": "number",
18
+ "description": "Blackness (0-1)"
19
+ }
20
+ },
21
+ "required": [
22
+ "h",
23
+ "w",
24
+ "b"
25
+ ],
26
+ "order": [
27
+ "h",
28
+ "w",
29
+ "b"
30
+ ],
31
+ "additionalProperties": false
32
+ },
33
+ "initializers": [
34
+ {
35
+ "title": "HWB Color Initializer",
36
+ "keyword": "hwb",
37
+ "description": "Creates an HWB color from H, W, B values",
38
+ "script": {
39
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
40
+ "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;"
41
+ }
42
+ },
43
+ {
44
+ "title": "HWBA Color Initializer",
45
+ "keyword": "hwba",
46
+ "description": "Creates an HWB color with alpha from H, W, B, A values",
47
+ "script": {
48
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
49
+ "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;"
50
+ }
51
+ }
52
+ ],
53
+ "conversions": [
54
+ {
55
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsv-color/0/",
56
+ "target": "$self",
57
+ "description": "Converts HSV to HWB",
58
+ "lossless": true,
59
+ "script": {
60
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
61
+ "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;"
62
+ }
63
+ }
64
+ ],
65
+ "slug": "hwb-color"
66
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "Lab",
3
+ "type": "color",
4
+ "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.",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "l": {
9
+ "type": "number",
10
+ "description": "Lightness (0-100)"
11
+ },
12
+ "a": {
13
+ "type": "number",
14
+ "description": "Green-red axis (typically -125 to 125)"
15
+ },
16
+ "b": {
17
+ "type": "number",
18
+ "description": "Blue-yellow axis (typically -125 to 125)"
19
+ }
20
+ },
21
+ "required": [
22
+ "l",
23
+ "a",
24
+ "b"
25
+ ],
26
+ "order": [
27
+ "l",
28
+ "a",
29
+ "b"
30
+ ],
31
+ "additionalProperties": false
32
+ },
33
+ "initializers": [
34
+ {
35
+ "title": "Lab Color Initializer",
36
+ "keyword": "lab",
37
+ "description": "Creates a CIE Lab color from L, a, b values",
38
+ "script": {
39
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
40
+ "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;"
41
+ }
42
+ }
43
+ ],
44
+ "conversions": [
45
+ {
46
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/",
47
+ "target": "$self",
48
+ "description": "Converts XYZ-D50 to CIE Lab using the CIE standard formula",
49
+ "lossless": true,
50
+ "script": {
51
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
52
+ "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;"
53
+ }
54
+ }
55
+ ],
56
+ "slug": "lab-color"
57
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "LCH",
3
+ "type": "color",
4
+ "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).",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "l": {
9
+ "type": "number",
10
+ "description": "Lightness (0-100)"
11
+ },
12
+ "c": {
13
+ "type": "number",
14
+ "description": "Chroma"
15
+ },
16
+ "h": {
17
+ "type": "number",
18
+ "description": "Hue angle (0-360 degrees)"
19
+ }
20
+ },
21
+ "required": [
22
+ "l",
23
+ "c",
24
+ "h"
25
+ ],
26
+ "order": [
27
+ "l",
28
+ "c",
29
+ "h"
30
+ ],
31
+ "additionalProperties": false
32
+ },
33
+ "initializers": [
34
+ {
35
+ "title": "LCH Color Initializer",
36
+ "keyword": "lch",
37
+ "description": "Creates a CIE LCH color from L, C, H values",
38
+ "script": {
39
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
40
+ "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;"
41
+ }
42
+ }
43
+ ],
44
+ "conversions": [
45
+ {
46
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/",
47
+ "target": "$self",
48
+ "description": "Converts CIE Lab to LCH using Cartesian to polar transformation",
49
+ "lossless": true,
50
+ "script": {
51
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
52
+ "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;"
53
+ }
54
+ }
55
+ ],
56
+ "slug": "lch-color"
57
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "OKHSL",
3
+ "type": "color",
4
+ "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/",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "h": {
9
+ "type": "number",
10
+ "description": "Hue angle (0-360 degrees), same as OKLCH hue"
11
+ },
12
+ "s": {
13
+ "type": "number",
14
+ "description": "Saturation (0-1), normalized relative to sRGB gamut boundary"
15
+ },
16
+ "l": {
17
+ "type": "number",
18
+ "description": "Lightness (0-1), perceptually uniform with toe function applied"
19
+ }
20
+ },
21
+ "required": [
22
+ "h",
23
+ "s",
24
+ "l"
25
+ ],
26
+ "order": [
27
+ "h",
28
+ "s",
29
+ "l"
30
+ ],
31
+ "additionalProperties": false
32
+ },
33
+ "initializers": [
34
+ {
35
+ "title": "OKHSL Color Initializer",
36
+ "keyword": "okhsl",
37
+ "description": "Creates an OKHSL color from H, S, L values",
38
+ "script": {
39
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
40
+ "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"
41
+ }
42
+ }
43
+ ],
44
+ "conversions": [
45
+ {
46
+ "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/",
47
+ "target": "$self",
48
+ "description": "Converts OKLab to OKHSL using Ottosson's algorithm with Halley's method refinement for gamut boundary",
49
+ "lossless": true,
50
+ "script": {
51
+ "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
52
+ "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"
53
+ }
54
+ }
55
+ ],
56
+ "slug": "okhsl-color"
57
+ }