Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | 4x 4x 29x 4x 48x 48x 35x 4x 29x 29x 4x 24x 24x 48x 48x 48x 48x 15x 15x 48x 14x 15x 15x 15x 24x | import type { TransformedToken } from 'style-dictionary/types';
import { MODE_EXTENSION_KEY, DENSITY_EXTENSION_KEY } from './constants.js';
/**
* A "variant axis" is a token dimension expressed as sibling values under a
* dedicated `$extensions` namespace: one implicit default (the token's own
* `$value`) plus one or more authored variants.
*
* The two axes are namespaced separately because they mean different things:
*
* - color scheme (`com.salesforce-ux.mode`): `light` / `dark` are environment-
* selected peers (`prefers-color-scheme`), so neither is a product "default"
* — `markDefault` is false and no `default` flag is emitted.
* - density (`com.salesforce-ux.density`): `comfy` is the genuine default and
* `compact` an explicit opt-in, so `markDefault` is true and the `comfy`
* entry carries `default: true`.
*
* A token is a color or a dimension, never both, so a given token participates
* in exactly one axis.
*/
type VariantAxis = {
/** `$extensions` namespace key */
key: string;
/** Name of the default sub-key, sourced from the resolved `$value` */
defaultName: string;
/** Names of the authored variant sub-keys, in emit order */
variantNames: string[];
/** Whether the default sub-key should carry an explicit `default: true` */
markDefault: boolean;
};
export const VARIANT_AXES: VariantAxis[] = [
{ key: MODE_EXTENSION_KEY, defaultName: 'light', variantNames: ['dark'], markDefault: false },
{ key: DENSITY_EXTENSION_KEY, defaultName: 'comfy', variantNames: ['compact'], markDefault: true },
];
/**
* One variant entry: the resolved concrete value under `$value`, plus optional
* metadata:
* - `reference` — the original alias when the source authored one; omitted for
* entries whose source value was a literal (e.g. density `compact` is
* authored as `0.125rem`, not an alias).
* - `default` — `true` on the density default (`comfy`), the value the token's
* own `$value` resolves to. Only the density axis marks a default; color
* scheme (`light`/`dark`) never carries this flag.
*/
type VariantEntry = { $value: unknown; reference?: string; default?: true };
type AxisBlock = Record<string, VariantEntry>;
/** A DTCG alias reference is a bare `{token.path}` string. */
const asReference = (value: unknown): string | undefined =>
typeof value === 'string' && /^\{[^{}]+\}$/.test(value) ? value : undefined;
/**
* Read a variant value for one axis, preferring the resolved extension (post
* value transforms) and falling back to the original source extension — so the
* helper works both in the real build and in unit tests that only populate
* `token.original.$extensions`.
*/
const readVariant = (token: TransformedToken, key: string, name: string): unknown => {
const resolved = token.$extensions?.[key]?.[name]?.$value;
if (resolved !== undefined) return resolved;
return token.original?.$extensions?.[key]?.[name]?.$value;
};
/**
* Read the original alias reference authored for a variant value, if any. The
* default (light/comfy) reference lives on the token's top-level
* `original.$value`; each authored variant's reference lives under
* `original.$extensions[key][name].$value`. Returns `undefined` when the source
* value is a literal rather than an alias.
*/
const readVariantReference = (
token: TransformedToken,
key: string,
name: string,
isDefault: boolean,
): string | undefined =>
isDefault
? asReference(token.original?.$value)
: asReference(token.original?.$extensions?.[key]?.[name]?.$value);
/**
* Build a variant entry: always `$value`, plus `reference` when a source alias
* exists and `default: true` when the axis marks its default. Both extras are
* omitted (not emitted as empty/false) when they don't apply.
*/
const makeEntry = (value: unknown, reference: string | undefined, markDefault: boolean): VariantEntry => ({
$value: value,
...(reference !== undefined ? { reference } : {}),
...(markDefault ? { default: true } : {}),
});
/**
* Build the resolved `$extensions` block(s) for a token, one per variant axis
* it participates in. A token is a color or a dimension, so in practice it
* participates in exactly one — color scheme (`com.salesforce-ux.mode`) or
* density (`com.salesforce-ux.density`).
*
* Each axis emits under its own key:
* { [axis.key]: { [defaultName]: { $value, reference?, default? },
* [variantName]: { $value, reference? }, … } }
*
* Each entry's `$value` is the resolved concrete value; `reference` carries the
* original DTCG alias (`{token.path}`) the value was authored as, and is
* omitted when the source value was a literal. The density default (`comfy`)
* additionally carries `default: true`; color scheme carries no default flag.
*
* The default value is the value captured by `value/capture-variant-defaults`
* before `css-vars` (flat.json), or the token's own resolved `$value`
* (raw.json, which does not run `css-vars`). The default key is omitted when no
* default value is available.
*
* Returns `undefined` when the token participates in no axis, so callers can
* omit `$extensions` entirely.
*
* @param {Object} token - Style Dictionary token (post-transform)
* @returns {Record<string, AxisBlock> | undefined}
*/
export const buildVariantExtensions = (token: TransformedToken): Record<string, AxisBlock> | undefined => {
const extensions: Record<string, AxisBlock> = {};
for (const axis of VARIANT_AXES) {
const variantEntries = axis.variantNames
.map((name) => [name, readVariant(token, axis.key, name)] as const)
.filter(([, value]) => value !== undefined);
// Skip axes the token does not participate in.
if (variantEntries.length === 0) continue;
const block: AxisBlock = {};
// Default (light / comfy): the captured value, else the resolved $value.
const defaultValue =
token.$extensions?.[axis.key]?.[axis.defaultName]?.$value ?? token.$value ?? token.value;
if (defaultValue !== undefined) {
block[axis.defaultName] = makeEntry(
defaultValue,
readVariantReference(token, axis.key, axis.defaultName, true),
axis.markDefault,
);
}
for (const [name, value] of variantEntries) {
block[name] = makeEntry(value, readVariantReference(token, axis.key, name, false), false);
}
extensions[axis.key] = block;
}
return Object.keys(extensions).length > 0 ? extensions : undefined;
};
|