@livenetworks/ashlar 1.5.1 → 1.6.1

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 (217) hide show
  1. package/README.md +189 -177
  2. package/js/COMPONENTS.md +55 -49
  3. package/js/index.js +22 -5
  4. package/js/ln-accordion/README.md +186 -137
  5. package/js/ln-accordion/ln-accordion.js +1 -1
  6. package/js/ln-ajax/README.md +73 -4
  7. package/js/ln-ajax/ln-ajax.js +1 -1
  8. package/js/ln-ajax/src/ln-ajax.js +13 -13
  9. package/js/ln-api-connector/README.md +261 -150
  10. package/js/ln-api-connector/ln-api-connector.js +1 -1
  11. package/js/ln-api-connector/src/ln-api-connector.js +368 -265
  12. package/js/ln-api-queue/README.md +219 -0
  13. package/js/ln-api-queue/ln-api-queue.js +1 -0
  14. package/js/ln-api-queue/src/ln-api-queue.js +320 -0
  15. package/js/ln-api-queue/src/queue-storage.js +455 -0
  16. package/js/ln-autoresize/README.md +47 -0
  17. package/js/ln-autoresize/ln-autoresize.js +1 -1
  18. package/js/ln-autosave/README.md +33 -1
  19. package/js/ln-autosave/ln-autosave.js +1 -1
  20. package/js/ln-autosave/src/ln-autosave.js +4 -4
  21. package/js/ln-circular-progress/README.md +27 -13
  22. package/js/ln-circular-progress/ln-circular-progress.js +1 -1
  23. package/js/ln-circular-progress/src/ln-circular-progress.js +18 -5
  24. package/js/ln-confirm/README.md +165 -86
  25. package/js/ln-confirm/_ln-confirm.scss +11 -0
  26. package/js/ln-confirm/ln-confirm.js +1 -1
  27. package/js/ln-confirm/src/ln-confirm.js +82 -42
  28. package/js/ln-core/README.md +896 -0
  29. package/js/ln-core/hash.js +92 -0
  30. package/js/ln-core/helpers.js +337 -8
  31. package/js/ln-core/index.js +4 -2
  32. package/js/ln-core/persist.js +29 -3
  33. package/js/ln-core/positioning.js +1 -35
  34. package/js/ln-core/window-cache.js +213 -0
  35. package/js/ln-couchdb-connector/README.md +177 -156
  36. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -1
  37. package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +398 -348
  38. package/js/ln-data-coordinator/README.md +536 -165
  39. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -1
  40. package/js/ln-data-coordinator/src/data-read-policy.js +18 -0
  41. package/js/ln-data-coordinator/src/ln-data-coordinator.js +1042 -249
  42. package/js/ln-data-coordinator/src/mutation-receipts.js +46 -0
  43. package/js/ln-data-store/README.md +241 -94
  44. package/js/ln-data-store/ln-data-store.js +1 -1
  45. package/js/ln-data-store/src/ln-data-store.js +694 -699
  46. package/js/ln-date/README.md +39 -1
  47. package/js/ln-date/ln-date-dev.scss +14 -0
  48. package/js/ln-date/ln-date.js +1 -1
  49. package/js/ln-date/src/ln-date.js +236 -178
  50. package/js/ln-debug/README.md +58 -0
  51. package/js/ln-debug/ln-debug.js +1 -0
  52. package/js/ln-debug/src/ln-debug.js +19 -0
  53. package/js/ln-dropdown/README.md +31 -8
  54. package/js/ln-dropdown/ln-dropdown.js +1 -1
  55. package/js/ln-dropdown/src/ln-dropdown.js +30 -13
  56. package/js/ln-editor/README.md +274 -0
  57. package/js/ln-editor/ln-editor.js +1 -0
  58. package/js/ln-editor/ln-editor.scss +43 -0
  59. package/js/ln-editor/src/ln-editor.js +619 -0
  60. package/js/ln-external-links/README.md +44 -5
  61. package/js/ln-external-links/ln-external-links.js +1 -1
  62. package/js/ln-fill/README.md +199 -0
  63. package/js/ln-fill/ln-fill.js +1 -0
  64. package/js/ln-fill/src/ln-fill.js +87 -0
  65. package/js/ln-filter/README.md +92 -2
  66. package/js/ln-filter/ln-filter-dev.scss +8 -0
  67. package/js/ln-filter/ln-filter.js +1 -1
  68. package/js/ln-filter/src/ln-filter.js +22 -7
  69. package/js/ln-form/README.md +212 -49
  70. package/js/ln-form/ln-form.js +1 -1
  71. package/js/ln-form/src/ln-form.js +99 -199
  72. package/js/ln-http/README.md +125 -89
  73. package/js/ln-http/ln-http.js +1 -1
  74. package/js/ln-http/src/ln-http.js +32 -13
  75. package/js/ln-icon/README.md +130 -0
  76. package/js/ln-icon/ln-icon.js +1 -0
  77. package/js/{ln-icons/src/ln-icons.js → ln-icon/src/ln-icon.js} +20 -15
  78. package/js/ln-link/README.md +47 -9
  79. package/js/ln-link/ln-link.js +1 -1
  80. package/js/ln-link/src/ln-link.js +22 -6
  81. package/js/ln-list/README.md +129 -0
  82. package/js/ln-list/ln-list.js +1 -0
  83. package/js/ln-list/ln-list.scss +28 -0
  84. package/js/ln-list/src/ln-list.js +1216 -0
  85. package/js/ln-modal/README.md +185 -154
  86. package/js/ln-modal/ln-modal-dev.scss +13 -0
  87. package/js/ln-modal/ln-modal.js +1 -1
  88. package/js/ln-modal/ln-modal.scss +28 -4
  89. package/js/ln-modal/src/ln-modal.js +137 -201
  90. package/js/ln-modal-coordinator/README.md +99 -0
  91. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +8 -0
  92. package/js/ln-modal-coordinator/ln-modal-coordinator.js +1 -0
  93. package/js/ln-modal-coordinator/src/ln-modal-coordinator.js +345 -0
  94. package/js/ln-nav/README.md +46 -3
  95. package/js/ln-nav/ln-nav.js +1 -1
  96. package/js/ln-nav/src/ln-nav.js +92 -127
  97. package/js/ln-number/README.md +28 -1
  98. package/js/ln-number/ln-number-dev.scss +13 -0
  99. package/js/ln-number/ln-number.js +1 -1
  100. package/js/ln-number/src/ln-number.js +443 -350
  101. package/js/ln-options/README.md +66 -0
  102. package/js/ln-options/ln-options.js +1 -0
  103. package/js/ln-options/src/ln-options.js +74 -0
  104. package/js/ln-popover/README.md +40 -5
  105. package/js/ln-popover/ln-popover.js +1 -1
  106. package/js/ln-popover/src/ln-popover.js +38 -11
  107. package/js/ln-progress/README.md +413 -442
  108. package/js/ln-progress/ln-progress.js +1 -1
  109. package/js/ln-progress/src/ln-progress.js +98 -150
  110. package/js/ln-router/README.md +432 -0
  111. package/js/ln-router/ln-router.js +1 -0
  112. package/js/ln-router/src/ln-router.js +539 -0
  113. package/js/ln-search/README.md +158 -83
  114. package/js/ln-search/ln-search-dev.scss +8 -0
  115. package/js/ln-search/ln-search.js +1 -1
  116. package/js/ln-search/src/ln-search.js +12 -9
  117. package/js/ln-slug/README.md +110 -0
  118. package/js/ln-slug/ln-slug.js +1 -0
  119. package/js/ln-slug/src/ln-slug.js +74 -0
  120. package/js/ln-sort/README.md +170 -0
  121. package/js/ln-sort/ln-sort-dev.scss +8 -0
  122. package/js/ln-sort/ln-sort.js +1 -0
  123. package/js/ln-sort/src/ln-sort.js +178 -0
  124. package/js/ln-sortable/README.md +133 -95
  125. package/js/ln-sortable/ln-sortable.js +1 -1
  126. package/js/ln-stat/README.md +55 -0
  127. package/js/ln-stat/ln-stat.js +1 -0
  128. package/js/ln-stat/src/ln-stat.js +55 -0
  129. package/js/ln-table/README.md +285 -101
  130. package/js/ln-table/ln-table-dev.scss +17 -0
  131. package/js/ln-table/ln-table.js +1 -1
  132. package/js/ln-table/ln-table.scss +1 -1
  133. package/js/ln-table/src/ln-table.js +1493 -473
  134. package/js/ln-table-coordinator/README.md +73 -0
  135. package/js/ln-table-coordinator/ln-table-coordinator.js +1 -0
  136. package/js/ln-table-coordinator/src/ln-table-coordinator.js +160 -0
  137. package/js/ln-tabs/README.md +221 -137
  138. package/js/ln-tabs/ln-tabs-dev.scss +18 -0
  139. package/js/ln-tabs/ln-tabs.js +1 -1
  140. package/js/ln-tabs/src/ln-tabs.js +41 -24
  141. package/js/ln-time/README.md +14 -0
  142. package/js/ln-time/ln-time.js +1 -1
  143. package/js/ln-time/src/ln-time.js +223 -192
  144. package/js/ln-toast/README.md +178 -122
  145. package/js/ln-toast/ln-toast.js +1 -15
  146. package/js/ln-toast/src/ln-toast.js +232 -210
  147. package/js/ln-toast/template.html +16 -12
  148. package/js/ln-toggle/README.md +61 -2
  149. package/js/ln-toggle/ln-toggle-dev.scss +13 -0
  150. package/js/ln-toggle/ln-toggle.js +1 -1
  151. package/js/ln-toggle/src/ln-toggle.js +56 -43
  152. package/js/ln-tooltip/README.md +95 -58
  153. package/js/ln-tooltip/ln-tooltip-dev.scss +9 -0
  154. package/js/ln-tooltip/ln-tooltip.js +1 -1
  155. package/js/ln-tooltip/src/ln-tooltip.js +45 -6
  156. package/js/ln-translations/README.md +118 -96
  157. package/js/ln-translations/ln-translations.js +1 -1
  158. package/js/ln-translations/src/ln-translations.js +10 -5
  159. package/js/ln-upload/README.md +85 -10
  160. package/js/ln-upload/ln-upload.js +1 -1
  161. package/js/ln-upload/src/ln-upload.js +26 -10
  162. package/js/ln-validate/README.md +72 -0
  163. package/js/ln-validate/ln-validate-dev.scss +15 -0
  164. package/js/ln-validate/ln-validate.js +1 -1
  165. package/js/ln-validate/src/ln-validate.js +64 -0
  166. package/package.json +61 -56
  167. package/scss/base/_global.scss +2 -2
  168. package/scss/base/_typography.scss +1 -2
  169. package/scss/components/_date.scss +1 -0
  170. package/scss/components/_dropdown.scss +0 -5
  171. package/scss/components/_editor.scss +5 -0
  172. package/scss/components/_filter.scss +26 -25
  173. package/scss/components/_form.scss +7 -0
  174. package/scss/components/_ln-table.scss +13 -3
  175. package/scss/components/_sort.scss +10 -0
  176. package/scss/components/_table.scss +36 -23
  177. package/scss/components/_toast.scss +68 -41
  178. package/scss/components/_toggle.scss +16 -0
  179. package/scss/config/_density.scss +205 -117
  180. package/scss/config/_icons.scss +10 -9
  181. package/scss/config/_tokens.scss +69 -47
  182. package/scss/config/mixins/_accordion.scss +2 -7
  183. package/scss/config/mixins/_alert.scss +4 -4
  184. package/scss/config/mixins/_breadcrumbs.scss +2 -2
  185. package/scss/config/mixins/_btn.scss +1 -1
  186. package/scss/config/mixins/_card.scss +89 -14
  187. package/scss/config/mixins/_collapsible.scss +3 -0
  188. package/scss/config/mixins/_colors.scss +1 -1
  189. package/scss/config/mixins/_diagnostics.scss +11 -0
  190. package/scss/config/mixins/_display.scss +1 -0
  191. package/scss/config/mixins/_dropdown.scss +3 -5
  192. package/scss/config/mixins/_editor.scss +124 -0
  193. package/scss/config/mixins/_empty-state.scss +7 -0
  194. package/scss/config/mixins/_filter.scss +3 -5
  195. package/scss/config/mixins/_footer.scss +1 -2
  196. package/scss/config/mixins/_form.scss +207 -21
  197. package/scss/config/mixins/_index.scss +2 -0
  198. package/scss/config/mixins/_ln-table.scss +41 -2
  199. package/scss/config/mixins/_modal.scss +149 -66
  200. package/scss/config/mixins/_nav.scss +3 -4
  201. package/scss/config/mixins/_popover.scss +39 -0
  202. package/scss/config/mixins/_sidebar.scss +1 -1
  203. package/scss/config/mixins/_sort.scss +42 -0
  204. package/scss/config/mixins/_table.scss +85 -60
  205. package/scss/config/mixins/_toast.scss +179 -152
  206. package/scss/config/mixins/_tooltip.scss +17 -7
  207. package/scss/config/mixins/_typography.scss +4 -2
  208. package/scss/ln-ashlar-dev.scss +27 -9
  209. package/scss/ln-ashlar.scss +7 -1
  210. package/js/ln-data-table/README.md +0 -110
  211. package/js/ln-data-table/ln-data-table.js +0 -1
  212. package/js/ln-data-table/ln-data-table.scss +0 -10
  213. package/js/ln-data-table/src/ln-data-table.js +0 -1087
  214. package/js/ln-icons/README.md +0 -88
  215. package/js/ln-icons/ln-icons.js +0 -1
  216. package/js/ln-table/ln-table-sort.js +0 -1
  217. package/js/ln-table/src/ln-table-sort.js +0 -156
