@latty-ds/tokens 0.7.0 → 0.9.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/README.md CHANGED
@@ -116,17 +116,17 @@ console.log(tokens.elevation['2']); // "0 4px 6px -1px rgb(…)"
116
116
 
117
117
  ### Primitive tokens (`tokens.css`)
118
118
 
119
- | Prefix | Example | Description |
120
- | --------------------------------------- | ------------------------------ | --------------------------------- |
121
- | `--lt-color-[name]-[step]` | `--lt-color-primary-500` | Color palette steps 50–900 |
122
- | `--lt-color-[name]-muted-[step]` | `--lt-color-primary-muted-300` | Desaturated variant of each color |
123
- | `--lt-color-neutral-[step]` | `--lt-color-neutral-100` | Greyscale scale |
124
- | `--lt-color-white` / `--lt-color-black` | | System colors |
125
- | `--lt-spacing-[n]` | `--lt-spacing-4` → `1rem` | Spacing scale (rem), n = 0–24 |
126
- | `--lt-spacing-px-[n]` | `--lt-spacing-px-4` → `16px` | Spacing scale (px) |
127
- | `--lt-border-radius` | `0.5rem` | Global border radius |
128
- | `--lt-elevation-[n]` | `--lt-elevation-2` | Box shadows, n = 0–5 |
129
- | `--lt-typography-fontFamily` | `"Hanken Grotesk", sans-serif` | Base font family |
119
+ | Prefix | Example | Description |
120
+ | --------------------------------------- | ------------------------------ | -------------------------------------------------------------------------- |
121
+ | `--lt-color-[name]-[step]` | `--lt-color-primary-500` | Color palette steps 50–900 |
122
+ | `--lt-color-[name]-muted-[step]` | `--lt-color-primary-muted-300` | Desaturated variant of each color |
123
+ | `--lt-color-neutral-[step]` | `--lt-color-neutral-100` | Greyscale scale |
124
+ | `--lt-color-white` / `--lt-color-black` | | System colors |
125
+ | `--lt-spacing-[n]` | `--lt-spacing-4` → `1rem` | Spacing scale (rem), n = 0–24 |
126
+ | `--lt-spacing-px-[n]` | `--lt-spacing-px-4` → `16px` | Spacing scale (px) |
127
+ | `--lt-border-radius` | `0.5rem` | Global border radius |
128
+ | `--lt-elevation-[n]` | `--lt-elevation-2` | Box shadows, n = 0–5 |
129
+ | `--lt-typography-fontFamilyPrimary` | `"Hanken Grotesk", sans-serif` | Base font family (additional `...Secondary`/`...Tertiary` when configured) |
130
130
 
131
131
  Color names: `primary`, `secondary`, `success`, `warning`, `error`, `info` (each with a `-muted` variant).
132
132
 
