@homebound/truss 2.22.0 → 2.24.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/build/index.d.ts CHANGED
@@ -7,6 +7,11 @@ import { Code } from 'ts-poet';
7
7
  * Or a set of properties, i.e. `f12` -> `{ fontFamily: ..., fontWeight: ... }`.
8
8
  */
9
9
  type FontConfig = Record<string, string | Properties>;
10
+ /**
11
+ * Maps a design token name (enum member / key) to a CSS custom property name.
12
+ * Values must be valid custom property identifiers (`--…`).
13
+ */
14
+ type TokenRegistry = Record<string, `--${string}`>;
10
15
  /**
11
16
  * Provides users with an easy way to configure the major/most-often configurable
12
17
  * aspect of a design system, i.e. the palette, fonts, and increments.
@@ -45,6 +50,11 @@ interface Config {
45
50
  typeAliases?: Record<string, Array<keyof Properties>>;
46
51
  /** Breakpoints, i.e. `{ sm: 0, md: 500 }`. */
47
52
  breakpoints?: Record<string, number>;
53
+ /**
54
+ * Optional design tokens: emitted as `Tokens` enum in generated `Css.ts` and available to
55
+ * `Css.setVar({ [Tokens.X]: … })` at build time (web target).
56
+ */
57
+ tokens?: TokenRegistry;
48
58
  /**
49
59
  * Which default methods to include.
50
60
  *
@@ -253,12 +263,24 @@ type IncConfig = [string, Prop | Prop[]];
253
263
  declare function newIncrementMethods(config: Config, abbr: UtilityName, prop: Prop | Prop[], opts?: {
254
264
  auto?: boolean;
255
265
  }): UtilityMethod[];
256
- /**
257
- * Creates just the core `<abbr>X` utility methods that set `props` with each increment value.
258
- *
259
- * See `newIncrementMethods` for handling the `<abbr>Px`, `<abbr>a`, and `<attr>(value)` methods.
260
- */
261
266
  declare function newCoreIncrementMethods(config: Config, abbr: UtilityName, props: Prop[]): UtilityMethod[];
262
267
  declare function newPxMethod(abbr: UtilityName, prop: Prop): UtilityMethod;
263
268
 
264
- export { type Aliases, type Config, type CreateMethodsFn, type FontConfig, type IncConfig, type SectionName, type Sections, type UtilityMethod, type UtilityName, type WebEntry, defaultSections, defineConfig, generate, newAliasesMethods, newCoreIncrementMethods, newIncrementMethods, newMethod, newMethodsForProp, newParamMethod, newPxMethod, newSetCssVariablesMethod, startWebCollection, stopWebCollection };
269
+ /**
270
+ * Web increment utilities use `--t-spacing` with `calc` (see generated `Css.ts` and the Vite plugin).
271
+ * Keep literals in one place so codegen, emitted CSS, and the transform stay aligned.
272
+ * `--t-spacing` must be set (e.g. `:root` prelude from `collectCss()` / mapping `increment`).
273
+ */
274
+ /** Custom property for increment-based spacing (web). */
275
+ declare const SPACING_CUSTOM_PROPERTY = "--t-spacing";
276
+ /** I.e. `calc(var(--t-spacing) * 3)` — requires prelude defining `--t-spacing`. */
277
+ declare function incrementCssValue(multiplier: number): string;
278
+ /**
279
+ * If `cssValue` is exactly `calc(var(--t-spacing) * k)` for this package's spacing property,
280
+ * returns the multiplier substring `k` (e.g. `"2"`, `"-1"`, `"2.5"`). Otherwise null.
281
+ */
282
+ declare function tryParseIncrementCalcMultiplier(cssValue: string): string | null;
283
+ /** Prepended to emitted Truss CSS; `incrementPx` comes from `truss-config` / `Css.json`. */
284
+ declare function rootSpacingPreludeCss(incrementPx: number): string;
285
+
286
+ export { type Aliases, type Config, type CreateMethodsFn, type FontConfig, type IncConfig, SPACING_CUSTOM_PROPERTY, type SectionName, type Sections, type TokenRegistry, type UtilityMethod, type UtilityName, type WebEntry, defaultSections, defineConfig, generate, incrementCssValue, newAliasesMethods, newCoreIncrementMethods, newIncrementMethods, newMethod, newMethodsForProp, newParamMethod, newPxMethod, newSetCssVariablesMethod, rootSpacingPreludeCss, startWebCollection, stopWebCollection, tryParseIncrementCalcMultiplier };
package/build/index.js CHANGED
@@ -3,6 +3,21 @@ function defineConfig(config) {
3
3
  return config;
4
4
  }