@@ -20,9 +20,9 @@
20
20
  // -----------------------------------------------------------------------
21
21
  // Colors — Primary
22
22
  // -----------------------------------------------------------------------
23
- --color-primary: 216 95% 42%;
24
- --color-primary-light: 216 95% 93%;
25
- --color-primary-lighter: 216 95% 97%;
23
+ --color-primary: 232 75% 52%;
24
+ --color-primary-light: 232 75% 93%;
25
+ --color-primary-lighter: 232 75% 97%;
26
26
 
27
27
  // -----------------------------------------------------------------------
28
28
  // Colors — Secondary (brand accent — surface/element color, NOT text)
@@ -59,17 +59,17 @@
59
59
  // during dark-mode audits -- semantic tokens inherit dark adjustments
60
60
  // automatically, direct-neutral references do not.
61
61
  //
62
- --color-neutral-50: 215 20% 98%;
63
- --color-neutral-100: 215 16% 96%;
64
- --color-neutral-150: 215 14% 93%;
65
- --color-neutral-200: 215 13% 91%;
66
- --color-neutral-300: 215 13% 83%;
67
- --color-neutral-400: 215 11% 65%;
68
- --color-neutral-500: 215 9% 46%;
69
- --color-neutral-600: 215 11% 34%;
70
- --color-neutral-700: 215 14% 24%;
71
- --color-neutral-800: 215 20% 15%;
72
- --color-neutral-900: 215 39% 11%;
62
+ --color-neutral-50: 220 20% 98%;
63
+ --color-neutral-100: 220 16% 96%;
64
+ --color-neutral-150: 220 14% 93%;
65
+ --color-neutral-200: 220 13% 91%;
66
+ --color-neutral-300: 220 13% 83%;
67
+ --color-neutral-400: 218 11% 65%;
68
+ --color-neutral-500: 220 9% 46%;
69
+ --color-neutral-600: 220 11% 34%;
70
+ --color-neutral-700: 220 14% 24%;
71
+ --color-neutral-800: 220 20% 15%;
72
+ --color-neutral-900: 221 39% 11%;
73
73
 