@@ -1,11 +1,55 @@
1
+ /**
2
+ * Border width applied to control chrome (outlined buttons, inputs, chips, surfaces).
3
+ * Named presets: `thin` = 1px (default), `medium` = 2px, `thick` = 4px.
4
+ * Any other CSS length (e.g. `'1.5px'`) passes through unchanged.
5
+ */
6
+ type BorderWidth = 'thin' | 'medium' | 'thick' | (string & {});
7
+ /** Generic CSS font-family keyword used as the safety-net fallback if a CDN font fails to load. */
8
+ type FontFallback = 'sans-serif' | 'serif' | 'monospace' | 'cursive' | 'fantasy' | 'system-ui';
9
+ /**
10
+ * A single `font.family` entry: a plain CSS value, a Google Fonts CSS2 URL, or
11
+ * a URL paired with a custom generic fallback (default `'sans-serif'`) for
12
+ * when the derived family isn't sans-serif — e.g. a serif display font.
13
+ */
14
+ type FontFamilyEntry = string | {
15
+ url: string;
16
+ fallback?: FontFallback;
17
+ };
1
18
  interface LattyConfig {
2
19
  /** Built-in names: primary, secondary, success, warning, error, info. Any additional name generates a new palette. */
3
20
  colors?: Record<string, string>;
4
21
  font?: {
5
- family?: string;
22
+ /**
23
+ * A CSS `font-family` value (e.g. `'Inter, sans-serif'`), a Google Fonts
24
+ * CSS2 stylesheet URL (e.g.
25
+ * `'https://fonts.googleapis.com/css2?family=Hanken+Grotesk:ital,wght@0,100..900&display=swap'`),
26
+ * a `{ url, fallback }` object, or an array of any of these. URLs get
27
+ * `@import`ed automatically and the family name is derived from the
28
+ * `family=` query param — no separate `<link>` tag needed. Stylesheet
29
+ * URLs from providers other than `fonts.googleapis.com` are still
30
+ * loaded, but since there's no reliable way to guess the family name
31
+ * from an arbitrary CDN, pass the resulting name as a plain value
32
+ * instead (or open an issue if there's a provider worth special-casing).
33
+ *
34
+ * A bare URL string always falls back to the generic `sans-serif` family
35
+ * if the font itself fails to load. Use the `{ url, fallback }` object
36
+ * form to pick a different generic fallback (e.g. `'serif'` for a serif
37
+ * display font):
38
+ * ```ts
39
+ * family: { url: 'https://fonts.googleapis.com/css2?family=Playfair+Display', fallback: 'serif' }
40
+ * ```
41
+ *
42
+ * When an array is passed, each entry maps to its own token in order —
43
+ * `--lt-typography-fontFamilyPrimary`, `...Secondary`, `...Tertiary`,
44
+ * etc. — rather than being combined into a single CSS fallback stack, so
45
+ * multiple fonts (e.g. a heading font and a body font) can each be
46
+ * referenced independently in your own CSS.
47
+ */
48
+ family?: FontFamilyEntry | FontFamilyEntry[];
6
49
  };
7
50
  border?: {
8
51
  radius?: string;
52
+ width?: BorderWidth;
9
53
  };
10
54
  /**
11
55
  * Controls which semantic token layer is emitted.
@@ -27,28 +71,28 @@ declare const createStyleSheet: (userConfig?: LattyConfig) => string;
27
71
  * Injects design tokens into the document as a `<style>` element.
28
72
  * Call once at your app's entry point — before any components render.
29
73
  *
30
- * Add `data-lt` to `<html>` to enable automatic FOUC prevention: configure()
31
- * hides the document during token injection and reveals it on the next frame.
32
- * For zero FOUC on initial load, also add this one-liner before your module script:
33
- * `<style>html[data-lt]:not([data-lt-ready]){visibility:hidden}</style>`
74
+ * `font.family` accepts a single value or an array. Each entry is either a
75
+ * plain CSS `font-family` value or a Google Fonts CSS2 stylesheet URL (which
76
+ * configure() `@import`s and derives the family name from automatically — no
77
+ * separate `<link>` tag needed). Array entries map to their own token in
78
+ * order — `--lt-typography-fontFamilyPrimary`, `...Secondary`, `...Tertiary`,
79
+ * etc. — so each font can be referenced independently in your own CSS.
34
80
  *
35
81
  * @example
36
82
  * ```html
37
- * <html lang="en" data-lt>
38
- * <head>
39
- * <style>html[data-lt]:not([data-lt-ready]){visibility:hidden}</style>
40
- * <script type="module">
41
- * import { configure } from '@latty-ds/tokens/configure';
42
- * configure({
43
- * colors: { primary: '#6366f1', secondary: '#f59e0b' },
44
- * font: { family: 'Inter, sans-serif' },
45
- * border: { radius: '0.375rem' },
46
- * });
47
- * </script>
48
- * </head>
49
- * </html>
83
+ * <script type="module">
84
+ * import { configure } from '@latty-ds/tokens/configure';
85
+ * configure({
86
+ * colors: { primary: '#6366f1', secondary: '#f59e0b' },
87
+ * font: { family: [
88
+ * 'https://fonts.googleapis.com/css2?family=Hanken+Grotesk:ital,wght@0,100..900;1,100..900&display=swap',
89
+ * 'Georgia, serif',
90
+ * ] },
91
+ * border: { radius: '0.375rem', width: 'medium' },
92
+ * });
93
+ * </script>
50
94
  * ```
51
95
  */
52
96
  declare const configure: (userConfig?: LattyConfig) => void;
53
97
 
54
- export { type LattyConfig, configure, createStyleSheet };
98
+ export { type BorderWidth, type LattyConfig, configure, createStyleSheet };
package/dist/configure.js CHANGED
@@ -32,6 +32,12 @@ var REM_BASE_PX = 16;
32
32
 
33
33
  // src/constants/tokens.constants.ts
34
34
  var DEFAULT_BORDER_RADIUS = "0.5rem";
35
+ var DEFAULT_BORDER_WIDTH = "1px";
36
+ var BORDER_WIDTH_PRESETS = {
37
+ thin: "1px",
38
+ medium: "2px",
39
+ thick: "4px"
40
+ };
35
41
  var DEFAULT_FONT_FAMILY = '"Hanken Grotesk", sans-serif';
36
42
 
37
43
  // src/spacing/spacing.ts
@@ -165,8 +171,8 @@ var buildTokens = (cfg) => {
165
171
  let color = buildColorTokens(cfg);
166
172
  color = addSystemColors(color);
167
173
  const spacing = buildSpacing();
168
- const border = { radius: DEFAULT_BORDER_RADIUS };
169
- const typography = { fontFamily: DEFAULT_FONT_FAMILY };
174
+ const border = { radius: DEFAULT_BORDER_RADIUS, width: DEFAULT_BORDER_WIDTH };
175
+ const typography = { fontFamilyPrimary: DEFAULT_FONT_FAMILY };
170
176
  const elevation = buildElevation(color.neutral);
171
177
  return { color, spacing, border, typography, elevation };
172
178
  };
@@ -313,6 +319,41 @@ var buildDarkSemanticTokens = (opts = {}) => {
313
319
  };
314
320
  var buildSemanticTokens = (mode = "light", opts = {}) => mode === "dark" ? buildDarkSemanticTokens(opts) : buildLightSemanticTokens(opts);
315
321
 
322
+ // src/configure/font.ts
323
+ var FONT_FAMILY_SLOT_NAMES = ["Primary", "Secondary", "Tertiary", "Quaternary"];
324
+ var fontFamilySlot = (index) => `fontFamily${FONT_FAMILY_SLOT_NAMES[index] ?? String(index + 1)}`;
325
+ var unwrapQuotes = (value) => {
326
+ const trimmed = value.trim();
327
+ const first = trimmed[0];
328
+ if (trimmed.length > 1 && (first === '"' || first === "'") && trimmed[trimmed.length - 1] === first) {
329
+ return trimmed.slice(1, -1).trim();
330
+ }
331
+ return trimmed;
332
+ };
333
+ var isUrl = (value) => /^https?:\/\//i.test(value);
334
+ var namesFromFamilyParam = (raw) => raw.split("|").map((part) => part.split(":")[0].trim()).filter(Boolean);
335
+ var quoteIfNeeded = (name) => /\s/.test(name) ? `"${name}"` : name;
336
+ var resolveSingleFont = (entry) => {
337
+ const rawValue = typeof entry === "string" ? entry : entry.url;
338
+ const fallback = typeof entry === "string" ? "sans-serif" : entry.fallback ?? "sans-serif";
339
+ const unwrapped = unwrapQuotes(rawValue);
340
+ if (!isUrl(unwrapped)) return { family: unwrapped, importUrl: null };
341
+ let parsed;
342
+ try {
343
+ parsed = new URL(unwrapped);
344
+ } catch {
345
+ return { family: unwrapped, importUrl: null };
346
+ }
347
+ if (parsed.hostname.toLowerCase() === "fonts.googleapis.com") {
348
+ const names = [...new Set(parsed.searchParams.getAll("family").flatMap(namesFromFamilyParam))];
349
+ if (names.length > 0) {
350
+ return { family: `${names.map(quoteIfNeeded).join(", ")}, ${fallback}`, importUrl: unwrapped };
351
+ }
352
+ }
353
+ return { family: "", importUrl: unwrapped };
354
+ };
355
+ var resolveFontFamilies = (value) => (Array.isArray(value) ? value : [value]).map(resolveSingleFont);
356
+
316
357
  // tokens.config.json
317
358
  var tokens_config_default = {
318
359
  color: {
@@ -332,39 +373,57 @@ var tokens_config_default = {
332
373
  var DEFAULTS = {
333
374
  colors: tokens_config_default.color,
334
375
  font: { family: DEFAULT_FONT_FAMILY },
335
- border: { radius: DEFAULT_BORDER_RADIUS }
376
+ border: { radius: DEFAULT_BORDER_RADIUS, width: DEFAULT_BORDER_WIDTH }
336
377
  };
337
378
  var toInternalConfig = (userConfig) => ({
338
379
  color: { ...DEFAULTS.colors, ...userConfig.colors }
339
380
  });
381
+ var resolveBorderWidth = (width) => BORDER_WIDTH_PRESETS[width] ?? width;
340
382
  var createStyleSheet = (userConfig = {}) => {
341
383
  const cfg = toInternalConfig(userConfig);
342
384
  let tokens = buildTokens(cfg);
385
+ const importUrls = [];
343
386
  if (userConfig.font?.family) {
344
- tokens = { ...tokens, typography: { fontFamily: userConfig.font.family } };
387
+ const resolvedFonts = resolveFontFamilies(userConfig.font.family);
388
+ const typographyOverrides = {};
389
+ resolvedFonts.forEach((font, i) => {
390
+ if (font.importUrl) importUrls.push(font.importUrl);
391
+ if (font.family) typographyOverrides[fontFamilySlot(i)] = font.family;
392
+ });
393
+ if (Object.keys(typographyOverrides).length > 0) {
394
+ tokens = { ...tokens, typography: { ...tokens.typography, ...typographyOverrides } };
395
+ }
345
396
  }
346
397
  if (userConfig.border?.radius) {
347
398
  tokens = { ...tokens, border: { ...tokens.border, radius: userConfig.border.radius } };
348
399
  }
400
+ if (userConfig.border?.width) {
401
+ tokens = { ...tokens, border: { ...tokens.border, width: resolveBorderWidth(userConfig.border.width) } };
402
+ }
349
403
  const primitives = tokensToCss(tokens);
350
404
  const theme = userConfig.theme ?? "light";
351
405
  const primary = tokens.color.primary;
352
406
  const semanticOpts = { primary500: primary["500"], primary400: primary["400"] };
353
- if (theme === "dark") return primitives + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts));
354
- if (theme === "light") return primitives + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts));
355
- return primitives + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts)) + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts), "@media (prefers-color-scheme: dark)") + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts), '[data-theme="dark"]') + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts), '[data-theme="light"]');
407
+ let body;
408
+ if (theme === "dark") {
409
+ body = primitives + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts));
410
+ } else if (theme === "light") {
411
+ body = primitives + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts));
412
+ } else {
413
+ body = primitives + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts)) + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts), "@media (prefers-color-scheme: dark)") + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts), '[data-theme="dark"]') + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts), '[data-theme="light"]');
414
+ }
415
+ const importLines = importUrls.map((url) => `@import url("${url}");`).join("\n");
416
+ return importLines ? `${importLines}
417
+ ${body}` : body;
356
418
  };
