design-token-library 0.1.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -5,7 +5,85 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.3.0] - 2026-11-03
9
+
10
+ ### Added
11
+
12
+ - `configuredValueOf(token)` is a token's value as configured, or as last given to `set()`, with its refs and computed values unresolved (#106). A token of an extended library that isn't given a value has its source token's. `isComputed(value)` tells whether a value is a recipe's result, also one made by another copy of the package, as `isRef()` does for a ref. A tool tells a given token from a derived one by whether its configured value has a ref or a computed value at any depth, and a converter reads a ref in a value to write it as a `var()`.
13
+ - `dependenciesOf(token)` is the tokens a token's value refers to, through refs anywhere in its configured value, including the arguments of its recipes, in the token's own library (#106).
14
+ - `entriesOf(library)` lists the tokens and groups of a library, each with its path, in the order of the config, for a tool that visits every token without restating the library's rules for what is a token or a group (#105). It walks as a DOM `TreeWalker` does: `whatToShow` is `"token"`, `"group"` or `"all"`, and a `filter` leaves an entry out, or `"reject"` leaves out a group and what is under it.
15
+ - `dependentsOf(token)` is the tokens whose configured values refer to a token, directly, in the token's own library: the reverse of `dependenciesOf()` (#110). It reads no token's value, so a tool finds what a `set()` may change, for an extension that hasn't been read, without reading the library. The index is made once for a library and again after a token is set to a different value.
16
+ - A CSS converter can return a record to write a token as more than one custom property, such as a value for each state of a component or a property for each field of a typography (#64). A key is added to the name of the token's property with `-`, and `""` is the token's own: `{ "": rest, hover }` for `fill` writes `--fill` and `--fill-hover`. A string is the same as `{ "": value }`, and an empty record writes nothing. `toProperties()` has a `{ var, property }` for each key, at `properties.fill.hover`, typed by the keys a converter returns when its return type is inferred; so it calls a converter it's given, and reads the values of those tokens. Adds the `CSSValue`, `CSSValues`, `CSSPropertiesOf` and `PropertiesConverters` types. `ToPropertiesOptions` and `CSSPropertiesLibrary` take the converters as a type parameter, with a default.
17
+
18
+ ### Changed
19
+
20
+ - An extension makes its tokens when they are first read, or up front for those its config gives, rather than one for every token of the library when `extend()` is called (#113). The library an extension extends holds it weakly, so an extension that nothing refers to is collected, with its subscribers. With 600 tokens, `extend({})` takes about a fifth of the time, and 200 dropped extensions keep 0.1 MB rather than 93 MB. Walking an extension with `entriesOf()`, writing its CSS or `dependentsOf()` still makes all of its tokens.
21
+ - **Breaking:** `CSSConverter` returns `CSSValue` (`string | CSSValues`) rather than `string`. A converter that returns a string is still one, but code that calls a value typed `CSSConverter` gets the union (#64).
22
+ - `package.json` sets `"sideEffects": false` so bundlers can tree-shake unused exports from the package.
23
+ - Package entry uses explicit named exports instead of `export *`. Exported `ConvertersOf`, `StandardConverters`, and `CSSPropertyValues` so the public CSS types resolve without forgotten-export errors.
24
+ - Published typings (`types` / `exports.types`) come from the API Extractor public `.d.ts` rollup at `dist/lib/public.d.ts` (generated at the end of `npm run build`). `dist/lib/index.d.ts` stays the TypeScript barrel used as the extractor entry point.
25
+ - A ref resolves to its token's value, which is resolved and frozen already, rather than to a copy of it. A recipe whose argument refers to a large value, like a palette of many colors, no longer copies it each time the recipe is computed.
26
+
27
+ ## [0.2.0] - 2026-10-28
28
+
29
+ ### Added
30
+
31
+ - `isRef(value)` tells whether a value is a ref, also one made by another copy of the package, and `pathOf(ref)` is the path of its token, as a readonly array of names (#93). An add-on that keeps a ref, instead of letting the library resolve it to its token's value, needs them, as an expression that writes a token as a `var()` does. `isRef()` narrows a value to a `Library.Ref<unknown, never>`, which a ref of any library is.
32
+ - `ref("colors.primary")` and `refs<T>()` make a ref to a token, by its dotted path or by a ref for each token of a shape: `$value: ref("colors.primary")` or `$value: $.colors.primary`. They make the same ref, which is data, the path of its token, and has the token's value type. `ref()` takes its types from where it's used, so a path that isn't a token, or a token of the wrong type, is a compile error. Adds the `Library.Ref`, `Library.Refs`, `Library.TokenValues`, `Library.TokenValue`, `Library.TokenPath` and `Library.DeepValue` types.
33
+ - A custom value can be any type, including a class instance, an object with methods or function properties, or a function, and the library never calls its functions: `DesignToken.Custom<"palette", Palette>`. `DesignToken.HasFunction`, which rejected a value type with a function in it, is removed.
34
+ - `recipes({ ... })` makes a recipe of each function of an object, named by its key. A recipe takes its function's arguments as values, refs, computed values, or values with them in them, and returns a computed value, as in `$value: Recipes.darken(ref("colors.primary"), 0.2)`. A computed value is data: the recipe and its arguments. The function is called when the token is read, and again when a token it read changes. It gets its arguments as tokens get their values, so a ref's value and a copy of plain data are frozen, and its parameters are typed readonly, as in `DesignToken.Values.Readonly.Color`. It may return a readonly value, which is copied and frozen like any token value. A recipe whose function takes an object builds a class value from named fields. A set of recipes is extended by spreading it. Adds the `Library.Computed`, `Library.Recipe`, `Library.RecipeArguments` and `Library.Recipes` types.
35
+ - `extend(config, { recipes: [override(recipe, fn)] })` replaces a recipe's function in the extended library, for every token that uses it, including the ones it doesn't restate. `override()` checks that `fn` takes the recipe's arguments and returns its result's type. Adds the `Library.ExtendOptions` and `Library.RecipeOverride` types.
36
+ - A custom type's converter in `toCSS` can be `false`, which leaves tokens of that type out of the CSS (#58). `toProperties` accepts the same `converters` option and leaves out those tokens. Every custom type still needs an entry, so a forgotten converter is still an error. Adds the `CSSConverterEntries` and `ToPropertiesOptions` types.
37
+ - `convert(type, value)` converts a value of a standard type to CSS, the way `toCSS` does for a token of the type, for a converter of a custom type that has values of standard types (#65). `DesignToken.ValueByType`, the value of each standard type keyed by its name, like `color`, is public.
38
+ - `toCSS` takes a converter for a standard type in `converters`, which is used in place of the one of the library (#65). A library without custom types takes converters for the standard types, and no others.
39
+ - `$root` as a token in its group, and not at the top level of a library (its CSS custom property is named for the group), `$deprecated` on tokens, and group metadata (`$description`, `$extensions`, `$deprecated`) that survives `extend()`. A circular reference throws a `CircularReferenceError`, whose `chain` and message list the tokens in the cycle (`a → b → a`), instead of overflowing the stack.
40
+ - Custom token types via `DesignToken.Custom<Name, Value>`, with static values, aliases, deep aliases, group type inheritance, `set()` and `extend()` support. Reserved DTCG type names are rejected at compile time.
41
+ - `toCSS(library, options)` accepts `options.converters`, a converter for each custom type that receives the value and the token. It's required for every custom type in the library. At runtime `toCSS` throws for a custom type without a converter.
42
+ - `Library.TokensOf`, `Library.TokenRecord`, `Library.GroupType`, `CSSOptions`, `CSSConverter`, `CSSConverters` and `CustomTokensOf` types.
43
+ - `Token.toString()` returns a JSON representation of the token's `name`, `$type`, resolved `$value`, `$description`, `$extensions` and `$deprecated`. It throws a `CircularReferenceError` for a token in a cycle. It was declared but stringified as `[object Object]`.
44
+
45
+ ### Changed
46
+
47
+ - **Breaking:** a token's value is deeply readonly (#24). It's frozen, including nested objects, arrays and their items, and `Token.$value` is typed `DesignToken.ReadonlyValueByToken`, so subscriber records and converter arguments are readonly too. `convert()` and the converters for the standard types take the readonly values, and `set()` takes them as well, so `set(other.$value)` compiles. The config passed to `create()` isn't frozen, and a class instance or function in a value isn't copied, so it isn't frozen. Adds the `DesignToken.Values.Readonly` types, `DesignToken.ReadonlyValueByType` and `DesignToken.DeepReadonly`.
48
+ - **Breaking:** an alias is a ref, `$value: ref("colors.primary")`, instead of a function given the library's tokens, and a function in a value is never called. `Library.Context`, `Library.ValueAlias`, `Library.Alias`, `Library.ValueSource` and `Library.DeepAlias` are removed, and `Token.set()` takes the same values as a config, including deep refs.
49
+ - A token's value is a copy of plain data, made from its own properties: symbol-keyed and non-enumerable properties are kept, an object that is in the value twice, or in itself, has one copy, and a getter is read once into a data property. Plain data is an array that isn't a subclass of `Array`, or an object whose prototype chain has at most one link, including an object literal from another realm. Anything else, including a function, is returned as it is.
50
+ - **Breaking:** token values use the [DTCG 2025.10](https://www.designtokens.org/tr/2025.10/format/) shapes. A color is `{ colorSpace, components, alpha?, hex? }`, a dimension is `{ value, unit: "px" | "rem" }` and a duration is `{ value, unit: "ms" | "s" }`. A shadow may be an array of layers and may be `inset`, the font weight keyword `"smi-bold"` is corrected to `"semi-bold"`, and a list inside a value (such as a stroke style's dash array) can contain aliases.
51
+ - `toCSS` and `toProperties` convert the new shapes: sRGB colors become hex and other color spaces the CSS color function, font weight keywords become numbers, `cubic-bezier()` and transition shorthand order are corrected, and a standard type with no converter is written as JSON instead of `[object Object]`.
52
+ - **Breaking:** tokens and groups use the DTCG property names: `$value`, `$type`, `$description`, `$extensions` and `$deprecated` on tokens, and `$type`, `$description`, `$extensions` and `$deprecated` on groups, both in a config and on `library.tokens`. `name`, `set()` and `toString()` are unchanged. A token is told apart from a group by having a `$value`.
53
+ - Group type inheritance is checked at compile time: a token may omit `type` in a config only when its nearest ancestor group declares a single, required, literal `type` that matches. Groups declared with `type: DesignToken.Type`, a union, or an optional `type` no longer let their tokens omit `type`.
54
+ - `type` is required on standard token types (e.g. `DesignToken.Color`). It may still be omitted in a `Library.Config` when inherited.
55
+ - Aliases are typed by value: an alias may return any value, or any token, whose value matches. Replaces the internal `TokenByValue`, `TokenByType` and `ValueByType` types.
56
+ - `Library.Subscriber` records are a union of the library's tokens, discriminated by `type`, instead of `Token<DesignToken.Any>`.
57
+ - Token values that are plain objects with a `value` key are no longer unwrapped as tokens; only library tokens are.
58
+ - Array values resolve faster: they are copied with an indexed loop instead of `for...in`. Holes in a sparse array value now resolve to `undefined` elements.
59
+ - Recomputing a token keeps its subscriptions to the dependencies it reads again, instead of unsubscribing from all of them and subscribing again, which makes updates faster.
60
+ - `Library.extend()` config is typed with the new `Library.ExtendConfig` and its type parameter defaults to `{}` instead of `any`.
61
+
62
+ ### Fixed
63
+
64
+ - A token's value, which is readonly, can be the `$value` of a token in `create()` or `extend()`, and the argument of a recipe, as it can be given to `set()` (#92). A value whose objects or arrays are readonly is accepted too, also with refs in it. `Library.DeepValue` makes the properties and elements of a value readonly.
65
+ - `set()` on a token of a library that extends an extended library changes that token, and not the token of the library it extends (#91).
66
+ - `Token.set()` with the value the token already has, by `Object.is`, doesn't notify subscribers or resolve the token again (#22). On an extended token it still detaches the token from its source.
67
+ - `extend()` throws, naming the token, when an override's `$type` differs from the source token's type, including a type the source token inherits from its group (#16). Restating the same type is allowed.
68
+ - `extend()` accepts a group's `$type` when it's the group's own type, so a shared group config that sets it can be given to `extend()`, and throws, naming the group, when it's different (#95). A different group type was ignored, and restating the same one was a compile error.
69
+ - A value that isn't plain data, such as a class instance, a `Date`, a `Map` or a `Set`, is returned as it is. It was copied to a plain object, which lost its prototype and internal state, and a function property of it was called as an alias (#55). A cyclic value no longer overflows the stack.
70
+ - `extend()` has an overload for a config that only has values of tokens, which has no tokens to infer (#62). A new token with a value of the wrong type is reported for the call, as no overload matching, instead of for the token.
71
+ - A custom token type with a value type that contains itself, like a tree, compiles (#61).
72
+ - `DeepAlias` accepts aliases inside Gradient stops (#28).
73
+ - Resolving a token of an extended library no longer disconnects its source token from its dependencies, which left the source token reported as changed when a dependency it no longer used changed.
74
+ - Number aliases in a CubicBezier accept Number tokens (#29).
75
+ - `Library.extend()` no longer throws when the source library contains groups.
76
+ - `Library.extend()` supports adding new groups, and new tokens inherit the type of their ancestor group.
77
+ - `Library.extend()` accepts alias values when overriding existing tokens, and resolves them against the extending library.
78
+ - `Library.extend()` throws when a new token has no type and no ancestor group type, matching `Library.create()`.
79
+ - `Library.extend()` shallow-merges `$extensions` overrides into the source token's, with override keys winning. An extended token's `$extensions` is always a new object, so mutating it no longer changes the source token.
80
+ - Library groups expose their declared `type` (non-enumerable), matching the `TokenLibrary` type.
81
+ - `Library.create()` and `extend()` configs accept `null` and `undefined` entries, which are ignored at runtime like other non-token entries.
82
+ - The root of `library.tokens` (for created and extended libraries) and the object returned by `toProperties` are frozen, like nested groups. Adding or deleting a top-level key throws in strict mode.
83
+ - A token's `$extensions` is a shallow copy of the config's at `Library.create()`, so adding, removing or replacing a top-level key in the config no longer changes the token. Nested values are shared with the config.
84
+ - Array entries in `Library.create()` and `extend()` configs are ignored like other non-token entries, instead of creating an empty group (which `toProperties` also emitted).
85
+ - A library subscriber that throws no longer stops later subscribers from being notified. Every subscriber is called, then each error is re-thrown as the same instance in its own microtask, so it surfaces as an uncaught error, like a DOM event listener's.
86
+ - Tokens in a config whose shape is inferred (no type argument to `Library.create()` or `extend()`) are checked against their `$type`, and their `$value` and `set()` use that type's value instead of the widened literal. Their `$type` keeps its literal type, including a custom type's name. A token with a child (a key without a `$` prefix), which `create()` throws for, is a type error. Custom types and tokens that inherit a group's type are not checked. `Library.ConfigValueCheck` and `Library.ConfigTypeHint` are added. Requires TypeScript 5.4 or later (`NoInfer`).
9
87
 
10
88
  ## [0.1.0] - 2023-05-16
11
89
 
package/README.md CHANGED
@@ -1,4 +1,40 @@
1
- ![CI](https://github.com/nicholasrice/design-token-library/actions/workflows/ci.yml/badge.svg)
2
- ![Docs](https://github.com/nicholasrice/design-token-library/actions/workflows/deploy.yml/badge.svg)
1
+ # design-token-library
3
2
 
4
- [API Documentation](https://nicholasrice.github.io/design-token-library/)
3
+ [![CI](https://github.com/nicholasrice/design-token-library/actions/workflows/ci.yml/badge.svg)](https://github.com/nicholasrice/design-token-library/actions/workflows/ci.yml)
4
+ [![Docs](https://github.com/nicholasrice/design-token-library/actions/workflows/deploy.yml/badge.svg)](https://github.com/nicholasrice/design-token-library/actions/workflows/deploy.yml)
5
+
6
+ A TypeScript library for managing [design tokens](https://www.designtokens.org/). The API follows the [Design Token Community Group (DTCG)](https://www.designtokens.org/) format and focuses on type safety, lazy evaluation, and runtime-agnostic behavior.
7
+
8
+ ## Features
9
+
10
+ - Token aliasing and refs with compile-time path checking
11
+ - Computed values (recipes) and library extension
12
+ - Change subscription and CSS / custom-property export
13
+ - [DTCG 2025.10](https://www.designtokens.org/tr/2025.10/format/) value shapes
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install design-token-library
19
+ ```
20
+
21
+ Requires **Node.js 22+** (see `engines` in `package.json`).
22
+
23
+ ## Documentation
24
+
25
+ - [Getting started & API reference](https://nicholasrice.github.io/design-token-library/) (GitHub Pages)
26
+ - [Changelog](./CHANGELOG.md)
27
+
28
+ ## Development
29
+
30
+ ```bash
31
+ npm ci
32
+ npm run build
33
+ npm test
34
+ ```
35
+
36
+ Contributing guidelines, changelog conventions, and the release process are documented in [CONTRIBUTING.md](./CONTRIBUTING.md).
37
+
38
+ ## License
39
+
40
+ [MIT](./LICENSE)
@@ -1,24 +1,245 @@
1
1
  import { DesignToken } from "./design-token.js";
2
2
  import { Library } from "./library.js";
3
3
  /**
4
+ * The values of the custom properties of a token, by what is added to the
5
+ * name of the token's property with `-`: `""` is the token's own property,
6
+ * and `hover` is `--fill-hover` for a token named `fill`.
7
+ *
4
8
  * @public
5
9
  */
6
- export declare function toCSS(library: Library.Library<any, any>): string;
7
- interface CSSPropertyValues {
10
+ export type CSSValues = {
11
+ readonly [suffix: string]: string;
12
+ };
13
+ /**
14
+ * What a {@link CSSConverter} returns: the value of the token's custom
15
+ * property, or {@link CSSValues} for a token written as more than one.
16
+ *
17
+ * @public
18
+ */
19
+ export type CSSValue = string | CSSValues;
20
+ /**
21
+ * Converts a token value to a CSS value.
22
+ *
23
+ * @remarks
24
+ * A string is the value of the token's custom property, and is the same as
25
+ * `{ "": value }`. A converter returns {@link CSSValues} to write a token as
26
+ * more than one property, such as a value for each state of a component, or
27
+ * a typography value as a property for each of its fields. An empty record
28
+ * writes none.
29
+ *
30
+ * @public
31
+ */
32
+ export type CSSConverter<V, T extends DesignToken.Shape = DesignToken.Shape> = (value: V, token: Library.Token<T, {}>) => CSSValue;
33
+ /**
34
+ * A union of the custom tokens in a library of shape `T`: tokens whose
35
+ * type is not defined by {@link https://www.designtokens.org/tr/2025.10/format/#types}.
36
+ *
37
+ * @public
38
+ */
39
+ export type CustomTokensOf<T> = Exclude<Library.TokensOf<T>, {
40
+ $type?: DesignToken.Type;
41
+ }>;
42
+ /**
43
+ * A {@link CSSConverter} for each custom token type in a library of shape `T`,
44
+ * keyed by type name.
45
+ *
46
+ * @public
47
+ */
48
+ export type CSSConverters<T> = ConvertersOf<CustomTokensOf<T>>;
49
+ /**
50
+ * @public
51
+ */
52
+ export type ConvertersOf<C> = {
53
+ [N in C extends {
54
+ $type: infer N extends string;
55
+ } ? N : never]: C extends {
56
+ $type: N;
57
+ } ? C extends DesignToken.Shape ? CSSConverter<DesignToken.ReadonlyValueByToken<C>, C> : never : never;
58
+ };
59
+ /**
60
+ * A converter for any of the standard types, which is used in place of the one
61
+ * of the library.
62
+ *
63
+ * @public
64
+ */
65
+ export type StandardConverters = {
66
+ [N in keyof DesignToken.ReadonlyValueByType]?: CSSConverter<DesignToken.ReadonlyValueByType[N], Extract<DesignToken.Standard, {
67
+ $type: N;
68
+ }>>;
69
+ };
70
+ /**
71
+ * What a library of shape `T` accepts for each custom token type: a
72
+ * {@link CSSConverter}, or `false` for a type that is not serialized.
73
+ *
74
+ * @public
75
+ */
76
+ export type CSSConverterEntries<T> = {
77
+ [N in keyof CSSConverters<T>]: CSSConverters<T>[N] | false;
78
+ };
79
+ /**
80
+ * Options that control how custom properties are named.
81
+ *
82
+ * @public
83
+ */
84
+ export interface CSSPropertiesOptions {
85
+ /**
86
+ * Derives a token's custom property name, without the leading `--`.
87
+ *
88
+ * @remarks
89
+ * Defaults to the token's name with `.` replaced by `-`. Pass the same
90
+ * function to {@link toCSS} and {@link toProperties} to keep the names
91
+ * aligned.
92
+ */
93
+ name?(token: Library.Token<any, any>): string;
94
+ }
95
+ /**
96
+ * The converters that {@link toProperties} takes, by type.
97
+ *
98
+ * @public
99
+ */
100
+ export type PropertiesConverters = {
101
+ readonly [type: string]: ((value: any, token: any) => CSSValue) | false;
102
+ };
103
+ /**
104
+ * Options accepted by {@link toProperties}.
105
+ *
106
+ * @public
107
+ */
108
+ export interface ToPropertiesOptions<C extends PropertiesConverters = PropertiesConverters> extends CSSPropertiesOptions {
109
+ /**
110
+ * The converters given to {@link toCSS}. A token whose custom type is `false`
111
+ * isn't written by `toCSS`, so it has no custom property and is left out.
112
+ * A token whose converter returns {@link CSSValues} has a property for
113
+ * each of its keys.
114
+ */
115
+ converters?: C;
116
+ }
117
+ /**
118
+ * Options accepted by {@link toCSS}.
119
+ *
120
+ * @remarks
121
+ * `converters` is required when the library contains custom token types,
122
+ * and must have an entry for each of them: a converter, or `false` for a type
123
+ * whose tokens are not written to CSS.
124
+ * It can have a converter for a standard type as well, which is used in place
125
+ * of the one of the library, see {@link convert} for what it does.
126
+ *
127
+ * @public
128
+ */
129
+ export type CSSOptions<T> = CSSPropertiesOptions & ([CustomTokensOf<T>] extends [never] ? {
130
+ converters?: StandardConverters;
131
+ } : {
132
+ converters: CSSConverterEntries<T> & StandardConverters;
133
+ });
134
+ /**
135
+ * The trailing arguments of {@link toCSS}: {@link CSSOptions} are
136
+ * required when the library contains custom token types.
137
+ *
138
+ * @public
139
+ */
140
+ export type ToCSSArgs<T> = [CustomTokensOf<T>] extends [never] ? [options?: CSSOptions<T>] : [options: CSSOptions<T>];
141
+ /**
142
+ * Convert a library to CSS custom property declarations.
143
+ *
144
+ * @remarks
145
+ * Libraries with custom token types must provide a converter for each
146
+ * custom type, or `false` to leave tokens of that type out of the CSS.
147
+ * A converter for a standard type is used in place of the one of the library.
148
+ *
149
+ * @public
150
+ */
151
+ export declare function toCSS<T extends {}, R extends {}>(library: Library.Library<T, R>, ...[options]: ToCSSArgs<NoInfer<T>>): string;
152
+ /**
153
+ * @public
154
+ */
155
+ export interface CSSPropertyValues {
8
156
  readonly var: `var(--${string})`;
9
157
  readonly property: `--${string}`;
10
158
  }
11
159
  /**
12
- * A collection of {@link @CSSPropertyValues} that mirrors the
13
- * structure of the source {@link Library.Library<any> | Library}.
160
+ * The {@link CSSPropertyValues} of a token of shape `T`, given the
161
+ * converters `C`: one for its property, or one for each key of the
162
+ * {@link CSSValues} its converter returns, where `""` is the token's own.
163
+ *
164
+ * @remarks
165
+ * The keys are known when the return type of the converter is inferred.
166
+ * When it isn't, such as for a converter typed {@link CSSConverter}, the
167
+ * token has the property of its own in the type, and the others at runtime.
168
+ *
14
169
  * @public
15
170
  */
16
- export type CSSPropertiesLibrary<T extends {}> = {
17
- [K in keyof Readonly<T>]: T[K] extends DesignToken.Any ? CSSPropertyValues : K extends "type" ? DesignToken.Type : T[K] extends {} ? CSSPropertiesLibrary<T[K]> : never;
171
+ export type CSSPropertiesOf<T, C = {}> = T extends {
172
+ $type: infer N extends keyof C;
173
+ } ? C[N] extends (...args: any[]) => infer V ? V extends string ? CSSPropertyValues : string extends keyof V ? CSSPropertyValues : ("" extends keyof V ? CSSPropertyValues : {}) & {
174
+ readonly [P in Exclude<keyof V, "">]: CSSPropertyValues;
175
+ } : CSSPropertyValues : CSSPropertyValues;
176
+ /**
177
+ * A collection of {@link CSSPropertyValues} (`var` and `property` per token) that mirrors the
178
+ * structure of the source {@link (Library:namespace).Library}.
179
+ *
180
+ * @remarks
181
+ * `C` is the converters given to {@link toProperties}, which name the
182
+ * properties of a token written as more than one, see {@link CSSPropertiesOf}.
183
+ *
184
+ * @public
185
+ */
186
+ export type CSSPropertiesLibrary<T extends {}, C = {}> = {
187
+ [K in keyof Readonly<T>]: T[K] extends DesignToken.Shape ? CSSPropertiesOf<T[K], C> : K extends keyof DesignToken.Group ? T[K] : T[K] extends {} ? CSSPropertiesLibrary<T[K], C> : never;
18
188
  };
19
189
  /**
20
- * Construct an {@link CSSPropertiesLibrary} from a {@link Library.Library<any>}.
190
+ * Construct an {@link CSSPropertiesLibrary} from a {@link (Library:namespace).Library}.
191
+ *
192
+ * @remarks
193
+ * Pass the same `converters` given to {@link toCSS} to leave out the tokens it does
194
+ * not write (a custom type whose converter is `false`). They are still in the
195
+ * {@link CSSPropertiesLibrary} type, but missing at runtime.
196
+ *
197
+ * A token whose type has a converter in `converters` has a property for each
198
+ * key of the {@link CSSValues} it returns, so this reads the token's value to
199
+ * call it. Its own property is `var` and `property`, as for any token, and
200
+ * the one for `hover` is at `hover`, so a converter's keys can't be `var` or
201
+ * `property`.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * const properties = toProperties(library, {
206
+ * converters: {
207
+ * states: (value: States) => ({
208
+ * "": convert("color", value.rest),
209
+ * hover: convert("color", value.hover),
210
+ * }),
211
+ * },
212
+ * });
213
+ * properties.fill.var; // "var(--fill)"
214
+ * properties.fill.hover.var; // "var(--fill-hover)"
215
+ * ```
216
+ *
217
+ * @public
218
+ */
219
+ export declare function toProperties<T extends Library.Library<any>, C extends PropertiesConverters = {}>(library: T, options?: ToPropertiesOptions<C>): CSSPropertiesLibrary<T["tokens"], C>;
220
+ /**
221
+ * Converts a value of a standard type to CSS, the way {@link toCSS} does for a
222
+ * token of the type, without a converter for the type in `converters`.
223
+ *
224
+ * @remarks
225
+ * A converter for a custom type that has values of standard types uses it for
226
+ * each of them, and a converter for a standard type can use it for what it
227
+ * keeps.
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * toCSS(library, {
232
+ * converters: {
233
+ * elevation: (value) =>
234
+ * `${convert("dimension", value.offset)} ${convert("color", value.color)}`,
235
+ * },
236
+ * });
237
+ * ```
238
+ *
239
+ * @param type - The type, like `"color"`
240
+ * @param value - The value of a token of the type
241
+ * @returns The value as it is written to CSS
242
+ *
21
243
  * @public
22
244
  */
23
- export declare function toProperties<T extends Library.Library<any>>(library: T): CSSPropertiesLibrary<T["tokens"]>;
24
- export {};
245
+ export declare function convert<Name extends keyof DesignToken.ReadonlyValueByType>(type: Name, value: DesignToken.ReadonlyValueByType[Name]): string;