74
74
  // -----------------------------------------------------------------------
75
75
  // Colors — Backgrounds
@@ -100,6 +100,8 @@
100
100
  --size-xl: 2rem; // 32px
101
101
  --size-2xl: 3rem; // 48px
102
102
  --size-3xl: 4rem; // 64px
103
+ --size-4xl: 6rem;
104
+ --size-5xl: 8rem;
103
105
 
104
106
  // -----------------------------------------------------------------------
105
107
  // Border Width
@@ -184,7 +186,7 @@
184
186
  // Typography
185
187
  // -----------------------------------------------------------------------
186
188
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
187
- --font-mono: ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace;
189
+ --font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace;
188
190
 
189
191
  --text-xs: 0.75rem; // 12px
190
192
  --text-sm: 0.875rem; // 14px
@@ -194,25 +196,28 @@
194
196
  --text-2xl: 1.5rem; // 24px
195
197
 
196
198
  // -----------------------------------------------------------------------
197
- // Typography — semantic role tokens (v1.1)
199
+ // Typography — semantic role tokens (v1.1) — VOCABULARY
198
200
  // -----------------------------------------------------------------------
199
201
  // Public API surface — full scale exposed; internal usage may skip levels.
202
+ // Vocabulary: density + themes rebind these; mixins read the
203
+ // --font-size/--line-height primitives via @include typography(role),
204
+ // never these tokens directly.
200
205
  --text-display-lg: 3.75rem; --lh-display-lg: 1.1;