357
419
  var configure = (userConfig = {}) => {
358
- const root = document.documentElement;
359
- root.removeAttribute("data-lt-ready");
360
420
  let style = document.getElementById("lt-tokens");
361
421
  if (!style) {
362
422
  style = document.createElement("style");
363
423
  style.id = "lt-tokens";
364
424
  document.head.prepend(style);
365
425
  }
366
- style.textContent = "html[data-lt]:not([data-lt-ready]){visibility:hidden}\n" + createStyleSheet(userConfig);
367
- requestAnimationFrame(() => root.setAttribute("data-lt-ready", ""));
426
+ style.textContent = createStyleSheet(userConfig);
368
427
  };
369
428
  export {
370
429
  configure,
@@ -1,2 +1,12 @@
1
1
  export declare const DEFAULT_BORDER_RADIUS: "0.5rem";
2
+ export declare const DEFAULT_BORDER_WIDTH: "1px";
3
+ /**
4
+ * Named presets accepted by `border.width` in `LattyConfig`.
5
+ * Any other CSS length passes through unchanged.
6
+ */
7
+ export declare const BORDER_WIDTH_PRESETS: {
8
+ readonly thin: "1px";
9
+ readonly medium: "2px";
10
+ readonly thick: "4px";
11
+ };
2
12
  export declare const DEFAULT_FONT_FAMILY: "\"Hanken Grotesk\", sans-serif";
package/dist/tokens.css CHANGED
@@ -1,5 +1,6 @@
1
1
  :root{
2
2
  --lt-border-radius: 0.5rem;
3
+ --lt-border-width: 1px;
3
4
  --lt-color-black: #000000;
4
5
  --lt-color-error-100: #ffc8c1;
5
6
  --lt-color-error-200: #ffa198;
@@ -188,5 +189,5 @@
188
189
  --lt-spacing-7: 1.75rem;
189
190
  --lt-spacing-8: 2rem;
190
191
  --lt-spacing-9: 2.25rem;
191
- --lt-typography-fontFamily: "Hanken Grotesk", sans-serif;
192
+ --lt-typography-fontFamilyPrimary: "Hanken Grotesk", sans-serif;
192
193
  }
package/dist/tokens.js CHANGED
@@ -1 +1 @@
1
- export const tokens = {"color":{"primary":{"50":"#f0e5df","100":"#ffd4b5","200":"#ffbb87","300":"#ffa860","400":"#ff9539","500":"#ff8200","600":"#d56400","700":"#963b00","800":"#5e3c25","900":"#3c1d05"},"primary-muted":{"50":"#e9e7e6","100":"#e8dfda","200":"#dfd1c8","300":"#dac8bd","400":"#d2beb2","500":"#c6b1a5","600":"#a08e82","700":"#6b5c53","800":"#50453e","900":"#2b2521"},"secondary":{"50":"#e5e7f1","100":"#cbd2ff","200":"#a3acf7","300":"#8a93f4","400":"#7076e5","500":"#5252c5","600":"#4544ad","700":"#333187","800":"#2e3254","900":"#202343"},"secondary-muted":{"50":"#e7e8e9","100":"#d5d7df","200":"#b4b7c4","300":"#a0a3b3","400":"#898c9e","500":"#6c6f81","600":"#5a5d6d","700":"#414351","800":"#363742","900":"#25262c"},"success":{"50":"#e1ebe2","100":"#beedc6","200":"#94e4a4","300":"#70df8b","400":"#4bd573","500":"#22c55e","600":"#00a244","700":"#006f22","800":"#2b4d32","900":"#0c2e16"},"success-muted":{"50":"#e7e8e7","100":"#dbe3dc","200":"#cad7cc","300":"#bfcfc1","400":"#b3c5b6","500":"#a5b8a8","600":"#839586","700":"#546256","800":"#404a41","900":"#222823"},"warning":{"50":"#ece7de","100":"#f5e0b5","200":"#f6d289","300":"#f9c962","400":"#f7bf3a","500":"#eeb308","600":"#c38e00","700":"#845a00","800":"#584720","900":"#332400"},"warning-muted":{"50":"#e8e8e6","100":"#e7e4dc","200":"#e2dcd0","300":"#ded7c8","400":"#dad1c0","500":"#d2c9b8","600":"#a9a090","700":"#6d6659","800":"#504c42","900":"#292621"},"error":{"50":"#f3e4e2","100":"#ffc8c1","200":"#ffa198","300":"#ff847b","400":"#ff6560","500":"#ef4444","600":"#cb2d31","700":"#950e18","800":"#5a312e","900":"#3f1917"},"error-muted":{"50":"#e9e7e7","100":"#e6dad8","200":"#d6c3c1","300":"#cdb4b2","400":"#bfa4a1","500":"#ab908d","600":"#8d7572","700":"#624e4c","800":"#4c3d3b","900":"#2d2423"},"info":{"50":"#e1e9ef","100":"#bce4fe","200":"#8fd2fd","300":"#6ac7ff","400":"#43b9fa","500":"#0ea5e9","600":"#0087c4","700":"#005c8d","800":"#1e465d","900":"#082a3c"},"info-muted":{"50":"#e7e8e9","100":"#d9e0e5","200":"#c5d0d7","300":"#b8c6cf","400":"#aab9c4","500":"#99a9b3","600":"#7a8993","700":"#4f5b63","800":"#3d464c","900":"#22272a"},"neutral":{"50":"#fafafa","100":"#f3f3f3","200":"#e4e4e4","300":"#cacaca","400":"#ababab","500":"#868686","600":"#696969","700":"#4d4d4d","800":"#333333","900":"#1b1b1b"},"white":"#FFFFFF","black":"#000000"},"spacing":{"px":{"0":"0px","1":"4px","2":"8px","3":"12px","4":"16px","5":"20px","6":"24px","7":"28px","8":"32px","9":"36px","10":"40px","11":"44px","12":"48px","13":"52px","14":"56px","15":"60px","16":"64px","17":"68px","18":"72px","19":"76px","20":"80px","21":"84px","22":"88px","23":"92px","24":"96px"},"rem":{"0":"0rem","1":"0.25rem","2":"0.5rem","3":"0.75rem","4":"1rem","5":"1.25rem","6":"1.5rem","7":"1.75rem","8":"2rem","9":"2.25rem","10":"2.5rem","11":"2.75rem","12":"3rem","13":"3.25rem","14":"3.5rem","15":"3.75rem","16":"4rem","17":"4.25rem","18":"4.5rem","19":"4.75rem","20":"5rem","21":"5.25rem","22":"5.5rem","23":"5.75rem","24":"6rem"}},"border":{"radius":"0.5rem"},"typography":{"fontFamily":"\"Hanken Grotesk\", sans-serif"},"elevation":{"0":"none","1":"0 1px 3px 0 rgb(27 27 27 / 0.1), 0 1px 2px 0 rgb(27 27 27 / 0.06)","2":"0 4px 6px -1px rgb(27 27 27 / 0.1), 0 2px 4px -1px rgb(27 27 27 / 0.06)","3":"0 10px 15px -3px rgb(27 27 27 / 0.1), 0 4px 6px -2px rgb(27 27 27 / 0.05)","4":"0 20px 25px -5px rgb(27 27 27 / 0.1), 0 10px 10px -5px rgb(27 27 27 / 0.04)","5":"0 25px 50px -12px rgb(27 27 27 / 0.25)"},"semantic":{"text-default":"color-neutral-900","text-subtle":"color-neutral-600","text-muted":"color-neutral-400","text-disabled":"color-neutral-400","text-inverse":"color-white","text-neutral":"color-neutral-700","text-primary":"color-primary-700","text-on-primary":"color-neutral-900","text-secondary":"color-secondary-700","text-on-secondary":"color-white","text-success":"color-success-700","text-on-success":"color-white","text-warning":"color-warning-700","text-on-warning":"color-warning-800","text-error":"color-error-700","text-on-error":"color-white","text-info":"color-info-700","text-on-info":"color-white","bg-default":"color-white","bg-subtle":"color-neutral-50","bg-surface":"color-neutral-100","bg-overlay":"color-neutral-200","bg-inverse":"color-neutral-900","bg-neutral-subtle":"color-neutral-100","bg-primary":"color-primary-500","bg-primary-subtle":"color-primary-100","bg-secondary":"color-secondary-500","bg-secondary-subtle":"color-secondary-100","bg-success":"color-success-500","bg-success-subtle":"color-success-100","bg-warning":"color-warning-500","bg-warning-subtle":"color-warning-100","bg-error":"color-error-500","bg-error-subtle":"color-error-100","bg-info":"color-info-500","bg-info-subtle":"color-info-100","border-default":"color-neutral-200","border-strong":"color-neutral-400","border-subtle":"color-neutral-100","border-focus":"color-primary-200","border-primary":"color-primary-200","border-primary-strong":"color-primary-500","border-secondary":"color-secondary-200","border-secondary-strong":"color-secondary-500","border-success":"color-success-200","border-success-strong":"color-success-500","border-warning":"color-warning-200","border-warning-strong":"color-warning-500","border-error":"color-error-200","border-error-strong":"color-error-500","border-info":"color-info-200","border-info-strong":"color-info-500","interactive-primary-bg":"color-primary-500","interactive-primary-bg-hover":"color-primary-600","interactive-primary-bg-active":"color-primary-700","interactive-secondary-bg":"color-secondary-500","interactive-secondary-bg-hover":"color-secondary-600","interactive-secondary-bg-active":"color-secondary-700","interactive-success-bg":"color-success-500","interactive-success-bg-hover":"color-success-600","interactive-success-bg-active":"color-success-700","interactive-warning-bg":"color-warning-500","interactive-warning-bg-hover":"color-warning-600","interactive-warning-bg-active":"color-warning-700","interactive-error-bg":"color-error-500","interactive-error-bg-hover":"color-error-600","interactive-error-bg-active":"color-error-700","interactive-info-bg":"color-info-500","interactive-info-bg-hover":"color-info-600","interactive-info-bg-active":"color-info-700"},"semanticDark":{"text-default":"color-neutral-100","text-subtle":"color-neutral-300","text-muted":"color-neutral-500","text-disabled":"color-neutral-600","text-inverse":"color-neutral-900","text-neutral":"color-neutral-200","text-primary":"color-primary-300","text-on-primary":"color-neutral-900","text-secondary":"color-secondary-300","text-on-secondary":"color-white","text-success":"color-success-300","text-on-success":"color-white","text-warning":"color-warning-300","text-on-warning":"color-warning-800","text-error":"color-error-300","text-on-error":"color-white","text-info":"color-info-300","text-on-info":"color-white","bg-default":"color-neutral-900","bg-subtle":"color-neutral-800","bg-surface":"color-neutral-800","bg-overlay":"color-neutral-700","bg-inverse":"color-neutral-50","bg-neutral-subtle":"color-neutral-800","bg-primary":"color-primary-500","bg-primary-subtle":"color-primary-900","bg-secondary":"color-secondary-500","bg-secondary-subtle":"color-secondary-900","bg-success":"color-success-500","bg-success-subtle":"color-success-900","bg-warning":"color-warning-500","bg-warning-subtle":"color-warning-900","bg-error":"color-error-500","bg-error-subtle":"color-error-900","bg-info":"color-info-500","bg-info-subtle":"color-info-900","border-default":"color-neutral-700","border-strong":"color-neutral-500","border-subtle":"color-neutral-800","border-focus":"color-primary-400","border-primary":"color-primary-700","border-primary-strong":"color-primary-500","border-secondary":"color-secondary-700","border-secondary-strong":"color-secondary-500","border-success":"color-success-700","border-success-strong":"color-success-500","border-warning":"color-warning-700","border-warning-strong":"color-warning-500","border-error":"color-error-700","border-error-strong":"color-error-500","border-info":"color-info-700","border-info-strong":"color-info-500","interactive-primary-bg":"color-primary-400","interactive-primary-bg-hover":"color-primary-300","interactive-primary-bg-active":"color-primary-500","interactive-secondary-bg":"color-secondary-400","interactive-secondary-bg-hover":"color-secondary-300","interactive-secondary-bg-active":"color-secondary-500","interactive-success-bg":"color-success-400","interactive-success-bg-hover":"color-success-300","interactive-success-bg-active":"color-success-500","interactive-warning-bg":"color-warning-400","interactive-warning-bg-hover":"color-warning-300","interactive-warning-bg-active":"color-warning-500","interactive-error-bg":"color-error-400","interactive-error-bg-hover":"color-error-300","interactive-error-bg-active":"color-error-500","interactive-info-bg":"color-info-400","interactive-info-bg-hover":"color-info-300","interactive-info-bg-active":"color-info-500"}};
1
+ export const tokens = {"color":{"primary":{"50":"#f0e5df","100":"#ffd4b5","200":"#ffbb87","300":"#ffa860","400":"#ff9539","500":"#ff8200","600":"#d56400","700":"#963b00","800":"#5e3c25","900":"#3c1d05"},"primary-muted":{"50":"#e9e7e6","100":"#e8dfda","200":"#dfd1c8","300":"#dac8bd","400":"#d2beb2","500":"#c6b1a5","600":"#a08e82","700":"#6b5c53","800":"#50453e","900":"#2b2521"},"secondary":{"50":"#e5e7f1","100":"#cbd2ff","200":"#a3acf7","300":"#8a93f4","400":"#7076e5","500":"#5252c5","600":"#4544ad","700":"#333187","800":"#2e3254","900":"#202343"},"secondary-muted":{"50":"#e7e8e9","100":"#d5d7df","200":"#b4b7c4","300":"#a0a3b3","400":"#898c9e","500":"#6c6f81","600":"#5a5d6d","700":"#414351","800":"#363742","900":"#25262c"},"success":{"50":"#e1ebe2","100":"#beedc6","200":"#94e4a4","300":"#70df8b","400":"#4bd573","500":"#22c55e","600":"#00a244","700":"#006f22","800":"#2b4d32","900":"#0c2e16"},"success-muted":{"50":"#e7e8e7","100":"#dbe3dc","200":"#cad7cc","300":"#bfcfc1","400":"#b3c5b6","500":"#a5b8a8","600":"#839586","700":"#546256","800":"#404a41","900":"#222823"},"warning":{"50":"#ece7de","100":"#f5e0b5","200":"#f6d289","300":"#f9c962","400":"#f7bf3a","500":"#eeb308","600":"#c38e00","700":"#845a00","800":"#584720","900":"#332400"},"warning-muted":{"50":"#e8e8e6","100":"#e7e4dc","200":"#e2dcd0","300":"#ded7c8","400":"#dad1c0","500":"#d2c9b8","600":"#a9a090","700":"#6d6659","800":"#504c42","900":"#292621"},"error":{"50":"#f3e4e2","100":"#ffc8c1","200":"#ffa198","300":"#ff847b","400":"#ff6560","500":"#ef4444","600":"#cb2d31","700":"#950e18","800":"#5a312e","900":"#3f1917"},"error-muted":{"50":"#e9e7e7","100":"#e6dad8","200":"#d6c3c1","300":"#cdb4b2","400":"#bfa4a1","500":"#ab908d","600":"#8d7572","700":"#624e4c","800":"#4c3d3b","900":"#2d2423"},"info":{"50":"#e1e9ef","100":"#bce4fe","200":"#8fd2fd","300":"#6ac7ff","400":"#43b9fa","500":"#0ea5e9","600":"#0087c4","700":"#005c8d","800":"#1e465d","900":"#082a3c"},"info-muted":{"50":"#e7e8e9","100":"#d9e0e5","200":"#c5d0d7","300":"#b8c6cf","400":"#aab9c4","500":"#99a9b3","600":"#7a8993","700":"#4f5b63","800":"#3d464c","900":"#22272a"},"neutral":{"50":"#fafafa","100":"#f3f3f3","200":"#e4e4e4","300":"#cacaca","400":"#ababab","500":"#868686","600":"#696969","700":"#4d4d4d","800":"#333333","900":"#1b1b1b"},"white":"#FFFFFF","black":"#000000"},"spacing":{"px":{"0":"0px","1":"4px","2":"8px","3":"12px","4":"16px","5":"20px","6":"24px","7":"28px","8":"32px","9":"36px","10":"40px","11":"44px","12":"48px","13":"52px","14":"56px","15":"60px","16":"64px","17":"68px","18":"72px","19":"76px","20":"80px","21":"84px","22":"88px","23":"92px","24":"96px"},"rem":{"0":"0rem","1":"0.25rem","2":"0.5rem","3":"0.75rem","4":"1rem","5":"1.25rem","6":"1.5rem","7":"1.75rem","8":"2rem","9":"2.25rem","10":"2.5rem","11":"2.75rem","12":"3rem","13":"3.25rem","14":"3.5rem","15":"3.75rem","16":"4rem","17":"4.25rem","18":"4.5rem","19":"4.75rem","20":"5rem","21":"5.25rem","22":"5.5rem","23":"5.75rem","24":"6rem"}},"border":{"radius":"0.5rem","width":"1px"},"typography":{"fontFamilyPrimary":"\"Hanken Grotesk\", sans-serif"},"elevation":{"0":"none","1":"0 1px 3px 0 rgb(27 27 27 / 0.1), 0 1px 2px 0 rgb(27 27 27 / 0.06)","2":"0 4px 6px -1px rgb(27 27 27 / 0.1), 0 2px 4px -1px rgb(27 27 27 / 0.06)","3":"0 10px 15px -3px rgb(27 27 27 / 0.1), 0 4px 6px -2px rgb(27 27 27 / 0.05)","4":"0 20px 25px -5px rgb(27 27 27 / 0.1), 0 10px 10px -5px rgb(27 27 27 / 0.04)","5":"0 25px 50px -12px rgb(27 27 27 / 0.25)"},"semantic":{"text-default":"color-neutral-900","text-subtle":"color-neutral-600","text-muted":"color-neutral-400","text-disabled":"color-neutral-400","text-inverse":"color-white","text-neutral":"color-neutral-700","text-primary":"color-primary-700","text-on-primary":"color-neutral-900","text-secondary":"color-secondary-700","text-on-secondary":"color-white","text-success":"color-success-700","text-on-success":"color-white","text-warning":"color-warning-700","text-on-warning":"color-warning-800","text-error":"color-error-700","text-on-error":"color-white","text-info":"color-info-700","text-on-info":"color-white","bg-default":"color-white","bg-subtle":"color-neutral-50","bg-surface":"color-neutral-100","bg-overlay":"color-neutral-200","bg-inverse":"color-neutral-900","bg-neutral-subtle":"color-neutral-100","bg-primary":"color-primary-500","bg-primary-subtle":"color-primary-100","bg-secondary":"color-secondary-500","bg-secondary-subtle":"color-secondary-100","bg-success":"color-success-500","bg-success-subtle":"color-success-100","bg-warning":"color-warning-500","bg-warning-subtle":"color-warning-100","bg-error":"color-error-500","bg-error-subtle":"color-error-100","bg-info":"color-info-500","bg-info-subtle":"color-info-100","border-default":"color-neutral-200","border-strong":"color-neutral-400","border-subtle":"color-neutral-100","border-focus":"color-primary-200","border-primary":"color-primary-200","border-primary-strong":"color-primary-500","border-secondary":"color-secondary-200","border-secondary-strong":"color-secondary-500","border-success":"color-success-200","border-success-strong":"color-success-500","border-warning":"color-warning-200","border-warning-strong":"color-warning-500","border-error":"color-error-200","border-error-strong":"color-error-500","border-info":"color-info-200","border-info-strong":"color-info-500","interactive-primary-bg":"color-primary-500","interactive-primary-bg-hover":"color-primary-600","interactive-primary-bg-active":"color-primary-700","interactive-secondary-bg":"color-secondary-500","interactive-secondary-bg-hover":"color-secondary-600","interactive-secondary-bg-active":"color-secondary-700","interactive-success-bg":"color-success-500","interactive-success-bg-hover":"color-success-600","interactive-success-bg-active":"color-success-700","interactive-warning-bg":"color-warning-500","interactive-warning-bg-hover":"color-warning-600","interactive-warning-bg-active":"color-warning-700","interactive-error-bg":"color-error-500","interactive-error-bg-hover":"color-error-600","interactive-error-bg-active":"color-error-700","interactive-info-bg":"color-info-500","interactive-info-bg-hover":"color-info-600","interactive-info-bg-active":"color-info-700"},"semanticDark":{"text-default":"color-neutral-100","text-subtle":"color-neutral-300","text-muted":"color-neutral-500","text-disabled":"color-neutral-600","text-inverse":"color-neutral-900","text-neutral":"color-neutral-200","text-primary":"color-primary-300","text-on-primary":"color-neutral-900","text-secondary":"color-secondary-300","text-on-secondary":"color-white","text-success":"color-success-300","text-on-success":"color-white","text-warning":"color-warning-300","text-on-warning":"color-warning-800","text-error":"color-error-300","text-on-error":"color-white","text-info":"color-info-300","text-on-info":"color-white","bg-default":"color-neutral-900","bg-subtle":"color-neutral-800","bg-surface":"color-neutral-800","bg-overlay":"color-neutral-700","bg-inverse":"color-neutral-50","bg-neutral-subtle":"color-neutral-800","bg-primary":"color-primary-500","bg-primary-subtle":"color-primary-900","bg-secondary":"color-secondary-500","bg-secondary-subtle":"color-secondary-900","bg-success":"color-success-500","bg-success-subtle":"color-success-900","bg-warning":"color-warning-500","bg-warning-subtle":"color-warning-900","bg-error":"color-error-500","bg-error-subtle":"color-error-900","bg-info":"color-info-500","bg-info-subtle":"color-info-900","border-default":"color-neutral-700","border-strong":"color-neutral-500","border-subtle":"color-neutral-800","border-focus":"color-primary-400","border-primary":"color-primary-700","border-primary-strong":"color-primary-500","border-secondary":"color-secondary-700","border-secondary-strong":"color-secondary-500","border-success":"color-success-700","border-success-strong":"color-success-500","border-warning":"color-warning-700","border-warning-strong":"color-warning-500","border-error":"color-error-700","border-error-strong":"color-error-500","border-info":"color-info-700","border-info-strong":"color-info-500","interactive-primary-bg":"color-primary-400","interactive-primary-bg-hover":"color-primary-300","interactive-primary-bg-active":"color-primary-500","interactive-secondary-bg":"color-secondary-400","interactive-secondary-bg-hover":"color-secondary-300","interactive-secondary-bg-active":"color-secondary-500","interactive-success-bg":"color-success-400","interactive-success-bg-hover":"color-success-300","interactive-success-bg-active":"color-success-500","interactive-warning-bg":"color-warning-400","interactive-warning-bg-hover":"color-warning-300","interactive-warning-bg-active":"color-warning-500","interactive-error-bg":"color-error-400","interactive-error-bg-hover":"color-error-300","interactive-error-bg-active":"color-error-500","interactive-info-bg":"color-info-400","interactive-info-bg-hover":"color-info-300","interactive-info-bg-active":"color-info-500"}};
package/dist/tokens.json CHANGED
@@ -216,10 +216,11 @@
216
216
  }