5
5
 
6
+ // src/spacing-css-var.ts
7
+ var SPACING_CUSTOM_PROPERTY = "--t-spacing";
8
+ function incrementCssValue(multiplier) {
9
+ return `calc(var(${SPACING_CUSTOM_PROPERTY}) * ${multiplier})`;
10
+ }
11
+ function tryParseIncrementCalcMultiplier(cssValue) {
12
+ const prop = SPACING_CUSTOM_PROPERTY.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13
+ const re = new RegExp(`^calc\\(var\\(${prop}\\) \\* (-?\\d+(?:\\.\\d+)?)\\)$`);
14
+ const m = cssValue.match(re);
15
+ return m ? m[1] : null;
16
+ }
17
+ function rootSpacingPreludeCss(incrementPx) {
18
+ return `:root { ${SPACING_CUSTOM_PROPERTY}: ${incrementPx}px; }`;
19
+ }
20
+
6
21
  // src/methods.ts
7
22
  var _webCollector = null;
8
23
  function startWebCollection() {
@@ -74,12 +89,15 @@ function newIncrementMethods(config, abbr, prop, opts = {}) {
74
89
  `${pxComment} ${abbr}Px(px: number) { return this.${props.map((p) => `add("${p}", \`\${px}px\`)`).join(".")}; }`
75
90
  ];
76
91
  }
92
+ function isWebIncrementTarget(config) {
93
+ return config.target !== "react-native";
94
+ }
77
95
  function newCoreIncrementMethods(config, abbr, props) {
78
96
  return zeroTo(config.numberOfIncrements).map((i) => {
79
- const px = `${i * config.increment}px`;
80
- const defs = Object.fromEntries(props.map((p) => [p, px]));
97
+ const value = isWebIncrementTarget(config) ? incrementCssValue(i) : `${i * config.increment}px`;
98
+ const defs = Object.fromEntries(props.map((p) => [p, value]));
81
99
  collect({ kind: "static", abbr: `${abbr}${i}`, defs });
82
- const sets = props.map((p) => `add("${p}", "${px}")`).join(".");
100
+ const sets = props.map((p) => `add("${p}", "${value}")`).join(".");
83
101
  return `${comment(defs)} get ${abbr}${i}() { return this.${sets}; }`;
84
102
  });
85
103
  }
@@ -940,6 +958,33 @@ var defaultTypeAliases = {
940
958
  Margin: ["margin", "marginTop", "marginRight", "marginBottom", "marginLeft"],
941
959
  Padding: ["padding", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft"]
942
960
  };
961
+ function emitTokensEnumAndSetVarTypes(tokens) {
962
+ const entries = tokens && typeof tokens === "object" ? Object.entries(tokens).filter(([, v]) => typeof v === "string") : [];
963
+ const hasTokens = entries.length > 0;
964
+ const enumBlock = hasTokens ? `export enum Tokens {
965
+ ${entries.map(([name, value]) => ` ${name} = ${JSON.stringify(value)},`).join("\n")}
966
+ }
967
+
968
+ ` : "";
969
+ const keysType = hasTokens ? `export type CssSetVarKeys = Tokens | \`--\${string}\`;
970
+
971
+ ` : `export type CssSetVarKeys = \`--\${string}\`;
972
+
973
+ `;
974
+ const scalar = `export type CssSetVarScalar = string | number;
975
+
976
+ `;
977
+ const valueType = `export type CssSetVarValue =
978
+ | CssSetVarScalar
979
+ | {
980
+ default?: CssSetVarScalar;
981
+ media?: Partial<Record<Breakpoint, CssSetVarScalar>>;
982
+ container?: Array<{ name?: string; gt?: number; lt?: number; value: CssSetVarScalar }>;
983
+ };
984
+
985
+ `;
986
+ return enumBlock + keysType + scalar + valueType;
987
+ }
943
988
  async function generate(config) {
944
989
  const { outputPath } = config;
945
990
  const target = config.target ?? "web";
@@ -960,7 +1005,7 @@ async function generate(config) {
960
1005
  throw new Error(`Unsupported truss target "${target}". Use "web" (default) or "react-native".`);
961
1006
  }
962
1007
  function generateReactNativeBuilder(config) {
963
- const { fonts, increment, extras, typeAliases, breakpoints = {}, palette } = config;
1008
+ const { fonts, increment, extras, typeAliases, breakpoints = {}, palette, tokens } = config;
964
1009
  const sections = generateSections(config);
965
1010
  const lines = Object.entries(sections).map(([name, value]) => [`// ${name}`, ...value, ""]).flat();
966
1011
  const typeAliasCode = Object.entries({
@@ -1024,6 +1069,8 @@ export type ${def("InlineStyle")} = Record<string, string | number | undefined>;
1024
1069
 
1025
1070
  ${typographyType}
1026
1071
 
1072
+ ${emitTokensEnumAndSetVarTypes(tokens)}
1073
+
1027
1074
  type Opts<T> = {
1028
1075
  rules: T,
1029
1076
  enabled: boolean,
@@ -1155,6 +1202,12 @@ class CssBuilder<T extends Properties> {
1155
1202
  return this;
1156
1203
  }
1157
1204
 
1205
+ /** Set CSS custom properties (web build-time only; not supported on react-native). */
1206
+ setVar(_values: Partial<Record<CssSetVarKeys, CssSetVarValue>>): CssBuilder<T> {
1207
+ void _values;
1208
+ throw new Error("Css.setVar() is only supported when Truss target is \\\"web\\\" (build-time atomic CSS).");
1209
+ }
1210
+
1158
1211
  /** Tagged template literal for raw CSS in .css.ts files; passes through the string as-is. */
1159
1212
  raw(strings: TemplateStringsArray, ...values: unknown[]): string {
1160
1213
  return String.raw(strings, ...values);
@@ -1238,7 +1291,7 @@ function collectWebGenerationData(config) {
1238
1291
  }
1239
1292
  }
1240
1293
  function generateWebCssBuilder(config, sections) {
1241
- const { fonts, increment, extras, typeAliases, palette, breakpoints = {} } = config;
1294
+ const { fonts, increment, extras, typeAliases, palette, breakpoints = {}, tokens } = config;
1242
1295
  const lines = Object.entries(sections).map(([name, value]) => [`// ${name}`, ...value, ""]).flat();
1243
1296
  const typeAliasCode = Object.entries({
1244
1297
  ...defaultTypeAliases,
@@ -1313,6 +1366,8 @@ export type ${def("RuntimeStyles")} = RawCssProperties & { readonly __kind: "run
1313
1366
 
1314
1367
  ${typographyType}
1315
1368
 
1369
+ ${emitTokensEnumAndSetVarTypes(tokens)}
1370
+
1316
1371
  // Augment React types so all JSX elements accept the \`css\` prop:
1317
1372
  // - HTMLAttributes/SVGAttributes cover intrinsic elements (div, svg, etc.)
1318
1373
  // - JSX.IntrinsicAttributes covers custom components (Card, Page, etc.)
@@ -1484,6 +1539,12 @@ class CssBuilder<T extends Properties, S extends StyleKind = "buildtime"> {
1484
1539
  return this.unsupportedRuntime("style() cannot be used in RuntimeStyle css expressions.");
1485
1540
  }
1486
1541
 
1542
+ /** Set CSS custom properties via atomic classes (resolved at build time on web). */
1543
+ setVar(_values: Partial<Record<CssSetVarKeys, CssSetVarValue>>): CssBuilder<T, S> {
1544
+ void _values;
1545
+ return this.unsupportedRuntime("setVar() cannot be used in RuntimeStyle css expressions.");
1546
+ }
1547
+
1487
1548
  /** Convert a style hash into \`{ className, style }\` props for manual spreading into non-\`css=\` contexts. */
1488
1549
  props(styles: Properties): Record<string, unknown> {
1489
1550
  return trussProps(styles as any);
@@ -1511,9 +1572,9 @@ class CssBuilder<T extends Properties, S extends StyleKind = "buildtime"> {
1511
1572
  }
1512
1573
  }
1513
1574
 
1514
- /** Converts \`inc\` into pixels value with a \`px\` suffix. */
1575
+ /** Converts \`inc\` into a spacing length using \`--t-spacing\` (build-time atomic CSS). */
1515
1576
  export function maybeInc(inc: number | string): string {
1516
- return typeof inc === "string" ? inc : \`\${inc * ${increment}}px\`;
1577
+ return typeof inc === "string" ? inc : \`calc(var(${SPACING_CUSTOM_PROPERTY}) * \${inc})\`;
1517
1578
  }
1518
1579
 
1519
1580
  /** Converts \`inc\` into pixels. */
@@ -1619,10 +1680,12 @@ function generateTrussMapping(config, entries) {
1619
1680
  for (const [name, mediaQuery] of Object.entries(genBreakpointsMap)) {
1620
1681
  breakpointEntries[`if${pascalCase(name)}`] = mediaQuery;
1621
1682
  }
1683
+ const tokenEntries = config.tokens && Object.keys(config.tokens).length > 0 ? config.tokens : void 0;
1622
1684
  return {
1623
1685
  increment: config.increment,
1624
1686
  ...Object.keys(breakpointEntries).length > 0 ? { breakpoints: breakpointEntries } : {},
1625
1687
  ...Object.keys(config.fonts).length > 0 ? { typography: Object.keys(config.fonts) } : {},
1688
+ ...tokenEntries ? { tokens: tokenEntries } : {},
1626
1689
  abbreviations
1627
1690
  };
1628
1691
  }
@@ -1636,6 +1699,9 @@ function condensedJson(mapping) {
1636
1699
  if (mapping.typography && mapping.typography.length > 0) {
1637
1700
  lines.push(` "typography": ${JSON.stringify(mapping.typography)},`);
1638
1701
  }
1702
+ if (mapping.tokens && Object.keys(mapping.tokens).length > 0) {
1703
+ lines.push(` "tokens": ${JSON.stringify(mapping.tokens)},`);
1704
+ }
1639
1705
  lines.push(` "abbreviations": {`);
1640
1706
  const entries = Object.entries(mapping.abbreviations);
1641
1707
  for (let i = 0; i < entries.length; i++) {
@@ -1648,9 +1714,11 @@ function condensedJson(mapping) {
1648
1714
  return lines.join("\n");
1649
1715
  }
1650
1716
  export {
1717
+ SPACING_CUSTOM_PROPERTY,
1651
1718
  defaultSections,
1652
1719
  defineConfig,
1653
1720
  generate,
1721
+ incrementCssValue,
1654
1722
  newAliasesMethods,
1655
1723
  newCoreIncrementMethods,
1656
1724
  newIncrementMethods,
@@ -1659,7 +1727,9 @@ export {
1659
1727
  newParamMethod,
1660
1728
  newPxMethod,
1661
1729
  newSetCssVariablesMethod,
1730
+ rootSpacingPreludeCss,
1662
1731
  startWebCollection,
1663
- stopWebCollection
1732
+ stopWebCollection,
1733
+ tryParseIncrementCalcMultiplier
1664
1734
  };
1665
1735
  //# sourceMappingURL=index.js.map