201
206
  --text-display-md: 3rem; --lh-display-md: 1.1;
202
- --text-display-sm: 2.25rem; --lh-display-sm: 1.15;
207
+ --text-display-sm: 1.875rem; --lh-display-sm: 1.1;
203
208
 
204
- --text-heading-lg: 1.875rem; --lh-heading-lg: 1.2;
205
- --text-heading-md: 1.5rem; --lh-heading-md: 1.25;
206
- --text-heading-sm: 1.25rem; --lh-heading-sm: 1.3;
209
+ --text-heading-lg: 1.75rem; --lh-heading-lg: 1.15;
210
+ --text-heading-md: 1.25rem; --lh-heading-md: 1.2;
211
+ --text-heading-sm: 1.125rem; --lh-heading-sm: 1.25;
207
212
 
208
- --text-title-md: 1.125rem; --lh-title-md: 1.4;
209
- --text-title-sm: 1rem; --lh-title-sm: 1.4;
213
+ --text-title-md: 1rem; --lh-title-md: 1.3;
214
+ --text-title-sm: 0.875rem; --lh-title-sm: 1.3;
210
215
 
211
216
  --text-body-lg: 1.125rem; --lh-body-lg: 1.6;
212
- --text-body-md: 1rem; --lh-body-md: 1.6;
213
- --text-body-sm: 0.875rem; --lh-body-sm: 1.5;
217
+ --text-body-md: 0.875rem; --lh-body-md: 1.5; // 14px / 1.5 — DocuFlow body
218
+ --text-body-sm: 0.8125rem; --lh-body-sm: 1.45; // 13px
214
219
 
215
- --text-label-md: 0.875rem; --lh-label-md: 1.4;
220
+ --text-label-md: 0.8125rem; --lh-label-md: 1.4; // 13px
216
221
  --text-label-sm: 0.75rem; --lh-label-sm: 1.4;
217
222
 
218
223
  --text-caption: 0.75rem; --lh-caption: 1.4;
@@ -241,17 +246,21 @@
241
246
  // -----------------------------------------------------------------------
242
247
  // App shell — intrinsic dimensions (not spacing rhythm)
243
248
  // -----------------------------------------------------------------------
244
- --app-header-height: 3.5rem;
245
- --app-sidebar-width: 16rem;
249
+ --app-header-height: 3.25rem; // 52px — DocuFlow topbar height
250
+ --app-sidebar-width: 14.5rem; // 232px — DocuFlow sidebar width
246
251
  --app-scrim-bg: hsl(var(--color-neutral-900) / 0.4);
247
252
 
248
253
  // -----------------------------------------------------------------------
249
254
  // Logical tokens — the public contract mixins read
250
255
  // -----------------------------------------------------------------------
251
- // Rule: mixin bodies reference ONLY these tokens (and --text-*/--lh-*/
252
- // --radius-*/--shadow-*/--transition-*/--easing-* role scales). Back-end
253
- // scales (--size-*, --color-neutral-*) are re-bound here and in context
254
- // scopes (density, theme, region overrides) — never read directly
256
+ // Rule: mixin bodies reference ONLY these tokens. Typography mixins read
257
+ // --font-size/--line-height ONLY (via @include typography(role), which
258
+ // rebinds the primitive per role on the consuming element's scope).
259
+ // --text-*/--lh-* role scale is VOCABULARY: density + themes rebind it,
260
+ // mixins never read it directly. --radius-*/--shadow-*/--transition-*/
261
+ // --easing-* role scales remain directly readable (not yet primitived).
262
+ // Back-end scales (--size-*, --color-neutral-*) are re-bound here and in
263
+ // context scopes (density, theme, region overrides) — never read directly
255
264
  // inside a component mixin.
256
265
  //
257
266
  // See .claude/plans/refactor-logical-tokens-architecture.md for the
@@ -260,8 +269,8 @@
260
269
 
261
270
  // -- Structure & rhythm -----------------------------------------------
262
271
  --padding-x: var(--size-sm-up);
263
- --padding-y: var(--size-sm);
264
- --gap: var(--size-sm);
272
+ --padding-y: var(--size-xs-up); // 6px — dense base (DocuFlow 7px rounds to 6px)
273
+ --gap: var(--size-xs-up); // 6px — dense base
265
274
  --radius: var(--radius-md);