217
217
  },
218
218
  "border": {
219
- "radius": "0.5rem"
219
+ "radius": "0.5rem",
220
+ "width": "1px"
220
221
  },
221
222
  "typography": {
222
- "fontFamily": "\"Hanken Grotesk\", sans-serif"
223
+ "fontFamilyPrimary": "\"Hanken Grotesk\", sans-serif"
223
224
  },
224
225
  "elevation": {
225
226
  "0": "none",
@@ -24,7 +24,7 @@ import type { ElevationTokens } from '../elevation/';
24
24
  * radius: "0.375rem"
25
25
  * },
26
26
  * typography: {
27
- * fontFamily: "Hanken Grotesk, sans-serif"
27
+ * fontFamilyPrimary: "Hanken Grotesk, sans-serif"
28
28
  * },
29
29
  * elevation: {
30
30
  * "0": "none",
@@ -39,21 +39,66 @@ export type Tokens = {
39
39
  spacing: SpacingTokens;
40
40
  border: {
41
41
  radius: string;
42
+ width: string;
42
43
  };
43
44
  typography: {
44
- fontFamily: string;
45
- };
45
+ fontFamilyPrimary: string;
46
+ } & Record<string, string>;
46
47
  elevation: ElevationTokens;
47
48
  };
48
49
  export type { Config, TokenColorName } from './';
