|
| 1 | +--- |
| 2 | +title: Schemas |
| 3 | +description: Schema definitions and validation in TokenScript. |
| 4 | +sidebar_label: Schemas |
| 5 | +--- |
| 6 | + |
| 7 | +import TokenScriptCodeBlock from '@site/src/components/TokenScriptCodeBlock'; |
| 8 | + |
| 9 | +# Schemas |
| 10 | + |
| 11 | +Schemas are function like packages to extend tokenscript. |
| 12 | + |
| 13 | +They allow you to ship **custom token logic** along with your **design token data** without having to modify source code. |
| 14 | + |
| 15 | +Schemas use Tokenscript nested in a JSON specification to compute values against given input. |
| 16 | + |
| 17 | +Using Schemas you can add: |
| 18 | + |
| 19 | +**Custom types** |
| 20 | + |
| 21 | +- **Color spaces** (rgb, oklch, etc) |
| 22 | +- **Custom units** (px, rem, etc) |
| 23 | + |
| 24 | +**Custom functions** |
| 25 | + |
| 26 | +- **Functions** (brighten, darken, etc) |
| 27 | + |
| 28 | +## Schema specification |
| 29 | + |
| 30 | +### Color schemas |
| 31 | + |
| 32 | +```jsonc |
| 33 | +{ |
| 34 | + // The name will be used to defined the type inside tokenscript -> Color.Srgb |
| 35 | + "name": "SRGB", |
| 36 | + "description": "SRGB color with three channels: red, green & blue.", |
| 37 | + |
| 38 | + "type": "color", |
| 39 | + |
| 40 | + // Input specification for the data properties stored in the color type. |
| 41 | + // In most cases this will store the values of your color channels. |
| 42 | + "schema": { |
| 43 | + "type": "object", |
| 44 | + // Order is used for the initializer function e.g.: rgb(r, g, b) and pretty printing of the symbol |
| 45 | + "order": ["r", "g", "b"], |
| 46 | + "required": ["r", "g", "b"], |
| 47 | + "properties": { |
| 48 | + "r": { "type": "number" }, |
| 49 | + "g": { "type": "number" }, |
| 50 | + "b": { "type": "number" } |
| 51 | + } |
| 52 | + }, |
| 53 | + |
| 54 | + // Initializers allow to construct the color via a function call |
| 55 | + // E.g.: variable color: Color.Rgb = rgb(255, 255, 255); |
| 56 | + "initializers": [ |
| 57 | + { |
| 58 | + "title": "function", |
| 59 | + "keyword": "srgb", |
| 60 | + "description": "Creates a RGB color from string", |
| 61 | + "script": { |
| 62 | + "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", |
| 63 | + // The script to initialize the color, takes the properties defined in the input schema and stores them on the color symbol |
| 64 | + // Look for minified version below |
| 65 | + "script": "variable color_parts: List = {input}; \n variable output: Color.RGB;\n output.r = color_parts.get(0);\n output.g = color_parts.get(1);\n output.b = color_parts.get(2);\n return output;" |
| 66 | + } |
| 67 | + } |
| 68 | + ], |
| 69 | + |
| 70 | + |
| 71 | + // Conversions are used to define conversion to and from other color types |
| 72 | + // Tokenscript will automatically find a conversion path, so you dont have to define a conversion for every color type. |
| 73 | + // But to enable the a lossless conversion it is important to define the most similar color type for the conversion source and destination. |
| 74 | + "conversions": [ |
| 75 | + { |
| 76 | + // The source schema URI for the |
| 77 | + // This URI will be used as the id for the lookup in the ColorManager |
| 78 | + "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/", |
| 79 | + // Target can reference it self with `$self` |
| 80 | + "target": "$self", |
| 81 | + "description": "Converts HEX to RGB", |
| 82 | + "lossless": true, |
| 83 | + "script": { |
| 84 | + "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", |
| 85 | + // The script to convert from hex to rgb |
| 86 | + // In this case does splitting of the hex string to fill the `Color.Rgb` type. |
| 87 | + "script": "variable color_parts: List = {input}.to_string().split('#'); \n variable color: List = color_parts.get(1).split(); \n variable length: Number = color.length(); \n variable rgb: List = 0, 0, 0; \n if(length == 3) [ \n rgb.update(0, parse_int(color.get(0).concat(color.get(0)), 16)); \n rgb.update(1, parse_int(color.get(1).concat(color.get(1)), 16)); \n rgb.update(2, parse_int(color.get(2).concat(color.get(2)), 16)); \n ] else [ \n rgb.update(0, parse_int(color.get(0).concat(color.get(1)), 16)); \n rgb.update(1, parse_int(color.get(2).concat(color.get(3)), 16)); \n rgb.update(2, parse_int(color.get(4).concat(color.get(5)), 16)); \n ]; \n \n variable output: Color.RGB; \n output.r = rgb.get(0); \n output.g = rgb.get(1); \n output.b = rgb.get(2); \n \n return output; \n" |
| 88 | + } |
| 89 | + }, |
| 90 | + // The Reverse of hex to rgb conversion |
| 91 | + { |
| 92 | + "source": "$self", |
| 93 | + "target": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/", |
| 94 | + "description": "Converts RGB to HEX", |
| 95 | + "lossless": true, |
| 96 | + "script": { |
| 97 | + "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", |
| 98 | + "script": "variable rgba: List = {input}.r, {input}.g, {input}.b;\n variable hex: String = \"#\";\n variable i: Number = 0;\n variable value: Number = 0;\n // Convert RGBA to Hex\n while( i < min(rgba.length(), 3)) [\n value = rgba.get(i);\n if(value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n ] else [\n hex = hex.concat(value.to_string(16));\n ];\n i = i + 1;\n ];\n \n if (rgba.length() == 4) [\n value = rgba.get(3) * 255; // Convert alpha to 0-255 range\n if(value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n ] else [\n hex = hex.concat(value.to_string(16));\n ];\n ];\n \n return hex;" |
| 99 | + } |
| 100 | + } |
| 101 | + ] |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +#### Initializers |
| 106 | + |
| 107 | +Initializers allow to construct a color via a function call. |
| 108 | + |
| 109 | +<TokenScriptCodeBlock mode="script" showResult={true}> |
| 110 | +{`srgb(255, 255, 255);`} |
| 111 | +</TokenScriptCodeBlock> |
| 112 | + |
| 113 | +This would call our initializer function with the input of `[255, 255, 255]`. |
| 114 | + |
| 115 | +<TokenScriptCodeBlock mode="script" showResult={true} input={[255, 255, 255]}> |
| 116 | +{`// Get the input reference |
| 117 | +variable color_parts: List = {input}; |
| 118 | + |
| 119 | +// Set the channels to the values from the input list |
| 120 | +variable output: Color.SRGB; |
| 121 | +output.r = color_parts.get(0); |
| 122 | +output.g = color_parts.get(1); |
| 123 | +output.b = color_parts.get(2); |
| 124 | + |
| 125 | +// Return the constructed color |
| 126 | +return output; `} |
| 127 | +</TokenScriptCodeBlock> |
| 128 | + |
| 129 | +#### Conversions |
| 130 | + |
| 131 | +Conversions are scripts to convert from a `source` schema type and to a `target` schema type. |
| 132 | + |
| 133 | +Use `$self` to target the current schema. |
| 134 | + |
| 135 | +A type can be converted via `x.to.typename()` |
| 136 | + |
| 137 | +<TokenScriptCodeBlock mode="script" showResult={true} input={"#eb6fb0"}> |
| 138 | +{`// Input #eb6fb0 |
| 139 | + |
| 140 | + // Convert the input to a string and split the hex symbol |
| 141 | +variable color_parts: List = {input}.to_string().split('#'); |
| 142 | + |
| 143 | + // Split the hex string into channel parts |
| 144 | + variable color: List = color_parts.get(1).split(); |
| 145 | + |
| 146 | + // Parse either 3 part or 6 part hex strings |
| 147 | + variable rgb: List; |
| 148 | + if (color.length() == 3) [ |
| 149 | + rgb = parse_int(color.get(0).concat(color.get(0)), 16), |
| 150 | + parse_int(color.get(1).concat(color.get(1)), 16), |
| 151 | + parse_int(color.get(2).concat(color.get(2)), 16); |
| 152 | + ] else [ |
| 153 | + rgb = parse_int(color.get(0).concat(color.get(1)), 16), |
| 154 | + parse_int(color.get(2).concat(color.get(3)), 16), |
| 155 | + parse_int(color.get(4).concat(color.get(5)), 16); |
| 156 | + ]; |
| 157 | + |
| 158 | + // Assemble the output type |
| 159 | + variable output: Color.Srgb; |
| 160 | + output.r = rgb.get(0); |
| 161 | + output.g = rgb.get(1); |
| 162 | + output.b = rgb.get(2); |
| 163 | + |
| 164 | + return output; |
| 165 | +`} |
| 166 | +</TokenScriptCodeBlock> |
| 167 | + |
| 168 | +### Function schemas |
| 169 | + |
| 170 | +```json |
| 171 | +{ |
| 172 | + "name": "Invert Color", |
| 173 | + "description": "Inverts a color by inverting each RGB channel (R' = 1 - R, G' = 1 - G, B' = 1 - B), preserving the alpha channel.", |
| 174 | + |
| 175 | + "type": "function", |
| 176 | + |
| 177 | + // The keyword by which the function can be called from: `invert(#333)` |
| 178 | + "keyword": "invert", |
| 179 | + |
| 180 | + // The input arguments with the specified type |
| 181 | + "input": { |
| 182 | + "type": "object", |
| 183 | + "properties": { |
| 184 | + "color": { |
| 185 | + "type": "color", |
| 186 | + "description": "The color to invert." |
| 187 | + } |
| 188 | + } |
| 189 | + }, |
| 190 | + // Script that will be executed on call |
| 191 | + "script": { |
| 192 | + "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", |
| 193 | + "script": "variable input: List = {input};\nvariable baseColor: Color = input.get(0);\n\nvariable rgbColor: Color.Srgb = baseColor.to.srgb();\n\nvariable invertedR: Number = 255 - rgbColor.r;\nvariable invertedG: Number = 255 - rgbColor.g;\nvariable invertedB: Number = 255 - rgbColor.b;\n\nvariable invertedColor: Color.Srgb = srgb(invertedR, invertedG, invertedB);\nreturn invertedColor;" |
| 194 | + }, |
| 195 | + // Schema dependencies, these will have to be set up in the Configuration for the function to work. |
| 196 | + "requirements": [ |
| 197 | + "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/schema/srgb-color/0.1.0/" |
| 198 | + ] |
| 199 | +} |
| 200 | +``` |
| 201 | + |
| 202 | +#### Script |
| 203 | + |
| 204 | +<TokenScriptCodeBlock mode="script" showResult={true} input={["#FFF"]}> |
| 205 | +{`// Input #FFF |
| 206 | +variable input: List = {input}; |
| 207 | +variable baseColor: Color = input.get(0); |
| 208 | + |
| 209 | +variable rgbColor: Color.Srgb = baseColor.to.srgb(); |
| 210 | + |
| 211 | +variable invertedR: Number = 255 - rgbColor.r; |
| 212 | +variable invertedG: Number = 255 - rgbColor.g; |
| 213 | +variable invertedB: Number = 255 - rgbColor.b; |
| 214 | + |
| 215 | +variable invertedColor: Color.Srgb = srgb(invertedR, invertedG, invertedB); |
| 216 | +return invertedColor;`} |
| 217 | +</TokenScriptCodeBlock> |
0 commit comments