266
275
 
267
276
  // -----------------------------------------------------------------------
@@ -350,10 +359,24 @@
350
359
  --btn-border-hover: var(--border-strong-hover);
351
360
 
352
361
  // Button padding tokens
353
- --btn-padding-x: var(--size-md);
354
- --btn-padding-y: var(--size-sm);
355
-
356
- // -- Typography ------------------------------------------------------
362
+ --btn-padding-x: var(--size-sm-up);
363
+ --btn-padding-y: var(--size-xs-up);
364
+
365
+ // Card padding tokens — roomier than base rhythm; density-reactive via _density.scss
366
+ --card-padding-y: var(--size-sm-up); // 12px dense base
367
+ --card-padding-x: var(--size-md); // 16px dense base
368
+
369
+ // Table cell padding tokens — roomier than base rhythm; density-reactive via _density.scss
370
+ --table-cell-padding-y: var(--size-sm); // 8px dense base (DocuFlow th: 9px / td: 10px)
371
+ --table-cell-padding-x: var(--size-md); // 16px dense base
372
+
373
+ // -- Typography — primitives -----------------------------------------
374
+ // Mixins read these two primitives ONLY (via @include typography(role),
375
+ // which rebinds them per role on the consuming element's scope — mirror
376
+ // of how @mixin card rebinds --color-bg). The --text-{role}/--lh-{role}
377
+ // role scale (lines ~202-220) is VOCABULARY: density + themes rebind it,
378
+ // mixins never read it directly. These :root values are the default for
379
+ // text that does not pass through a typography mixin.
357
380
  --font-size: var(--text-body-md);
358
381
  --line-height: var(--lh-body-md);
359
382
 
@@ -389,10 +412,10 @@
389
412
  // margin-bottom: var(--margin-block);
390
413
  // }
391
414
  //
392
- // Density-compact reacts automatically: density-compact rebinds
393
- // the underlying --size-* scale, and the consumer's
394
- // `--margin-block: var(--size-md)` re-resolves at the consumer
395
- // element under the compact scope.
415
+ // NOTE: [data-density] scopes rebind type roles, logical
416
+ // --padding-y/--gap, and control sizing — NOT the --size-* scale.
417
+ // So --margin-block (reading --size-*) is constant across density
418
+ // tiers for now; making structural margins reactive is a Phase 3 item.
396
419
  //
397
420
  // --margin-inline is the horizontal companion. Used for
398
421
  // element-pushing structural offsets (icon→text gap inside a
@@ -402,18 +425,17 @@
402
425
 
403
426
  // ---------------------------------------------------------------------------
404
427
  // Mobile rhythm — logical tokens tighten at ≤ md viewport.
405
- // Not the same as `.density-compact` (that's a user opt-in).
428
+ // Not the same as `[data-density]` (that's a user opt-in).
406
429
  // This is viewport-driven structural compression: content dominates chrome
407
430
  // at narrow widths. Components reading --padding-x/--padding-y/--gap
408
431
  // react via the cascade.
409
432
  //
410
- // Interaction with .density-compact: stacks. Compact user + mobile viewport =
411
- // both apply; the underlying --size-* scale is already tighter in
412
- // .density-compact so logical tokens resolve to the tightest value.
433
+ // Interaction with [data-density]: stacks. Compact density + mobile viewport =
434
+ // both apply; the underlying --padding-y tightens further from the mobile rebind.
413
435
  // ---------------------------------------------------------------------------
414
436
  @include mq-down(md) {
415
437
  :root {
416
- --padding-y: var(--size-xs); // 12px → 4px
417
- --gap: var(--size-xs-up); // 8px → 6px
438
+ --padding-y: var(--size-xs); // 6px → 4px
439
+ --gap: var(--size-xs-up); // 6px → 6px (already tight)
418
440
  }
419
441
  }
@@ -41,12 +41,7 @@
41
41
  @include cursor-pointer;
42
42
  }
43
43
 
44
- // Content body padding — on children, not the overflow:hidden wrapper
45
- .collapsible-body > * {
46
- --padding-y: var(--size-sm-up);
47
- --padding-x: var(--size-md);
48
- padding: var(--padding-y) var(--padding-x);
49
- margin: 0;
50
- }
44
+ // Body spacing now comes from the universal `.collapsible-body > *`
45
+ // rule in components/_toggle.scss (applies to every collapsible).
51
46
  }
52
47
  }
@@ -15,10 +15,10 @@
15
15
  //
16
16
  // HTML:
17
17
  // <div class="alert" role="alert">
18
- // <svg class="ln-icon" aria-hidden="true"><use href="#ln-info-circle"></use></svg>
18
+ // <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-info-circle"></use></svg>
19
19
  // <p>Message text.</p>
20
20
  // <button type="button" aria-label="Dismiss">
21
- // <svg class="ln-icon" aria-hidden="true"><use href="#ln-x"></use></svg>
21
+ // <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
22
22
  // </button>
23
23
  // </div>
24
24
 
@@ -63,12 +63,12 @@
63
63
  // HTML (dismissible via ln-toggle, dismissed state persists across reloads):
64
64
  // <div class="alert banner" role="status" aria-live="polite"
65
65
  // data-ln-toggle="open" data-ln-persist id="trial-notice">