50
+ /**
51
+ * Border width applied to control chrome (outlined buttons, inputs, chips, surfaces).
52
+ * Named presets: `thin` = 1px (default), `medium` = 2px, `thick` = 4px.
53
+ * Any other CSS length (e.g. `'1.5px'`) passes through unchanged.
54
+ */
55
+ export type BorderWidth = 'thin' | 'medium' | 'thick' | (string & {});
56
+ /** Generic CSS font-family keyword used as the safety-net fallback if a CDN font fails to load. */
57
+ export type FontFallback = 'sans-serif' | 'serif' | 'monospace' | 'cursive' | 'fantasy' | 'system-ui';
58
+ /**
59
+ * A single `font.family` entry: a plain CSS value, a Google Fonts CSS2 URL, or
60
+ * a URL paired with a custom generic fallback (default `'sans-serif'`) for
61
+ * when the derived family isn't sans-serif — e.g. a serif display font.
62
+ */
63
+ export type FontFamilyEntry = string | {
64
+ url: string;
65
+ fallback?: FontFallback;
66
+ };
49
67
  export interface LattyConfig {
50
68
  /** Built-in names: primary, secondary, success, warning, error, info. Any additional name generates a new palette. */
51
69
  colors?: Record<string, string>;
52
70
  font?: {
53
- family?: string;
71
+ /**
72
+ * A CSS `font-family` value (e.g. `'Inter, sans-serif'`), a Google Fonts
73
+ * CSS2 stylesheet URL (e.g.
74
+ * `'https://fonts.googleapis.com/css2?family=Hanken+Grotesk:ital,wght@0,100..900&display=swap'`),
75
+ * a `{ url, fallback }` object, or an array of any of these. URLs get
76
+ * `@import`ed automatically and the family name is derived from the
77
+ * `family=` query param — no separate `<link>` tag needed. Stylesheet
78
+ * URLs from providers other than `fonts.googleapis.com` are still
79
+ * loaded, but since there's no reliable way to guess the family name
80
+ * from an arbitrary CDN, pass the resulting name as a plain value
81
+ * instead (or open an issue if there's a provider worth special-casing).
82
+ *
83
+ * A bare URL string always falls back to the generic `sans-serif` family
84
+ * if the font itself fails to load. Use the `{ url, fallback }` object
85
+ * form to pick a different generic fallback (e.g. `'serif'` for a serif
86
+ * display font):
87
+ * ```ts
88
+ * family: { url: 'https://fonts.googleapis.com/css2?family=Playfair+Display', fallback: 'serif' }
89
+ * ```
90
+ *
91
+ * When an array is passed, each entry maps to its own token in order —
92
+ * `--lt-typography-fontFamilyPrimary`, `...Secondary`, `...Tertiary`,
93
+ * etc. — rather than being combined into a single CSS fallback stack, so
94
+ * multiple fonts (e.g. a heading font and a body font) can each be
95
+ * referenced independently in your own CSS.
96
+ */
97
+ family?: FontFamilyEntry | FontFamilyEntry[];
54
98
  };
55
99
  border?: {
56
100
  radius?: string;
101
+ width?: BorderWidth;
57
102
  };
58
103
  /**
59
104
  * Controls which semantic token layer is emitted.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latty-ds/tokens",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Design tokens for the Latty design system — generates CSS custom properties, JSON, and JavaScript exports from a single config file",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -60,5 +60,5 @@
60
60
  "dependencies": {
61
61
  "culori": "^4.0.2"
62
62
  },
63
- "gitHead": "40d5903d30b50455ed244d5f0fd75fbd84934c46"
63
+ "gitHead": "8d5627a4a71bb4c7efdf3b754198c6450ce5467f"
64
64
  }