@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/src/cli/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import cac from "cac";
|
|
10
10
|
import anylogger from "ulog";
|
|
11
|
+
import { type BuildDirOptions, handleBuildCommand } from "./commands/build-dir.js";
|
|
11
12
|
import { type BundleOptions, handleBundleCommand } from "./commands/bundle.js";
|
|
12
13
|
import { handleListCommand, type ListOptions } from "./commands/list.js";
|
|
13
14
|
import { handlePresetsCommand } from "./commands/presets.js";
|
|
@@ -22,6 +23,7 @@ cli
|
|
|
22
23
|
.option("-c, --config <path>", "Path to config file")
|
|
23
24
|
.option("-o, --output <path>", "Output file path", { default: "./tokenscript-schemas.js" })
|
|
24
25
|
.option("-d, --dry-run", "Preview what would be bundled without writing")
|
|
26
|
+
.option("-s, --schemas-dir <path>", "Custom schema directory (overrides default)")
|
|
25
27
|
.action(async (schemas: string[], options: BundleOptions) => {
|
|
26
28
|
try {
|
|
27
29
|
await handleBundleCommand(schemas, options);
|
|
@@ -31,6 +33,20 @@ cli
|
|
|
31
33
|
}
|
|
32
34
|
});
|
|
33
35
|
|
|
36
|
+
// Build command
|
|
37
|
+
cli
|
|
38
|
+
.command("build <directory>", "Build an individual schema directory")
|
|
39
|
+
.option("-o, --output <path>", "Output file path (defaults to stdout)")
|
|
40
|
+
.option("-p, --pretty", "Pretty print JSON output")
|
|
41
|
+
.action(async (directory: string, options: BuildDirOptions) => {
|
|
42
|
+
try {
|
|
43
|
+
await handleBuildCommand(directory, options);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
log.error("Error:", error);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
34
50
|
// List command
|
|
35
51
|
cli
|
|
36
52
|
.command("list", "List available schemas")
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "adjust_chroma",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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].",
|
|
5
|
-
"keyword": "adjust_chroma",
|
|
6
|
-
"requirements": [
|
|
7
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
8
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
9
|
-
],
|
|
10
|
-
"schema": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"properties": {
|
|
13
|
-
"input": {
|
|
14
|
-
"type": "array",
|
|
15
|
-
"items": [
|
|
16
|
-
{
|
|
17
|
-
"description": "Color to adjust",
|
|
18
|
-
"type": "color"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"description": "Amount to adjust chroma (+/-)",
|
|
22
|
-
"type": "number"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"minItems": 2,
|
|
26
|
-
"maxItems": 2
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"required": [
|
|
30
|
-
"input"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
"returns": {
|
|
34
|
-
"type": "color",
|
|
35
|
-
"description": "Color with adjusted chroma"
|
|
36
|
-
},
|
|
37
|
-
"script": {
|
|
38
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
39
|
-
"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;"
|
|
40
|
-
},
|
|
41
|
-
"examples": [
|
|
42
|
-
{
|
|
43
|
-
"description": "Increase saturation",
|
|
44
|
-
"input": [
|
|
45
|
-
"#808080",
|
|
46
|
-
0.1
|
|
47
|
-
],
|
|
48
|
-
"output": "More saturated color"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"description": "Decrease saturation",
|
|
52
|
-
"input": [
|
|
53
|
-
"#ff6600",
|
|
54
|
-
-0.1
|
|
55
|
-
],
|
|
56
|
-
"output": "Less saturated orange"
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
"slug": "adjust_chroma"
|
|
60
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "adjust_hue",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "adjust_hue",
|
|
6
|
-
"requirements": [
|
|
7
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
8
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
9
|
-
],
|
|
10
|
-
"schema": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"properties": {
|
|
13
|
-
"input": {
|
|
14
|
-
"type": "array",
|
|
15
|
-
"items": [
|
|
16
|
-
{
|
|
17
|
-
"description": "Color to adjust",
|
|
18
|
-
"type": "color"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"description": "Degrees to rotate hue (+ or -)",
|
|
22
|
-
"type": "number"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"minItems": 2,
|
|
26
|
-
"maxItems": 2
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"required": [
|
|
30
|
-
"input"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
"returns": {
|
|
34
|
-
"type": "color",
|
|
35
|
-
"description": "Color with adjusted hue"
|
|
36
|
-
},
|
|
37
|
-
"script": {
|
|
38
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
39
|
-
"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;"
|
|
40
|
-
},
|
|
41
|
-
"examples": [
|
|
42
|
-
{
|
|
43
|
-
"description": "Shift red toward orange",
|
|
44
|
-
"input": [
|
|
45
|
-
"#ff0000",
|
|
46
|
-
30
|
|
47
|
-
],
|
|
48
|
-
"output": "Orange color"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"description": "Shift blue toward green",
|
|
52
|
-
"input": [
|
|
53
|
-
"#0000ff",
|
|
54
|
-
-60
|
|
55
|
-
],
|
|
56
|
-
"output": "Cyan color"
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
"slug": "adjust_hue"
|
|
60
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "adjust_lightness",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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].",
|
|
5
|
-
"keyword": "adjust_lightness",
|
|
6
|
-
"requirements": [
|
|
7
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
8
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/"
|
|
9
|
-
],
|
|
10
|
-
"schema": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"properties": {
|
|
13
|
-
"input": {
|
|
14
|
-
"type": "array",
|
|
15
|
-
"items": [
|
|
16
|
-
{
|
|
17
|
-
"description": "Color to adjust",
|
|
18
|
-
"type": "color"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"description": "Amount to adjust lightness (+/-)",
|
|
22
|
-
"type": "number"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"minItems": 2,
|
|
26
|
-
"maxItems": 2
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"required": [
|
|
30
|
-
"input"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
"returns": {
|
|
34
|
-
"type": "color",
|
|
35
|
-
"description": "Color with adjusted lightness"
|
|
36
|
-
},
|
|
37
|
-
"script": {
|
|
38
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
39
|
-
"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;"
|
|
40
|
-
},
|
|
41
|
-
"examples": [
|
|
42
|
-
{
|
|
43
|
-
"description": "Lighten a color",
|
|
44
|
-
"input": [
|
|
45
|
-
"#808080",
|
|
46
|
-
0.2
|
|
47
|
-
],
|
|
48
|
-
"output": "Lighter gray"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"description": "Darken a color",
|
|
52
|
-
"input": [
|
|
53
|
-
"#808080",
|
|
54
|
-
-0.3
|
|
55
|
-
],
|
|
56
|
-
"output": "Darker gray"
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
"slug": "adjust_lightness"
|
|
60
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "adjust_to_contrast",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "adjust_to_contrast",
|
|
6
|
-
"requirements": [
|
|
7
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
8
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
9
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/"
|
|
10
|
-
],
|
|
11
|
-
"schema": {
|
|
12
|
-
"type": "object",
|
|
13
|
-
"properties": {
|
|
14
|
-
"input": {
|
|
15
|
-
"type": "array",
|
|
16
|
-
"items": [
|
|
17
|
-
{
|
|
18
|
-
"description": "Foreground color to adjust",
|
|
19
|
-
"type": "color"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"description": "Background color (fixed)",
|
|
23
|
-
"type": "color"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"description": "Target contrast ratio (default 4.5 for WCAG AA)",
|
|
27
|
-
"type": "number"
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"minItems": 2,
|
|
31
|
-
"maxItems": 3
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
"required": [
|
|
35
|
-
"input"
|
|
36
|
-
]
|
|
37
|
-
},
|
|
38
|
-
"returns": {
|
|
39
|
-
"type": "color",
|
|
40
|
-
"description": "Adjusted foreground color meeting the target contrast"
|
|
41
|
-
},
|
|
42
|
-
"script": {
|
|
43
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
44
|
-
"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;"
|
|
45
|
-
},
|
|
46
|
-
"examples": [
|
|
47
|
-
{
|
|
48
|
-
"description": "Adjust light gray to meet AA on white",
|
|
49
|
-
"input": [
|
|
50
|
-
"#cccccc",
|
|
51
|
-
"#ffffff",
|
|
52
|
-
4.5
|
|
53
|
-
],
|
|
54
|
-
"output": "Darkened gray meeting 4.5:1"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"description": "Adjust brand color for accessibility",
|
|
58
|
-
"input": [
|
|
59
|
-
"#6750a4",
|
|
60
|
-
"#ffffff",
|
|
61
|
-
4.5
|
|
62
|
-
],
|
|
63
|
-
"output": "Adjusted purple meeting 4.5:1"
|
|
64
|
-
}
|
|
65
|
-
],
|
|
66
|
-
"slug": "adjust_to_contrast"
|
|
67
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Alpha Blend",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "alpha_blend",
|
|
6
|
-
"input": {
|
|
7
|
-
"type": "object",
|
|
8
|
-
"properties": {
|
|
9
|
-
"foreground": {
|
|
10
|
-
"type": "color",
|
|
11
|
-
"description": "Foreground (top) color"
|
|
12
|
-
},
|
|
13
|
-
"background": {
|
|
14
|
-
"type": "color",
|
|
15
|
-
"description": "Background (bottom) color"
|
|
16
|
-
},
|
|
17
|
-
"alpha": {
|
|
18
|
-
"type": "number",
|
|
19
|
-
"description": "Opacity of foreground (0-1, default: 0.5)"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"script": {
|
|
24
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
25
|
-
"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;"
|
|
26
|
-
},
|
|
27
|
-
"requirements": [
|
|
28
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
29
|
-
],
|
|
30
|
-
"slug": "alpha_blend"
|
|
31
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Alpha Scale",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"description": "Generates transparency variants of a color. Returns colors with progressively lower opacity. Perfect for overlays, shadows, and layered UI elements.",
|
|
5
|
-
"keyword": "alpha_scale",
|
|
6
|
-
"input": {
|
|
7
|
-
"type": "object",
|
|
8
|
-
"properties": {
|
|
9
|
-
"color": {
|
|
10
|
-
"type": "color",
|
|
11
|
-
"description": "Base color"
|
|
12
|
-
},
|
|
13
|
-
"count": {
|
|
14
|
-
"type": "number",
|
|
15
|
-
"description": "Number of alpha steps. Default is 10"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"script": {
|
|
20
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
21
|
-
"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;"
|
|
22
|
-
},
|
|
23
|
-
"requirements": [
|
|
24
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
25
|
-
],
|
|
26
|
-
"slug": "alpha_scale"
|
|
27
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Analogous",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"description": "Generates analogous colors - hues adjacent on the color wheel. Creates harmonious, low-contrast palettes. Perfect for backgrounds and subtle variations.",
|
|
5
|
-
"keyword": "analogous",
|
|
6
|
-
"input": {
|
|
7
|
-
"type": "object",
|
|
8
|
-
"properties": {
|
|
9
|
-
"color": {
|
|
10
|
-
"type": "color",
|
|
11
|
-
"description": "Base color"
|
|
12
|
-
},
|
|
13
|
-
"count": {
|
|
14
|
-
"type": "number",
|
|
15
|
-
"description": "Number of colors (odd recommended). Default is 5"
|
|
16
|
-
},
|
|
17
|
-
"spread": {
|
|
18
|
-
"type": "number",
|
|
19
|
-
"description": "Total angle spread in degrees. Default is 60"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"script": {
|
|
24
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
25
|
-
"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;"
|
|
26
|
-
},
|
|
27
|
-
"requirements": [
|
|
28
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/",
|
|
29
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
30
|
-
],
|
|
31
|
-
"slug": "analogous"
|
|
32
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "APCA Contrast",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "apca_contrast",
|
|
6
|
-
"input": {
|
|
7
|
-
"type": "object",
|
|
8
|
-
"properties": {
|
|
9
|
-
"text": {
|
|
10
|
-
"type": "color",
|
|
11
|
-
"description": "Text/foreground color"
|
|
12
|
-
},
|
|
13
|
-
"background": {
|
|
14
|
-
"type": "color",
|
|
15
|
-
"description": "Background color"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"script": {
|
|
20
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
21
|
-
"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;"
|
|
22
|
-
},
|
|
23
|
-
"requirements": [
|
|
24
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
25
|
-
],
|
|
26
|
-
"slug": "apca_contrast"
|
|
27
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "are_similar",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "are_similar",
|
|
6
|
-
"requirements": [
|
|
7
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
8
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/"
|
|
9
|
-
],
|
|
10
|
-
"schema": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"properties": {
|
|
13
|
-
"input": {
|
|
14
|
-
"type": "array",
|
|
15
|
-
"items": [
|
|
16
|
-
{
|
|
17
|
-
"description": "First color",
|
|
18
|
-
"type": "color"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"description": "Second color",
|
|
22
|
-
"type": "color"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"description": "Delta E threshold (default 0.02 ≈ JND)",
|
|
26
|
-
"type": "number"
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
|
-
"minItems": 2,
|
|
30
|
-
"maxItems": 3
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"required": [
|
|
34
|
-
"input"
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
"returns": {
|
|
38
|
-
"type": "boolean",
|
|
39
|
-
"description": "True if colors are perceptually similar (within threshold)"
|
|
40
|
-
},
|
|
41
|
-
"script": {
|
|
42
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
43
|
-
"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;"
|
|
44
|
-
},
|
|
45
|
-
"examples": [
|
|
46
|
-
{
|
|
47
|
-
"description": "Nearly identical colors",
|
|
48
|
-
"input": [
|
|
49
|
-
"#ff0000",
|
|
50
|
-
"#ff0001"
|
|
51
|
-
],
|
|
52
|
-
"output": true
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"description": "Obviously different colors",
|
|
56
|
-
"input": [
|
|
57
|
-
"#ff0000",
|
|
58
|
-
"#00ff00"
|
|
59
|
-
],
|
|
60
|
-
"output": false
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"description": "Custom threshold for stricter comparison",
|
|
64
|
-
"input": [
|
|
65
|
-
"#808080",
|
|
66
|
-
"#818181",
|
|
67
|
-
0.01
|
|
68
|
-
],
|
|
69
|
-
"output": true
|
|
70
|
-
}
|
|
71
|
-
],
|
|
72
|
-
"slug": "are_similar"
|
|
73
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "auto_text_color",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "auto_text_color",
|
|
6
|
-
"requirements": [
|
|
7
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/",
|
|
8
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/"
|
|
9
|
-
],
|
|
10
|
-
"schema": {
|
|
11
|
-
"type": "object",
|
|
12
|
-
"properties": {
|
|
13
|
-
"input": {
|
|
14
|
-
"type": "array",
|
|
15
|
-
"items": [
|
|
16
|
-
{
|
|
17
|
-
"description": "Background color to test",
|
|
18
|
-
"type": "color"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"description": "Optional threshold (0-1, default 0.179 per WCAG). Higher values bias toward white text.",
|
|
22
|
-
"type": "number"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"minItems": 1,
|
|
26
|
-
"maxItems": 2
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"required": [
|
|
30
|
-
"input"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
"returns": {
|
|
34
|
-
"type": "color",
|
|
35
|
-
"description": "Either black (#000000) or white (#ffffff) for optimal contrast"
|
|
36
|
-
},
|
|
37
|
-
"script": {
|
|
38
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
39
|
-
"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;"
|
|
40
|
-
},
|
|
41
|
-
"examples": [
|
|
42
|
-
{
|
|
43
|
-
"description": "Dark background returns white text",
|
|
44
|
-
"input": [
|
|
45
|
-
"#1a1a1a"
|
|
46
|
-
],
|
|
47
|
-
"output": "#ffffff"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
"description": "Light background returns black text",
|
|
51
|
-
"input": [
|
|
52
|
-
"#f0f0f0"
|
|
53
|
-
],
|
|
54
|
-
"output": "#000000"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"description": "Mid-tone with custom threshold",
|
|
58
|
-
"input": [
|
|
59
|
-
"#808080",
|
|
60
|
-
0.5
|
|
61
|
-
],
|
|
62
|
-
"output": "#ffffff"
|
|
63
|
-
}
|
|
64
|
-
],
|
|
65
|
-
"slug": "auto_text_color"
|
|
66
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Best Contrast",
|
|
3
|
-
"type": "function",
|
|
4
|
-
"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.",
|
|
5
|
-
"keyword": "best_contrast",
|
|
6
|
-
"input": {
|
|
7
|
-
"type": "object",
|
|
8
|
-
"properties": {
|
|
9
|
-
"background": {
|
|
10
|
-
"type": "color",
|
|
11
|
-
"description": "The background color to contrast against"
|
|
12
|
-
},
|
|
13
|
-
"candidates": {
|
|
14
|
-
"type": "list",
|
|
15
|
-
"description": "List of candidate colors to choose from. Default is [black, white]"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"script": {
|
|
20
|
-
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
21
|
-
"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;"
|
|
22
|
-
},
|
|
23
|
-
"requirements": [
|
|
24
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/",
|
|
25
|
-
"https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/"
|
|
26
|
-
],
|
|
27
|
-
"slug": "best_contrast"
|
|
28
|
-
}
|