@f-ewald/components 1.0.2 → 1.1.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.
Files changed (111) hide show
  1. package/README.md +4 -1
  2. package/custom-elements.json +7 -7
  3. package/dist/address-autocomplete.d.ts.map +1 -1
  4. package/dist/address-autocomplete.js +3 -1
  5. package/dist/address-autocomplete.js.map +1 -1
  6. package/dist/autocomplete-input.d.ts.map +1 -1
  7. package/dist/autocomplete-input.js +3 -1
  8. package/dist/autocomplete-input.js.map +1 -1
  9. package/dist/calendar-month.d.ts.map +1 -1
  10. package/dist/calendar-month.js +9 -8
  11. package/dist/calendar-month.js.map +1 -1
  12. package/dist/chat-message.d.ts.map +1 -1
  13. package/dist/chat-message.js +15 -5
  14. package/dist/chat-message.js.map +1 -1
  15. package/dist/confirm-dialog.d.ts.map +1 -1
  16. package/dist/confirm-dialog.js +24 -6
  17. package/dist/confirm-dialog.js.map +1 -1
  18. package/dist/copy-link-button.d.ts.map +1 -1
  19. package/dist/copy-link-button.js +4 -2
  20. package/dist/copy-link-button.js.map +1 -1
  21. package/dist/data-table.js +1 -1
  22. package/dist/data-table.js.map +1 -1
  23. package/dist/distribution-chart.d.ts +2 -2
  24. package/dist/distribution-chart.js +4 -4
  25. package/dist/distribution-chart.js.map +1 -1
  26. package/dist/dropdown-button.d.ts.map +1 -1
  27. package/dist/dropdown-button.js +13 -2
  28. package/dist/dropdown-button.js.map +1 -1
  29. package/dist/form-select.d.ts.map +1 -1
  30. package/dist/form-select.js +32 -8
  31. package/dist/form-select.js.map +1 -1
  32. package/dist/frame-box.js +3 -3
  33. package/dist/frame-box.js.map +1 -1
  34. package/dist/icon-button.d.ts +1 -1
  35. package/dist/icon-button.js +1 -1
  36. package/dist/icon-button.js.map +1 -1
  37. package/dist/kbd-hint.js +1 -1
  38. package/dist/kbd-hint.js.map +1 -1
  39. package/dist/map-circle.js +2 -2
  40. package/dist/map-circle.js.map +1 -1
  41. package/dist/map-pin.js +2 -2
  42. package/dist/map-pin.js.map +1 -1
  43. package/dist/percent-bar-chart.js +1 -1
  44. package/dist/percent-bar-chart.js.map +1 -1
  45. package/dist/photo-gallery.d.ts.map +1 -1
  46. package/dist/photo-gallery.js +33 -9
  47. package/dist/photo-gallery.js.map +1 -1
  48. package/dist/popover-panel.d.ts.map +1 -1
  49. package/dist/popover-panel.js +7 -4
  50. package/dist/popover-panel.js.map +1 -1
  51. package/dist/radio-cards.d.ts.map +1 -1
  52. package/dist/radio-cards.js +4 -3
  53. package/dist/radio-cards.js.map +1 -1
  54. package/dist/radio-pills.d.ts.map +1 -1
  55. package/dist/radio-pills.js +7 -4
  56. package/dist/radio-pills.js.map +1 -1
  57. package/dist/reveal-button.d.ts.map +1 -1
  58. package/dist/reveal-button.js +4 -1
  59. package/dist/reveal-button.js.map +1 -1
  60. package/dist/slide-panel.d.ts.map +1 -1
  61. package/dist/slide-panel.js +8 -5
  62. package/dist/slide-panel.js.map +1 -1
  63. package/dist/stat-meter.js +5 -5
  64. package/dist/stat-meter.js.map +1 -1
  65. package/dist/status-pill.js +2 -2
  66. package/dist/status-pill.js.map +1 -1
  67. package/dist/tile-grid.js +1 -1
  68. package/dist/tile-grid.js.map +1 -1
  69. package/dist/toast-notification.d.ts.map +1 -1
  70. package/dist/toast-notification.js +10 -5
  71. package/dist/toast-notification.js.map +1 -1
  72. package/dist/tokens.css +10 -0
  73. package/dist/tokens.d.ts.map +1 -1
  74. package/dist/tokens.js +15 -2
  75. package/dist/tokens.js.map +1 -1
  76. package/dist/ui-button.d.ts.map +1 -1
  77. package/dist/ui-button.js +3 -1
  78. package/dist/ui-button.js.map +1 -1
  79. package/dist/user-avatar.js +1 -1
  80. package/dist/user-avatar.js.map +1 -1
  81. package/dist/weight-bar-chart.js +2 -2
  82. package/dist/weight-bar-chart.js.map +1 -1
  83. package/docs/address-autocomplete.md +1 -0
  84. package/docs/autocomplete-input.md +1 -0
  85. package/docs/calendar-month.md +6 -1
  86. package/docs/chat-message.md +2 -0
  87. package/docs/confirm-dialog.md +3 -0
  88. package/docs/data-table.md +1 -0
  89. package/docs/design-language.md +68 -40
  90. package/docs/distribution-chart.md +3 -2
  91. package/docs/dropdown-button.md +2 -0
  92. package/docs/form-select.md +2 -0
  93. package/docs/frame-box.md +3 -1
  94. package/docs/icon-button.md +1 -1
  95. package/docs/kbd-hint.md +1 -0
  96. package/docs/map-circle.md +2 -0
  97. package/docs/map-pin.md +2 -0
  98. package/docs/photo-gallery.md +1 -0
  99. package/docs/popover-panel.md +3 -1
  100. package/docs/radio-cards.md +2 -0
  101. package/docs/radio-pills.md +1 -0
  102. package/docs/reveal-button.md +3 -1
  103. package/docs/slide-panel.md +3 -1
  104. package/docs/stat-meter.md +3 -0
  105. package/docs/status-pill.md +1 -0
  106. package/docs/toast-notification.md +2 -0
  107. package/docs/ui-button.md +2 -0
  108. package/docs/user-avatar.md +1 -0
  109. package/docs/weight-bar-chart.md +0 -1
  110. package/llms.txt +27 -27
  111. package/package.json +1 -3