66
- // <svg class="ln-icon" aria-hidden="true"><use href="#ln-info-circle"></use></svg>
66
+ // <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-info-circle"></use></svg>
67
67
  // <p>Trial expires in 3 days.</p>
68
68
  // <button type="button" aria-label="Dismiss"
69
69
  // data-ln-toggle-for="trial-notice"
70
70
  // data-ln-toggle-action="close">
71
- // <svg class="ln-icon" aria-hidden="true"><use href="#ln-x"></use></svg>
71
+ // <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
72
72
  // </button>
73
73
  // </div>
74
74
 
@@ -2,6 +2,7 @@
2
2
  @use 'borders' as *;
3
3
  @use 'focus' as *;
4
4
  @use 'interaction' as *;
5
+ @use 'typography' as *;
5
6
 
6
7
  // ─── Breadcrumbs ────────────────────────────────────────────────────────────
7
8
  // CSS-only navigation trail. No JS involvement.
@@ -37,8 +38,7 @@
37
38
  @include flex;
38
39
  @include items-center;
39
40
  gap: var(--gap);
40
- font-size: var(--text-body-sm);
41
- line-height: var(--lh-body-sm);
41
+ @include typography(body-sm);
42
42
  margin: 0;
43
43
 
44
44
  + li::before {
@@ -48,7 +48,7 @@
48
48
  // --color-accent / --color-accent-fg) sidestep that: the default
49
49
  // theme leaves them unset (mixin falls back to solid + white), Glass
50
50
  // rebinds them at theme :root to flip the accent button to
51
- // translucent. Solid-accent consumers (toast-side, pill-checked,
51
+ // translucent. Solid-accent consumers (toast-icon, pill-checked,
52
52
  // stepper active/complete) read --color-accent / --color-accent-fg
53
53
  // directly and are unaffected by Glass's rebind because Glass leaves
54
54
  // --color-accent-fg alone. See CLAUDE.md ## Theme Architecture for
@@ -11,6 +11,9 @@
11
11
  // Card & Section — composite mixins
12
12
 
13
13
  @mixin card {
14
+ --color-bg: var(--nested-card-bg, var(--bg-base));
15
+ --shadow: var(--nested-card-shadow, var(--shadow-resting));
16
+
14
17
  background: var(--color-bg);
15
18
  @include border;
16
19
  @include w-full;
@@ -18,12 +21,28 @@
18
21
  border-radius: var(--radius);
19
22
  @include overflow-hidden;
20
23
  @include transition;
21
- --shadow: var(--shadow-resting);
22
24
  box-shadow: var(--shadow);
23
25
 
24
26
  &:hover {
25
- --color-border: var(--border-strong);
26
- --shadow: var(--shadow-floating);
27
+ --color-border: color-mix(in srgb, var(--color-accent) 35%, transparent);
28
+ --shadow: var(--nested-card-shadow-hover, var(--shadow-floating));
29
+ }
30
+
31
+ // Nesting ladder: when nested inside a section-card or another card,
32
+ // climb to elevated surface to preserve visual depth.
33
+ .section-card &,
34
+ .section &,
35
+ .card & {
36
+ --color-bg: var(--bg-elevated);
37
+ --shadow: var(--shadow-floating);
38
+ }
39
+
40
+ // Bulletproof nesting support for custom/semantic selectors:
41
+ // sets the elevated background variables on all descendants.
42
+ & * {
43
+ --nested-card-bg: var(--bg-elevated);
44
+ --nested-card-shadow: var(--shadow-floating);
45
+ --nested-card-shadow-hover: var(--shadow-floating);
27
46
  }
28
47
 
29
48
  // Direct-child selectors only — nested <header>/<main>/<footer>
@@ -47,13 +66,22 @@
47
66
  @include flex-1;
48
67
  text-decoration: none;
49
68
  color: inherit;
69
+
70
+ > header { @include panel-header; }
71
+ > main { @include panel-body; }
72
+ > footer { @include panel-footer; }
73
+
74
+ > main {
75
+ @include flex-1;
76
+ gap: var(--gap);
77
+ }
50
78
  }
51
79
 
52
80
  // Tile-header h3: when a card carries a title inside > main
53
81
  // without a <header> wrapper, the global h3 cascade (heading-sm)
54
82
  // is too large for tile context. Tighten to tile-title sizing.
55
83
  > main h3 {
56
- font-size: var(--text-body-sm);
84
+ @include typography(body-sm);
57
85
  @include font-semibold;
58
86
  color: inherit;
59
87
  margin: 0;
@@ -64,15 +92,15 @@
64
92
  @include flex;
65
93
  @include items-center;
66
94
  @include justify-between;
67
- padding: var(--padding-y) var(--padding-x);
95
+ padding: var(--card-padding-y) var(--card-padding-x);
68
96
  @include border-b;
69
97
  --color-bg: var(--bg-sunken);
70
98
  background: var(--color-bg);
71
99
  @include transition;
100
+ margin-bottom: 0;
72
101
 
73
- h3 {
74
- font-size: var(--text-body-sm);
75
- line-height: var(--lh-body-sm);
102
+ :is(h1, h2, h3, h4, h5, h6) {
103
+ @include typography(body-sm);
76
104
  @include font-medium;
77
105
  color: var(--color-fg);
78
106
  margin: 0;
@@ -110,7 +138,48 @@
110
138
  }
111
139
 
112
140
  @mixin panel-body {
113
- padding: var(--padding-y) var(--padding-x);
141
+ padding: var(--card-padding-y) var(--card-padding-x);
142
+
143
+ ul, ol {
144
+ padding-left: 1.5em;
145
+ margin-bottom: var(--size-md);
146
+
147
+ li {
148
+ margin-bottom: var(--size-xs);
149
+ line-height: 1.7;
150
+ list-style: inherit;
151
+
152
+ &:last-child {
153
+ margin-bottom: 0;
154
+ }
155
+ }
156
+ }
157
+
158
+ ul {
159
+ list-style-type: disc;
160
+ }
161
+
162
+ ol {
163
+ list-style-type: decimal;
164
+ }
165
+
166
+ &:has(> table),
167
+ &:has(> .table-container) {
168
+ overflow-x: auto;
169
+ max-width: 100%;
170
+ -webkit-overflow-scrolling: touch;
171
+ }
172
+
173
+ // ln-table brings its own chrome and sticky thead/footer — `clip` keeps
174
+ // this cell from becoming the scroll container that would trap them.
175
+ &:has(> [data-ln-table]) {
176
+ overflow-x: clip;
177
+ max-width: 100%;
178
+ }
179
+
180
+ table {
181
+ min-width: var(--table-min-width, 600px);
182
+ }
114
183
 
115
184
  // Auto-flush: ONLY when the table (or .table-container) is the
116
185
  // sole child of main — i.e. the card is a pure table card.
@@ -122,11 +191,13 @@
122
191
  // then <table>), :only-child doesn't match, padding stays,
123
192
  // and the table keeps its own inset.
124
193
  &:has(> table:only-child),
125
- &:has(> .table-container:only-child) {
194
+ &:has(> .table-container:only-child),
195
+ &:has(> [data-ln-table]:only-child) {
126
196
  padding: 0;
127
197
 
128
198
  > table,
129
- > .table-container > table {
199
+ > .table-container > table,
200
+ > [data-ln-table] > table {
130
201
  border-radius: 0;
131
202
  box-shadow: none;
132
203
  margin-bottom: 0;
@@ -135,8 +206,10 @@
135
206
  }
136
207
 
137
208
  @mixin panel-footer {
138
- padding: var(--padding-y) var(--padding-x);
209
+ padding: var(--card-padding-y) var(--card-padding-x);
139
210
  @include border-t;
211
+ --color-bg: var(--bg-sunken);
212
+ background: var(--color-bg);
140
213
  @include flex;
141
214
  @include justify-end;
142
215
  gap: var(--gap);
@@ -146,7 +219,7 @@
146
219
  --margin-block: var(--size-xl);
147
220
  margin-bottom: var(--margin-block);
148
221
 
149
- header {
222
+ > header {
150
223
  @include flex;
151
224
  @include items-center;
152
225
  @include justify-between;
@@ -277,7 +350,9 @@
277
350
  border-inline-end: var(--border-inline-end, var(--border-width) solid var(--color-border));
278
351
  border-radius: var(--radius);
279
352
  box-shadow: var(--shadow);
280
- z-index: var(--z-dropdown);
353
+ // z-index is the caller's responsibility: top-layer consumers (dropdown,
354
+ // popover, tooltip portal) don't need one; standalone consumers do —
355
+ // see [data-ln-table] .column-filter-dropdown in components/_filter.scss.
281
356
  }
282
357
 
283
358
  // ─── Card field list (label/value rows) ────────────────
@@ -21,4 +21,7 @@
21
21
 
22
22
  @mixin collapsible-content {
23
23
  overflow: hidden;
24
+ // Pair with overflow:hidden so the grid item's automatic minimum resolves
25
+ // to 0 (not min-content) and the 0fr track can collapse fully.
26
+ min-height: 0;
24
27
  }
@@ -19,7 +19,7 @@
19
19
  // --color-accent vocabulary to a status family (success / warning /
20
20
  // error / info / secondary). Used by .success / .warning / .error /
21
21
  // .info / .secondary classes in _utilities.scss. Cascading both families
22
- // ensures every downstream consumer (btn, toast-side, card-accent,
22
+ // ensures every downstream consumer (btn, toast-icon, card-accent,
23
23
  // alert, badge, etc.) picks up the status color through inherited
24
24
  // custom-property values — no per-mixin redeclare required.
25
25
  @mixin semantic-color($family) {
@@ -35,3 +35,14 @@
35
35
  margin-inline-start: var(--size-xs, 0.5rem);
36
36
  }
37
37
  }
38
+
39
+ // Replaced-element DOM diagnostic error (img, video, iframe, etc.)
40
+ // Replaced elements do NOT render ::before/::after generated content, so this
41
+ // mixin uses a visible outline + translucent wash instead of a text label.
42
+ // The human-readable reason is carried by surrounding copy (e.g. a demo
43
+ // section header), not by a pseudo-element.
44
+ @mixin dev-replaced-error {
45
+ outline: 2px dashed hsl(var(--color-error));
46
+ outline-offset: var(--size-xs);
47
+ background: color-mix(in srgb, hsl(var(--color-error)) 15%, transparent);
48
+ }
@@ -5,6 +5,7 @@
5
5
  @mixin block { display: block; }
6
6
  @mixin inline-block { display: inline-block; }
7
7
  @mixin hidden { display: none; }
8
+ @mixin invisible { visibility: hidden; }
8
9
 
9
10
  @mixin flex-col { display: flex; flex-direction: column; }
10
11
  @mixin flex-row { display: flex; flex-direction: row; }
@@ -130,11 +130,9 @@
130
130
 
131
131
  @mixin dropdown-menu {
132
132
  @include floating-panel;
133
- @include absolute;
134
- right: 0;
135
- top: 100%;
136
- --margin-block: var(--size-xs);
137
- margin-top: var(--margin-block);
133
+ @include fixed;
134
+ inset: auto;
135
+ margin: 0;
138
136
  --padding-y: var(--size-xs);
139
137
  padding-block: var(--padding-y);
140
138
  min-width: 10rem;
@@ -0,0 +1,124 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'typography' as *;
4
+ @use 'colors' as *;
5
+ @use 'borders' as *;
6
+ @use 'transitions' as *;
7
+ @use 'motion' as *;
8
+ @use 'focus' as *;
9
+ @use 'prose' as *;
10
+
11
+ // ─── Editor ────────────────────────────────────────────────────
12
+ // Lightweight WYSIWYG editor wrapper: toolbar + contentEditable
13
+ // surface. The editing surface reuses @include prose for content
14
+ // formatting so authored content matches rendered output.
15
+ //
16
+ // Structure:
17
+ // [data-ln-editor]
18
+ // > [role="toolbar"] toolbar (authored container)
19
+ // > ul > li > button formatting actions (grouped)
20
+ // > .ln-editor__surface contentEditable div (JS-created)
21
+ // > textarea hidden source (form submission)
22
+
23
+ @mixin editor {
24
+ @include border;
25
+ border-radius: var(--radius);
26
+ overflow: hidden;
27
+ --color-bg: var(--bg-base);
28
+ background: var(--color-bg);
29
+
30
+ // ─── Toolbar ───────────────────────────────────────────────
31
+ > [role="toolbar"] {
32
+ @include flex;
33
+ @include flex-wrap;
34
+ @include items-center;
35
+ --gap: var(--size-2xs);
36
+ gap: var(--gap);
37
+ --padding-y: var(--size-xs);
38
+ --padding-x: var(--size-sm);
39
+ padding: var(--padding-y) var(--padding-x);
40
+ --color-bg: var(--bg-sunken);
41
+ background: var(--color-bg);
42
+ border-bottom: var(--border-width) solid var(--color-border);
43
+
44
+ // Button groups — each <ul> is a visual group
45
+ > ul {
46
+ @include flex;
47
+ @include items-center;
48
+ --gap: var(--size-2xs);
49
+ gap: var(--gap);
50
+ list-style: none;
51
+ padding: 0;
52
+ margin: 0;
53
+
54
+ // Visual separator between groups (not on last)
55
+ &:not(:last-child) {
56
+ --padding-x: var(--size-xs);
57
+ padding-right: var(--padding-x);
58
+ border-right: var(--border-width) solid var(--color-border);
59
+ --margin-inline: var(--size-xs);
60
+ margin-right: var(--margin-inline);
61
+ }
62
+ }
63
+
64
+ // Toolbar buttons — compact, neutral
65
+ button {
66
+ --padding-y: var(--size-2xs);
67
+ --padding-x: var(--size-xs);
68
+ padding: var(--padding-y) var(--padding-x);
69
+ border-radius: var(--radius-sm);
70
+ background: transparent;
71
+ color: var(--color-fg);
72
+ @include transition-colors;
73
+
74
+ &:hover {
75
+ --color-bg: var(--bg-base);
76
+ background: var(--color-bg);
77
+ }
78
+
79
+ // Active formatting state (JS toggles this class)
80
+ &.ln-editor-active {
81
+ --color-bg: var(--bg-base);
82
+ background: var(--color-bg);
83
+ color: var(--color-accent);
84
+ }
85
+ }
86
+ }
87
+
88
+ // ─── Editing Surface ───────────────────────────────────────
89
+ > .ln-editor__surface {
90
+ @include prose;
91
+ max-width: none; // Override prose's max-width constraint
92
+ --padding-y: var(--size-md);
93
+ --padding-x: var(--size-md);
94
+ padding: var(--padding-y) var(--padding-x);
95
+ min-height: 8rem;
96
+ outline: none;
97
+ cursor: text;
98
+
99
+ // Counter the global reset (`ul li, ol li { list-style: none }`).
100
+ // The reset kills markers on <li> directly; prose only sets
101
+ // list-style on the <ul>/<ol> container. Restore inheritance
102
+ // so the container's disc/decimal flows through to each item.
103
+ ul li { list-style: disc; }
104
+ ol li { list-style: decimal; }
105
+
106
+ // Empty placeholder via CSS
107
+ &:empty::before {
108
+ content: attr(data-placeholder);
109
+ --color-fg: var(--fg-subtle);
110
+ color: var(--color-fg);
111
+ pointer-events: none;
112
+ }
113
+ }
114
+
115
+ // Focus state on the whole editor when editing
116
+ &:focus-within {
117
+ border-color: var(--color-accent);
118
+ }
119
+
120
+ // ─── Hidden textarea (source for form submission) ──────────
121
+ > textarea[data-ln-editor-source] {
122
+ display: none;
123
+ }
124
+ }
@@ -28,3 +28,10 @@
28
28
  margin-top: var(--margin-block);
29
29
  }
30
30
  }
31
+
32
+ @mixin empty-state-span {
33
+ grid-column: 1 / -1;
34
+ width: 100%;
35
+ height: auto !important;
36
+ min-height: auto !important;
37
+ }