@tokens-studio/tokenscript-schemas 0.1.2 → 0.1.3
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 +31 -7
- package/dist/cli/index.cjs +15 -8
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +15 -8
- 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/bundle.test.ts +95 -1
- package/src/cli/commands/bundle.ts +11 -1
- package/src/cli/index.ts +1 -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
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
SchemaSpecification,
|
|
11
11
|
} from "@/bundler/types.js";
|
|
12
12
|
import { extractSchemaName, parseSchemaUri } from "@/utils/schema-uri";
|
|
13
|
-
import {
|
|
13
|
+
import { buildSchemaFromDirectory } from "./build-schema.js";
|
|
14
14
|
|
|
15
15
|
export interface SchemaReference {
|
|
16
16
|
slug: string;
|
|
@@ -181,7 +181,7 @@ export async function collectRequiredSchemas(
|
|
|
181
181
|
schemasDir || process.env.SCHEMAS_DIR || join(process.cwd(), "src/schemas");
|
|
182
182
|
const schemaDir = join(resolvedSchemasDir, categoryDir, slug);
|
|
183
183
|
|
|
184
|
-
spec = await
|
|
184
|
+
spec = await buildSchemaFromDirectory(schemaDir, baseUrl ? { baseUrl } : undefined);
|
|
185
185
|
} catch (error) {
|
|
186
186
|
log.warn(`Failed to load schema ${slug} (${effectiveType}):`, error);
|
|
187
187
|
return;
|
|
@@ -275,7 +275,7 @@ export async function collectDependencyTree(
|
|
|
275
275
|
const schemaDir = join(resolvedSchemasDir, categoryDir, schema.slug);
|
|
276
276
|
|
|
277
277
|
try {
|
|
278
|
-
const spec = await
|
|
278
|
+
const spec = await buildSchemaFromDirectory(schemaDir, baseUrl ? { baseUrl } : undefined);
|
|
279
279
|
const requirements = extractRequirements(spec, extractOptions);
|
|
280
280
|
|
|
281
281
|
// Extract just the slugs from URIs
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { access } from "node:fs/promises";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import { buildSchemaFromDirectory } from "./build-schema.js";
|
|
9
9
|
import {
|
|
10
10
|
collectDependencyTree,
|
|
11
11
|
collectRequiredSchemasForList,
|
|
@@ -124,7 +124,7 @@ export async function bundleSelectiveSchemas(
|
|
|
124
124
|
// Bundle type schemas
|
|
125
125
|
for (const typeSlug of deps.types) {
|
|
126
126
|
const schemaDir = join(schemasDir, "types", typeSlug);
|
|
127
|
-
const bundled = await
|
|
127
|
+
const bundled = await buildSchemaFromDirectory(schemaDir, { baseUrl });
|
|
128
128
|
|
|
129
129
|
if (bundled.type === "color") {
|
|
130
130
|
const uri = `${baseUrl}/api/v1/core/${typeSlug}/0/`;
|
|
@@ -138,7 +138,7 @@ export async function bundleSelectiveSchemas(
|
|
|
138
138
|
// Bundle function schemas
|
|
139
139
|
for (const funcSlug of deps.functions) {
|
|
140
140
|
const schemaDir = join(schemasDir, "functions", funcSlug);
|
|
141
|
-
const bundled = await
|
|
141
|
+
const bundled = await buildSchemaFromDirectory(schemaDir, { baseUrl });
|
|
142
142
|
|
|
143
143
|
if (bundled.type === "function") {
|
|
144
144
|
const uri = `${baseUrl}/api/v1/function/${funcSlug}/0/`;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
2
4
|
import { expandPresetSchemas } from "@/bundler/presets/index.js";
|
|
3
5
|
import { bundleSchemas } from "./bundle.js";
|
|
4
6
|
|
|
@@ -88,4 +90,96 @@ describe("Bundle Command", () => {
|
|
|
88
90
|
warnSpy.mockRestore();
|
|
89
91
|
});
|
|
90
92
|
});
|
|
93
|
+
|
|
94
|
+
describe("Custom Schema Directory", () => {
|
|
95
|
+
const customSchemasDir = join(process.cwd(), "test-custom-schemas");
|
|
96
|
+
const customTypeDir = join(customSchemasDir, "types", "custom-color");
|
|
97
|
+
|
|
98
|
+
beforeAll(async () => {
|
|
99
|
+
// Create custom schema directory structure
|
|
100
|
+
await mkdir(customTypeDir, { recursive: true });
|
|
101
|
+
|
|
102
|
+
// Create a custom schema.json
|
|
103
|
+
const schemaJson = {
|
|
104
|
+
name: "CustomColor",
|
|
105
|
+
type: "color" as const,
|
|
106
|
+
description: "A custom color type for testing",
|
|
107
|
+
slug: "custom-color",
|
|
108
|
+
schema: {
|
|
109
|
+
type: "object" as const,
|
|
110
|
+
properties: {
|
|
111
|
+
x: { type: "number" as const },
|
|
112
|
+
y: { type: "number" as const },
|
|
113
|
+
},
|
|
114
|
+
required: ["x", "y"],
|
|
115
|
+
additionalProperties: false,
|
|
116
|
+
},
|
|
117
|
+
initializers: [
|
|
118
|
+
{
|
|
119
|
+
keyword: "customcolor",
|
|
120
|
+
script: {
|
|
121
|
+
type: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
122
|
+
script: "./custom-initializer.tokenscript",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
conversions: [] as any[],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
await writeFile(
|
|
130
|
+
join(customTypeDir, "schema.json"),
|
|
131
|
+
JSON.stringify(schemaJson, null, 2),
|
|
132
|
+
"utf-8",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Create a custom initializer script
|
|
136
|
+
const initializerScript = `
|
|
137
|
+
variable x_val: Number = args.get(0);
|
|
138
|
+
variable y_val: Number = args.get(1);
|
|
139
|
+
variable result: Color.CustomColor = (x: x_val, y: y_val);
|
|
140
|
+
result
|
|
141
|
+
`.trim();
|
|
142
|
+
|
|
143
|
+
await writeFile(join(customTypeDir, "custom-initializer.tokenscript"), initializerScript);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
afterAll(async () => {
|
|
147
|
+
// Clean up custom schemas directory
|
|
148
|
+
try {
|
|
149
|
+
await rm(customSchemasDir, { recursive: true, force: true });
|
|
150
|
+
} catch {
|
|
151
|
+
// Ignore if directory doesn't exist
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should bundle schemas from custom directory", async () => {
|
|
156
|
+
const result = await bundleSchemas(["type:custom-color"], customSchemasDir);
|
|
157
|
+
|
|
158
|
+
expect(result.output).toContain("SCHEMAS");
|
|
159
|
+
expect(result.output).toContain("custom-color");
|
|
160
|
+
expect(result.metadata.requestedSchemas).toEqual(["type:custom-color"]);
|
|
161
|
+
expect(result.metadata.resolvedDependencies).toContain("custom-color");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should include custom schema in output", async () => {
|
|
165
|
+
const result = await bundleSchemas(["custom-color"], customSchemasDir);
|
|
166
|
+
|
|
167
|
+
// Verify the output contains the custom schema definition
|
|
168
|
+
expect(result.output).toContain("CustomColor");
|
|
169
|
+
expect(result.output).toContain("customcolor");
|
|
170
|
+
expect(result.output).toContain("A custom color type for testing");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should use custom directory metadata in output", async () => {
|
|
174
|
+
const cliArgs = ["type:custom-color", "--schemas-dir", customSchemasDir];
|
|
175
|
+
const result = await bundleSchemas(["type:custom-color"], customSchemasDir, cliArgs);
|
|
176
|
+
|
|
177
|
+
expect(result.metadata.generatedBy).toContain("--schemas-dir");
|
|
178
|
+
expect(result.metadata.generatedBy).toContain(customSchemasDir);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should fail gracefully when schema not found in custom directory", async () => {
|
|
182
|
+
await expect(bundleSchemas(["type:nonexistent-schema"], customSchemasDir)).rejects.toThrow();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
91
185
|
});
|
|
@@ -20,6 +20,7 @@ export interface BundleOptions {
|
|
|
20
20
|
config?: string;
|
|
21
21
|
output?: string;
|
|
22
22
|
dryRun?: boolean;
|
|
23
|
+
schemasDir?: string; // Comma-separated list of custom schema directories
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -227,11 +228,20 @@ export async function handleBundleCommand(
|
|
|
227
228
|
if (options.dryRun) {
|
|
228
229
|
cliArgs.push("--dry-run");
|
|
229
230
|
}
|
|
231
|
+
if (options.schemasDir) {
|
|
232
|
+
cliArgs.push("--schemas-dir", options.schemasDir);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Use custom schema directory if provided
|
|
236
|
+
const customSchemasDir = options.schemasDir;
|
|
237
|
+
if (customSchemasDir) {
|
|
238
|
+
log.info(`Using custom schema directory: ${customSchemasDir}`);
|
|
239
|
+
}
|
|
230
240
|
|
|
231
241
|
// Bundle schemas
|
|
232
242
|
const { output, metadata, dependencyTree } = await bundleSchemas(
|
|
233
243
|
configSchemas,
|
|
234
|
-
|
|
244
|
+
customSchemasDir,
|
|
235
245
|
cliArgs,
|
|
236
246
|
);
|
|
237
247
|
|
package/src/cli/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ cli
|
|
|
22
22
|
.option("-c, --config <path>", "Path to config file")
|
|
23
23
|
.option("-o, --output <path>", "Output file path", { default: "./tokenscript-schemas.js" })
|
|
24
24
|
.option("-d, --dry-run", "Preview what would be bundled without writing")
|
|
25
|
+
.option("-s, --schemas-dir <path>", "Custom schema directory (overrides default)")
|
|
25
26
|
.action(async (schemas: string[], options: BundleOptions) => {
|
|
26
27
|
try {
|
|
27
28
|
await handleBundleCommand(schemas, options);
|
|
@@ -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
|
-
}
|