@@ -55,7 +55,7 @@ let WeightBarChart = class WeightBarChart extends LitElement {
55
55
  flex: 1 1 auto;
56
56
  height: 0.75rem;
57
57
  background: var(--ui-surface-muted, #f8fafc);
58
- border-radius: var(--ui-radius-sm, 0.25rem);
58
+ border-radius: 9999px;
59
59
  overflow: hidden;
60
60
  }
61
61
  .bar {
@@ -66,7 +66,7 @@ let WeightBarChart = class WeightBarChart extends LitElement {
66
66
  color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,
67
67
  color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%
68
68
  );
69
- border-radius: 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0;
69
+ border-radius: 0 9999px 9999px 0;
70
70
  transition: width 150ms ease-out;
71
71
  }
72
72
  .pct {
@@ -1 +1 @@
1
- {"version":3,"file":"weight-bar-chart.js","sourceRoot":"","sources":["../src/weight-bar-chart.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;;;;;;GASG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,oEAAoE;QACpC,UAAK,GAAoB,EAAE,CAAC;IAsF9D,CAAC;aApFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuDF;KACF,AA1DqB,CA0DpB;IAEO,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,+EAA+E;QAC/E,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClG,OAAO,IAAI,CAAA;6CAC8B,OAAO;UAC1C,MAAM,CACN,MAAM,EACN,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EACjB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAA;;oCAEY,IAAI,CAAC,KAAK;;iDAEG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;;kCAEvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;;WAEnD,CACF;;KAEJ,CAAC;IACJ,CAAC;CACF,CAAA;AAtFiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;6CAA6B;AAFjD,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CAwF1B","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport interface WeightBarItem {\n id: string;\n label: string;\n /** Normalized fraction (fractions across all items sum to ~1). */\n value: number;\n}\n\n/**\n * Sorted horizontal bar chart of labeled weights (normalized fractions\n * summing to ~1). Bars sort descending — the order IS the priority ranking.\n * Widths scale relative to the largest weight (which fills its track); the\n * percent labels carry the absolute values. Rows are keyed by item id\n * (repeat directive) so a re-render with new weights moves the existing\n * rows; bar widths animate via CSS, re-sorting is instant.\n *\n * @element weight-bar-chart\n */\n@customElement(\"weight-bar-chart\")\nexport class WeightBarChart extends LitElement {\n /** Items to render as weighted rows, sorted descending by value. */\n @property({ attribute: false }) items: WeightBarItem[] = [];\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n }\n .row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin: 0.5rem 0;\n }\n .label {\n flex: 0 0 5.5rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n color: var(--ui-text, #0f172a);\n }\n .track {\n flex: 1 1 auto;\n height: 0.75rem;\n background: var(--ui-surface-muted, #f8fafc);\n border-radius: var(--ui-radius-sm, 0.25rem);\n overflow: hidden;\n }\n .bar {\n height: 100%;\n /* Match map-circle's 30% white/black vertical depth while preserving token overrides. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%\n );\n border-radius: 0 var(--ui-radius-sm, 0.25rem) var(--ui-radius-sm, 0.25rem) 0;\n transition: width 150ms ease-out;\n }\n .pct {\n flex: 0 0 2.5rem;\n text-align: right;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-variant-numeric: tabular-nums;\n color: var(--ui-text-muted, #64748b);\n }\n @media (prefers-reduced-motion: reduce) {\n .bar {\n transition: none;\n }\n }\n `,\n ];\n\n override render() {\n if (this.items.length === 0) return null;\n // Stable sort: ties keep the incoming item order, so the chart never flickers.\n const sorted = [...this.items].sort((a, b) => b.value - a.value);\n const max = Math.max(0.01, ...sorted.map((item) => item.value));\n const summary = sorted.map((item) => `${item.label} ${Math.round(item.value * 100)}%`).join(\", \");\n return html`\n <div role=\"img\" aria-label=\"Weights: ${summary}\">\n ${repeat(\n sorted,\n (item) => item.id,\n (item) => html`\n <div class=\"row\">\n <span class=\"label\">${item.label}</span>\n <div class=\"track\">\n <div class=\"bar\" style=\"width: ${(item.value / max) * 100}%\"></div>\n </div>\n <span class=\"pct\">${Math.round(item.value * 100)}%</span>\n </div>\n `,\n )}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"weight-bar-chart\": WeightBarChart;\n }\n}\n"]}
1
+ {"version":3,"file":"weight-bar-chart.js","sourceRoot":"","sources":["../src/weight-bar-chart.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;;;;;;GASG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACL,oEAAoE;QACpC,UAAK,GAAoB,EAAE,CAAC;IAsF9D,CAAC;aApFiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuDF;KACF,AA1DqB,CA0DpB;IAEO,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,+EAA+E;QAC/E,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClG,OAAO,IAAI,CAAA;6CAC8B,OAAO;UAC1C,MAAM,CACN,MAAM,EACN,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EACjB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAA;;oCAEY,IAAI,CAAC,KAAK;;iDAEG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;;kCAEvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;;WAEnD,CACF;;KAEJ,CAAC;IACJ,CAAC;CACF,CAAA;AAtFiC;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;6CAA6B;AAFjD,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CAwF1B","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport interface WeightBarItem {\n id: string;\n label: string;\n /** Normalized fraction (fractions across all items sum to ~1). */\n value: number;\n}\n\n/**\n * Sorted horizontal bar chart of labeled weights (normalized fractions\n * summing to ~1). Bars sort descending — the order IS the priority ranking.\n * Widths scale relative to the largest weight (which fills its track); the\n * percent labels carry the absolute values. Rows are keyed by item id\n * (repeat directive) so a re-render with new weights moves the existing\n * rows; bar widths animate via CSS, re-sorting is instant.\n *\n * @element weight-bar-chart\n */\n@customElement(\"weight-bar-chart\")\nexport class WeightBarChart extends LitElement {\n /** Items to render as weighted rows, sorted descending by value. */\n @property({ attribute: false }) items: WeightBarItem[] = [];\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n }\n .row {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin: 0.5rem 0;\n }\n .label {\n flex: 0 0 5.5rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n color: var(--ui-text, #0f172a);\n }\n .track {\n flex: 1 1 auto;\n height: 0.75rem;\n background: var(--ui-surface-muted, #f8fafc);\n border-radius: 9999px;\n overflow: hidden;\n }\n .bar {\n height: 100%;\n /* Match map-circle's 30% white/black vertical depth while preserving token overrides. */\n background: linear-gradient(\n to bottom,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #ffffff) 0%,\n color-mix(in srgb, var(--ui-primary, #4f46e5) 70%, #000000) 100%\n );\n border-radius: 0 9999px 9999px 0;\n transition: width 150ms ease-out;\n }\n .pct {\n flex: 0 0 2.5rem;\n text-align: right;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-variant-numeric: tabular-nums;\n color: var(--ui-text-muted, #64748b);\n }\n @media (prefers-reduced-motion: reduce) {\n .bar {\n transition: none;\n }\n }\n `,\n ];\n\n override render() {\n if (this.items.length === 0) return null;\n // Stable sort: ties keep the incoming item order, so the chart never flickers.\n const sorted = [...this.items].sort((a, b) => b.value - a.value);\n const max = Math.max(0.01, ...sorted.map((item) => item.value));\n const summary = sorted.map((item) => `${item.label} ${Math.round(item.value * 100)}%`).join(\", \");\n return html`\n <div role=\"img\" aria-label=\"Weights: ${summary}\">\n ${repeat(\n sorted,\n (item) => item.id,\n (item) => html`\n <div class=\"row\">\n <span class=\"label\">${item.label}</span>\n <div class=\"track\">\n <div class=\"bar\" style=\"width: ${(item.value / max) * 100}%\"></div>\n </div>\n <span class=\"pct\">${Math.round(item.value * 100)}%</span>\n </div>\n `,\n )}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"weight-bar-chart\": WeightBarChart;\n }\n}\n"]}
@@ -64,6 +64,7 @@ _None._
64
64
  | `--ui-focus-ring` |
65
65
  | `--ui-font` |
66
66
  | `--ui-font-size-sm` |
67
+ | `--ui-line-height-tight` |
67
68
  | `--ui-primary` |
68
69
  | `--ui-radius-sm` |
69
70
  | `--ui-shadow` |
@@ -75,6 +75,7 @@ _None._
75
75
  | `--ui-focus-ring` |
76
76
  | `--ui-font` |
77
77
  | `--ui-font-size-sm` |
78
+ | `--ui-line-height-tight` |
78
79
  | `--ui-primary` |
79
80
  | `--ui-radius-sm` |
80
81
  | `--ui-shadow` |
@@ -64,11 +64,16 @@ _None._
64
64
  | `--ui-danger` |
65
65
  | `--ui-focus-ring` |
66
66
  | `--ui-font` |
67
- | `--ui-font-size` |
67
+ | `--ui-font-size-lg` |
68
68
  | `--ui-font-size-sm` |
69
69
  | `--ui-font-size-xs` |
70
+ | `--ui-font-weight-bold` |
71
+ | `--ui-font-weight-medium` |
72
+ | `--ui-font-weight-regular` |
73
+ | `--ui-font-weight-semibold` |
70
74
  | `--ui-hover-overlay` |
71
75
  | `--ui-info` |
76
+ | `--ui-line-height-tight` |
72
77
  | `--ui-primary` |
73
78
  | `--ui-radius-sm` |
74
79
  | `--ui-success` |
@@ -55,6 +55,8 @@ _None._
55
55
  | `--ui-font-mono` |
56
56
  | `--ui-font-size` |
57
57
  | `--ui-font-size-sm` |
58
+ | `--ui-font-weight-semibold` |
59
+ | `--ui-line-height-normal` |
58
60
  | `--ui-primary` |
59
61
  | `--ui-radius` |
60
62
  | `--ui-radius-sm` |
@@ -55,6 +55,9 @@ import "@f-ewald/components/confirm-dialog.js";
55
55
  | `--ui-font` |
56
56
  | `--ui-font-size` |
57
57
  | `--ui-font-size-sm` |
58
+ | `--ui-font-weight-medium` |
59
+ | `--ui-line-height-normal` |
60
+ | `--ui-line-height-tight` |
58
61
  | `--ui-on-accent` |
59
62
  | `--ui-overlay` |
60
63
  | `--ui-primary` |
@@ -60,6 +60,7 @@ _None._
60
60
  | `--ui-focus-ring` |
61
61
  | `--ui-font` |
62
62
  | `--ui-font-size-sm` |
63
+ | `--ui-font-weight-semibold` |
63
64
  | `--ui-surface-muted` |
64
65
  | `--ui-text` |
65
66
  | `--ui-text-muted` |
@@ -17,8 +17,10 @@ defines the details used when creating or reviewing components.
17
17
 
18
18
  ## Tokens and colors
19
19
 
20
- - Use `var(--ui-*, exact-fallback)` for reusable colors, fonts, radii, shadows,
21
- and focus rings.
20
+ - Use `var(--ui-*, exact-fallback)` for reusable colors, fonts, type metrics,
21
+ radii, shadows, and focus rings. `tokens.ts` (`tokenValues`) is the single
22
+ source of truth; every `var(--ui-*, …)` usage must repeat that token's exact
23
+ fallback, and any newly introduced `--ui-*` usage must add/carry one too.
22
24
  - Semantic states use `primary`, `info`, `success`, `warning`, and `danger`.
23
25
  - Foregrounds on solid semantic fills use `--ui-on-accent`.
24
26
  - Elevated dark tooltips use `--ui-tooltip`; modal backdrops use
@@ -34,24 +36,59 @@ defines the details used when creating or reviewing components.
34
36
 
35
37
  ## Typography
36
38
 
37
- - Body/component text: `--ui-font-size` at regular weight.
38
- - Compact controls/labels: `--ui-font-size-sm`, usually weight 500.
39
- - Secondary details/keycaps: `--ui-font-size-xs`.
40
- - Code/keycaps: `--ui-font-mono`.
41
- - Section/title emphasis should normally use weight 600; reserve 700 for badges
42
- or data emphasis.
39
+ Type is fully tokenized; every axis has an exact-fallback `--ui-*` token and
40
+ literal values are migrated onto them.
41
+
42
+ - **Family:** `--ui-font` (system sans stack) for UI text; `--ui-font-mono`
43
+ for code and keycaps. `font-family: inherit` is reserved for components that
44
+ intentionally adopt the host's type — `editable-text` and the inline
45
+ formatters — so an inline edit or formatted value matches surrounding copy.
46
+ - **Size:** `--ui-font-size` (`0.875rem`) for body/component text,
47
+ `--ui-font-size-sm` (`0.75rem`) for compact controls/labels,
48
+ `--ui-font-size-xs` (`0.6875rem`) for secondary details/keycaps, and
49
+ `--ui-font-size-lg` (`1rem`) for the largest titles/emphasis.
50
+ - **Weight:** only four weights exist —
51
+ `--ui-font-weight-regular` (400), `--ui-font-weight-medium` (500),
52
+ `--ui-font-weight-semibold` (600), and `--ui-font-weight-bold` (700). Body is
53
+ regular, compact controls/labels are medium, section/title emphasis is
54
+ semibold, and bold is reserved for badges or data emphasis.
55
+ - **Line height:** `--ui-line-height-glyph` (1) for single-glyph icon/marker
56
+ boxes, `--ui-line-height-tight` (1.25) for headings and compact multi-line
57
+ labels, and `--ui-line-height-normal` (1.5) for running body text.
58
+ - **Tracking:** `--ui-tracking-normal` (0) is the default; `--ui-tracking-wide`
59
+ (0.04em) is the single widened step for uppercase micro-labels.
60
+ - **Exemptions:** SVG presentation attributes (`font-size="…"`,
61
+ `font-weight="…"`) can't take `var()` and stay literal at the token's
62
+ fallback value; `line-height: 0` used purely to collapse an inline
63
+ icon/SVG box is domain geometry, not a type choice.
43
64
 
44
65
  ## Spacing and geometry
45
66
 
46
- - Layout spacing uses literal multiples of `0.25rem`; spacing is not tokenized.
47
- - Convert CSS layout pixels to rems.
48
- - Exceptions: 1px borders, SVG/canvas geometry, percentages, aspect ratios,
49
- animation timing, and proportional calculations derived from a public size.
50
- - Use `--ui-radius-sm` for controls, `--ui-radius` for cards/dialogs, and an
51
- intentional full pill/circle radius only for pill/avatar/marker shapes.
67
+ - `padding`, `margin`, and `gap` use literal multiples of `0.25rem`; spacing is
68
+ not tokenized. Convert CSS layout pixels to rems.
69
+ - Documented spacing exceptions: 1px borders, SVG/canvas geometry, percentages,
70
+ aspect ratios, animation timing, proportional calculations derived from a
71
+ public size, `0.125rem` optical alignment nudges, and domain geometry.
72
+ - Use `--ui-radius-sm` (`0.25rem` ≈ 4px) for controls, `--ui-radius`
73
+ (`0.5rem` ≈ 8px) for cards/dialogs/surfaces, and an intentional full
74
+ pill/circle radius only for pill/avatar/marker shapes.
52
75
  - Compact controls must retain a clear hit target and must not reserve layout
53
76
  space for optional content that is absent.
54
77
 
78
+ ### Control metrics
79
+
80
+ - Standard interactive controls share a single `2rem` height; padding is
81
+ applied by role (inline text, icon-only, standalone) rather than ad hoc.
82
+ - Icons are `14px` inline (next to text) and `18px` standalone; icon-only tap
83
+ targets are `32px`.
84
+ - Control corners use `--ui-radius-sm` (4px); enclosing surfaces use
85
+ `--ui-radius` (8px).
86
+ - Side panels use a `20rem` compact / `25rem` comfortable width; the shared
87
+ responsive breakpoint is `48rem`.
88
+ - Radio inputs render at `1rem`.
89
+ - Charts and metadata-only components keep their own domain geometry and are
90
+ exempt from the control-metric grid.
91
+
55
92
  ## Interaction states
56
93
 
57
94
  - Interactive surfaces provide hover, active/selected, disabled, and
@@ -87,8 +124,8 @@ defines the details used when creating or reviewing components.
87
124
 
88
125
  ## Component exceptions
89
126
 
90
- - Metadata-only tags may omit standalone playground sections and screenshots
91
- when their full behavior is demonstrated through a parent:
127
+ - Metadata-only tags may omit standalone playground sections when their full
128
+ behavior is demonstrated through a parent:
92
129
  `calendar-entry`, `gallery-item`, and `gallery-item-variant`.
93
130
  - Styleless inline formatters may omit empty `static styles`/token imports:
94
131
  `distance-value`, `live-timer`, `relative-time`, and `roman-numeral`.
@@ -107,38 +144,29 @@ defines the details used when creating or reviewing components.
107
144
  - Public API changes update JSDoc, generated docs, `llms.txt`, README when
108
145
  relevant, and the changelog.
109
146
 
147
+ ## Measurement contracts
148
+
149
+ - `design-tests/design-language.spec.ts` enforces the token, import, catalog,
150
+ and measurement rules deterministically. The measurement contracts cover
151
+ tokenized font family/size/weight/leading/tracking, the `0.25rem` spacing
152
+ grid, 14px/18px icon calls, 2rem interactive targets, surface padding,
153
+ panel widths, and the shared 48rem breakpoint.
154
+ - Narrow file-scoped allowlists are permitted only for documented domain
155
+ geometry that cannot use a semantic token. They must equal the live literal
156
+ inventory exactly, so stale entries and new unapproved literals both fail.
157
+ - Never add an allowlist entry merely to unblock new work, and never weaken the
158
+ token-fallback, `.js`-import, catalog, or measurement checks.
159
+
110
160
  ## Validation tiers
111
161
 
112
162
  1. Inner loop: `npm run typecheck` and all touched component specs in one
113
163
  Playwright invocation.
114
164
  2. Run docs once after APIs/JSDoc stabilize.
115
165
  3. Run `npm run test:design` after token/catalog/state changes.
116
- 4. Run screenshots once after the full design batch.
117
- 5. Run the complete package/demo/Pages gate once at task completion.
118
- 6. Reviewer fixes rerun only affected specs/contracts, followed by one final
166
+ 4. Run the complete package/demo/Pages gate once at task completion.
167
+ 5. Reviewer fixes rerun only affected specs/contracts, followed by one final
119
168
  full gate.
120
169
 
121
- Canonical screenshot baselines are generated by the **Generate visual
122
- baselines** GitHub Actions workflow on the same amd64 runner image used by CI.
123
- Run it manually, download the `visual-baselines-linux-amd64` artifact, review
124
- the images, and replace `visual-tests/__screenshots__/`.
125
-
126
- For local iteration, the pinned Playwright image provides a close
127
- architecture-matched preview:
128
-
129
- ```bash
130
- docker run --platform linux/amd64 --rm --ipc=host \
131
- -v "$PWD":/work -v /work/node_modules -w /work \
132
- mcr.microsoft.com/playwright:v1.61.1-noble \
133
- bash -lc "npm ci && npm run test:visual:update"
134
- ```
135
-
136
- The architecture pin is required on Apple-silicon hosts, but installed fonts
137
- still differ slightly from GitHub-hosted runners. Local Docker repeatability
138
- does not replace the GitHub-generated baseline check. Run
139
- `npm run test:visual` natively only when using a matching checked-in platform
140
- baseline.
141
-
142
170
  ## Review checklist
143
171
 
144
172
  - Shared tokens use exact fallbacks; data-color exceptions are intentional.
@@ -5,7 +5,7 @@ markers. The SVG viewBox is kept in sync with the element's pixel width via
5
5
  ResizeObserver so that font sizes and stroke widths are always in real pixels
6
6
  regardless of container width.
7
7
 
8
- Pass `fontSize` to control all text size (default 11). Pass a single
8
+ Pass `fontSize` to control all text size (default 10). Pass a single
9
9
  `{label:'', value}` for a single-value display or multiple
10
10
  `{label:'A'|'B'|...}` entries to compare several values.
11
11
 
@@ -31,7 +31,7 @@ import "@f-ewald/components/distribution-chart.js";
31
31
  | `metric` | `metric` | `string` | `""` | Metric name, fetched from `/api/distribution/<metric>` on change. |
32
32
  | `values` | _(JS property only)_ | `DistributionValue[]` | `[]` | One or more values to mark on the distribution curve. |
33
33
  | `markerColors` | _(JS property only)_ | `string[]` | `["#4f46e5", "#d97706", "#0d9488", "#e11d48"]` | Colors assigned to markers in order. Defaults to indigo/amber/teal/rose 600s. |
34
- | `fontSize` | `font-size` | `number` | `11` | Target font size in CSS pixels (default 11). Always renders at this size regardless of container width. |
34
+ | `fontSize` | `font-size` | `number` | `10` | Target font size in CSS pixels (default 10). Always renders at this size regardless of container width. |
35
35
 
36
36
  ## Events
37
37
 
@@ -49,6 +49,7 @@ _None._
49
49
  | `--ui-danger` |
50
50
  | `--ui-font` |
51
51
  | `--ui-font-size-sm` |
52
+ | `--ui-font-size-xs` |
52
53
  | `--ui-primary` |
53
54
  | `--ui-radius-sm` |
54
55
  | `--ui-surface-muted` |
@@ -52,6 +52,8 @@ _None._
52
52
  | `--ui-focus-ring` |
53
53
  | `--ui-font` |
54
54
  | `--ui-font-size-sm` |
55
+ | `--ui-font-weight-medium` |
56
+ | `--ui-line-height-tight` |
55
57
  | `--ui-on-accent` |
56
58
  | `--ui-primary` |
57
59
  | `--ui-primary-hover` |
@@ -73,6 +73,8 @@ _None._
73
73
  | `--ui-focus-ring` |
74
74
  | `--ui-font` |
75
75
  | `--ui-font-size-sm` |
76
+ | `--ui-font-weight-semibold` |
77
+ | `--ui-line-height-tight` |
76
78
  | `--ui-primary` |
77
79
  | `--ui-radius-sm` |
78
80
  | `--ui-shadow` |
package/docs/frame-box.md CHANGED
@@ -40,6 +40,8 @@ _None._
40
40
  | `--ui-border` |
41
41
  | `--ui-font` |
42
42
  | `--ui-font-size-xs` |
43
- | `--ui-radius-sm` |
43
+ | `--ui-font-weight-semibold` |
44
+ | `--ui-radius` |
44
45
  | `--ui-surface` |
45
46
  | `--ui-text-muted` |
47
+ | `--ui-tracking-wide` |
@@ -27,7 +27,7 @@ import "@f-ewald/components/icon-button.js";
27
27
 
28
28
  | Property | Attribute | Type | Default | Description |
29
29
  | --- | --- | --- | --- | --- |
30
- | `icon` | _(JS property only)_ | `TemplateResult | null` | `null` | Pre-rendered icon template, e.g. `iconPencil(16)` from this package's icon set. |
30
+ | `icon` | _(JS property only)_ | `TemplateResult | null` | `null` | Pre-rendered icon template, e.g. `iconPencil(18)` from this package's icon set. |
31
31
  | `label` | `label` | `string` | `""` | Required accessible label, applied as `aria-label`/`title`. |
32
32
  | `disabled` | `disabled` | `boolean` | `false` | Disables the button and dims it. |
33
33
 
package/docs/kbd-hint.md CHANGED
@@ -39,4 +39,5 @@ _None._
39
39
  | --- |
40
40
  | `--ui-font-mono` |
41
41
  | `--ui-font-size-xs` |
42
+ | `--ui-line-height-glyph` |
42
43
  | `--ui-radius-sm` |
@@ -47,3 +47,5 @@ _None._
47
47
  | Custom property |
48
48
  | --- |
49
49
  | `--ui-font` |
50
+ | `--ui-font-weight-bold` |
51
+ | `--ui-line-height-glyph` |
package/docs/map-pin.md CHANGED
@@ -41,3 +41,5 @@ _None._
41
41
  | Custom property |
42
42
  | --- |
43
43
  | `--ui-font` |
44
+ | `--ui-font-weight-bold` |
45
+ | `--ui-line-height-glyph` |
@@ -67,6 +67,7 @@ import "@f-ewald/components/photo-gallery.js";
67
67
  | `--ui-font` |
68
68
  | `--ui-font-size` |
69
69
  | `--ui-font-size-sm` |
70
+ | `--ui-line-height-tight` |
70
71
  | `--ui-on-accent` |
71
72
  | `--ui-overlay` |
72
73
  | `--ui-primary` |
@@ -61,7 +61,9 @@ import "@f-ewald/components/popover-panel.js";
61
61
  | `--ui-border` |
62
62
  | `--ui-focus-ring` |
63
63
  | `--ui-font` |
64
- | `--ui-font-size` |
64
+ | `--ui-font-size-lg` |
65
+ | `--ui-font-weight-semibold` |
66
+ | `--ui-line-height-tight` |
65
67
  | `--ui-overlay` |
66
68
  | `--ui-radius` |
67
69
  | `--ui-radius-sm` |
@@ -53,6 +53,8 @@ _None._
53
53
  | `--ui-focus-ring` |
54
54
  | `--ui-font` |
55
55
  | `--ui-font-size-sm` |
56
+ | `--ui-font-weight-regular` |
57
+ | `--ui-font-weight-semibold` |
56
58
  | `--ui-primary` |
57
59
  | `--ui-radius-sm` |
58
60
  | `--ui-surface-muted` |
@@ -53,6 +53,7 @@ _None._
53
53
  | `--ui-focus-ring` |
54
54
  | `--ui-font` |
55
55
  | `--ui-font-size-sm` |
56
+ | `--ui-line-height-tight` |
56
57
  | `--ui-primary` |
57
58
  | `--ui-surface-muted` |
58
59
  | `--ui-text` |
@@ -39,7 +39,9 @@ _None._
39
39
  | --- |
40
40
  | `--ui-focus-ring` |
41
41
  | `--ui-font` |
42
- | `--ui-font-size` |
42
+ | `--ui-font-size-sm` |
43
+ | `--ui-font-weight-medium` |
44
+ | `--ui-line-height-tight` |
43
45
  | `--ui-on-accent` |
44
46
  | `--ui-primary` |
45
47
  | `--ui-primary-hover` |
@@ -48,7 +48,9 @@ import "@f-ewald/components/slide-panel.js";
48
48
  | `--ui-border` |
49
49
  | `--ui-focus-ring` |
50
50
  | `--ui-font` |
51
- | `--ui-font-size` |
51
+ | `--ui-font-size-lg` |
52
+ | `--ui-font-weight-semibold` |
53
+ | `--ui-line-height-tight` |
52
54
  | `--ui-radius` |
53
55
  | `--ui-radius-sm` |
54
56
  | `--ui-shadow-lg` |
@@ -42,7 +42,10 @@ _None._
42
42
  | --- |
43
43
  | `--ui-font` |
44
44
  | `--ui-font-size-sm` |
45
+ | `--ui-font-weight-medium` |
46
+ | `--ui-font-weight-semibold` |
45
47
  | `--ui-success` |
46
48
  | `--ui-surface-muted` |
47
49
  | `--ui-text` |
48
50
  | `--ui-text-muted` |
51
+ | `--ui-tracking-wide` |
@@ -39,6 +39,7 @@ _None._
39
39
  | `--ui-danger` |
40
40
  | `--ui-font` |
41
41
  | `--ui-font-size-sm` |
42
+ | `--ui-font-weight-medium` |
42
43
  | `--ui-info` |
43
44
  | `--ui-primary` |
44
45
  | `--ui-success` |
@@ -47,6 +47,8 @@ _None._
47
47
  | `--ui-font-size` |
48
48
  | `--ui-hover-overlay` |
49
49
  | `--ui-info` |
50
+ | `--ui-line-height-glyph` |
51
+ | `--ui-line-height-normal` |
50
52
  | `--ui-on-accent` |
51
53
  | `--ui-radius` |
52
54
  | `--ui-radius-sm` |
package/docs/ui-button.md CHANGED
@@ -61,6 +61,8 @@ _None._
61
61
  | `--ui-focus-ring` |
62
62
  | `--ui-font` |
63
63
  | `--ui-font-size-sm` |
64
+ | `--ui-font-weight-medium` |
65
+ | `--ui-line-height-tight` |
64
66
  | `--ui-on-accent` |
65
67
  | `--ui-primary` |
66
68
  | `--ui-primary-hover` |
@@ -39,5 +39,6 @@ _None._
39
39
  | Custom property |
40
40
  | --- |
41
41
  | `--ui-font` |
42
+ | `--ui-font-weight-semibold` |
42
43
  | `--ui-on-accent` |
43
44
  | `--ui-primary` |
@@ -47,7 +47,6 @@ _None._
47
47
  | `--ui-font` |
48
48
  | `--ui-font-size-sm` |
49
49
  | `--ui-primary` |
50
- | `--ui-radius-sm` |
51
50
  | `--ui-surface-muted` |
52
51
  | `--ui-text` |
53
52
  | `--ui-text-muted` |