@livenetworks/ashlar 1.4.1 → 1.6.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 (219) 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 +162 -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 +77 -41
  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 +189 -99
  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 -55
  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 -0
  173. package/scss/components/_form.scss +7 -0
  174. package/scss/components/_ln-table.scss +14 -5
  175. package/scss/components/_sort.scss +10 -0
  176. package/scss/components/_table.scss +68 -1
  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 +48 -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 +47 -0
  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 +4 -1
  198. package/scss/config/mixins/_ln-table.scss +51 -93
  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 +232 -38
  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 +39 -0
  209. package/scss/ln-ashlar.scss +8 -2
  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 -1103
  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 -168
  218. package/scss/components/_data-table.scss +0 -83
  219. package/scss/config/mixins/_data-table.scss +0 -346
@@ -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
+ }
@@ -0,0 +1,47 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'interaction' as *;
4
+ @use 'transitions' as *;
5
+ @use 'form' as *;
6
+
7
+ // ─── Filter dropdown — content chrome only ─────────────────────────
8
+ // The floating shell (chrome, fixed positioning, min-width, show/hide)
9
+ // comes from `@mixin popover` on the popover-driven path, OR from
10
+ // `@mixin floating-panel` + scroll box on the ln-data-table dynamic
11
+ // path (see components/_filter.scss). This mixin styles ONLY the inner
12
+ // content: scrollable options box, search input, and the options list
13
+ // (pills via check-list).
14
+ //
15
+ // NOTE: there is intentionally NO clear-button block. The redundant
16
+ // "Clear Filter" button was removed library-wide — the `data-ln-filter-reset`
17
+ // "All" sentinel checkbox already performs the reset. Do not re-introduce a
18
+ // `[data-ln-filter-clear]` selector here.
19
+ //
20
+ // Tunables (consumer-overridable via --var with sane defaults):
21
+ // --filter-dropdown-max-height (default 20rem)
22
+ // --filter-dropdown-overflow-y (default auto)
23
+ // --filter-dropdown-item-pad-y (default var(--size-3xs))
24
+ //
25
+ // Usage:
26
+ // .column-filter-dropdown { @include filter-dropdown; }
27
+
28
+ @mixin filter-dropdown {
29
+ max-height: var(--filter-dropdown-max-height, 20rem);
30
+ overflow-y: var(--filter-dropdown-overflow-y, auto);
31
+ text-transform: none; // reset inherited header uppercase
32
+
33
+ [data-ln-filter-search] {
34
+ @include form-input;
35
+ font-size: var(--filter-dropdown-text, var(--text-xs));
36
+ --padding-y: var(--size-2xs);
37
+ --padding-x: var(--size-sm);
38
+ width: 100%;
39
+ margin-bottom: var(--size-xs);
40
+ }
41
+
42
+ [data-ln-filter-options] {
43
+ @include check-list-nav;
44
+
45
+ li { padding: var(--filter-dropdown-item-pad-y, var(--size-3xs)) 0; }
46
+ }
47
+ }
@@ -25,8 +25,7 @@
25
25
  padding: var(--padding-y) var(--padding-x);
26
26
  border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
27
27
  background: var(--color-bg);
28
- font-size: var(--text-body-sm);
29
- line-height: var(--lh-body-sm);
28
+ @include typography(body-sm);
30
29
  color: var(--color-fg);
31
30
  @include flex;
32
31
  @include items-center;
@@ -17,9 +17,8 @@
17
17
 
18
18
  @mixin form-label {
19
19
  @include block;
20
- font-size: var(--text-label-md);
21
- line-height: var(--lh-label-md);
22
- @include font-medium;
20
+ @include typography(label-sm);
21
+ @include font-semibold;
23
22
  color: var(--color-fg);
24
23
  // Structural label-to-input gap (not shell rhythm).
25
24
  --margin-block: var(--size-xs);
@@ -91,12 +90,13 @@
91
90
 
92
91
  @mixin form-input {
93
92
  @include w-full;
94
- --padding-y: var(--size-sm);
93
+ --padding-y: var(--input-padding-y, var(--size-sm));
95
94
  --padding-x: var(--size-md);
96
95
  padding: var(--padding-y) var(--padding-x);
97
96
  font-size: var(--font-size);
98
97
  line-height: var(--line-height);
99
98
  color: var(--color-fg);
99
+ --color-bg: var(--input-bg, var(--bg-recessed));
100
100
  background: var(--color-bg);
101
101
  @include border;
102
102
  border-radius: var(--radius);
@@ -211,6 +211,7 @@
211
211
  padding: var(--size-xs) var(--size-sm); // icon provides its own row spacing
212
212
  --color-fg: var(--fg-subtle);
213
213
  color: var(--color-fg);
214
+ box-sizing: content-box;
214
215
  }
215
216
 
216
217
  > input,
@@ -262,27 +263,93 @@
262
263
  @mixin form-input-icon-group {
263
264
  @include form-field-group;
264
265
 
265
- > input {
266
- // Hide the clear button while the field is empty.
267
- &:placeholder-shown ~ [data-ln-search-clear] {
268
- @include hidden;
269
- }
266
+ // Visually hide the clear button while the field is empty (visibility:hidden
267
+ // preserves layout space so the input width stays stable).
268
+ // Structural target: the only button in this group is the clear control.
269
+ > input:placeholder-shown ~ button {
270
+ @include invisible;
270
271
  }
271
272
 
272
- > [data-ln-search-clear] {
273
- @include flex-shrink-0;
274
- --padding-y: var(--size-2xs);
275
- --padding-x: var(--size-2xs);
276
- padding: var(--padding-y) var(--padding-x);
277
- @include cursor-pointer;
278
- --color-fg: var(--fg-subtle);
279
- color: var(--color-fg);
273
+ // Clear control — square hit area, circular highlight on hover only.
274
+ // Overrides the flush date/submit-button styling inherited from
275
+ // form-field-group > button (equal specificity, emitted later → wins).
276
+ > button {
277
+ align-self: center; // cancel inherited stretch
278
+ aspect-ratio: 1; // square hit area
279
+ padding: var(--size-2xs); // equal padding all sides (cancels L+R size-sm and T+B 0)
280
+ border-radius: var(--radius-full); // circular highlight (square in flat theme)
281
+ background: transparent; // transparent idle
280
282
  @include transition-colors;
281
283
 
282
- &:hover {
284
+ > .ln-icon {
285
+ --color-fg: var(--fg-subtle); // idle icon color
283
286
  color: var(--color-fg);
287
+ @include transition-colors; // icon recolor must animate (parent transition won't drive child color)
288
+ }
289
+
290
+ &:hover {
291
+ background: var(--btn-bg-hover); // circle highlight appears
292
+ > .ln-icon {
293
+ --color-fg: var(--fg-default);
294
+ }
284
295
  }
285
296
  }
297
+
298
+ // Fix C — pull the leading icon and the input text closer.
299
+ // Inherited from form-field-group at equal specificity; emitted later → wins per property.
300
+
301
+ // 1. Zero the input's OWN adjacent side padding (the user's explicit minimum).
302
+ // Mirror form-field-group's :not() structure so the override lands at equal
303
+ // specificity and replaces only the horizontal padding it set.
304
+ > input,
305
+ > select {
306
+ &:not(:first-child) {
307
+ padding-left: 0;
308
+ }
309
+ &:not(:last-child) {
310
+ padding-right: 0;
311
+ }
312
+ }
313
+
314
+ // 2. Reduce ONLY the leading icon's INNER (right) padding so the magnifier sits
315
+ // close to the text. KEEP the left padding (inherited --size-sm outer breathing
316
+ // room) and KEEP vertical padding. Set padding-right ONLY — never the `padding`
317
+ // shorthand (it would clobber left + vertical).
318
+ > .ln-icon {
319
+ padding-right: var(--size-xs); // 8px → 4px; left + vertical inherited from form-field-group
320
+ }
321
+ }
322
+
323
+ // ─── Search field — compact icon-group variant (delta-only) ───────
324
+ //
325
+ // Opt-in refinements layered on top of the icon-group chrome that the
326
+ // global auto-bind (components/_form.scss: label:has(> .ln-icon):has(>
327
+ // search/text input)) already supplies. PREREQUISITE: the consumer is an
328
+ // icon + search-input <label> so the auto-bind provides the base shell;
329
+ // on a plain label this emits only the tweaks below, no chrome.
330
+ //
331
+ // Usage (project/demo SCSS):
332
+ // label.search { @include search; }
333
+
334
+ @mixin search {
335
+ width: min(100%, 20rem); // intrinsic capped field width
336
+ --color-bg: var(--bg-recessed); // recessed fill so the white clear-hover reads
337
+ --btn-bg-hover: var(--bg-base); // clear button highlights white on hover (token rebind, no specificity override)
338
+
339
+ // Density-immune: pin to the dense base scale so the field is one size
340
+ // everywhere, regardless of ambient [data-density]. Mirror the values
341
+ // in config/_density.scss — keep in sync.
342
+ --size-xs: 0.125rem;
343
+ --size-sm: 0.375rem;
344
+ --font-size: 0.875rem;
345
+ --line-height: 1.5;
346
+
347
+ // Compact field — smaller vertical padding. form-input self-declares
348
+ // --padding-y on the input, so override it on the input directly.
349
+ > input { --padding-y: var(--size-xs); }
350
+
351
+ // Compact icons via the --icon-size primitive (no ln-icon--sm class in markup).
352
+ .ln-icon { --icon-size: 1rem; }
286
353
  }
287
354
 
288
355
  // ─── Checkbox & Radio ───────────────────────────────────────────
@@ -349,7 +416,7 @@
349
416
  @include flex;
350
417
  @include items-center;
351
418
  gap: var(--gap);
352
- --padding-y: var(--size-sm);
419
+ --padding-y: var(--input-padding-y, var(--size-sm));
353
420
  --padding-x: var(--size-md);
354
421
  padding: var(--padding-y) var(--padding-x);
355
422
  @include cursor-pointer;
@@ -516,6 +583,71 @@
516
583
  }
517
584
  }
518
585
 
586
+ // Segmented track control pills
587
+ @mixin pills-segmented {
588
+ @include inline-flex;
589
+ @include items-center;
590
+ list-style: none;
591
+ padding: 2px;
592
+ margin: 0;
593
+ background-color: var(--bg-recessed);
594
+ border-radius: var(--radius);
595
+ border: var(--border-width) solid var(--color-border);
596
+ width: 100%;
597
+
598
+ > li {
599
+ flex: 1 1 0%;
600
+ margin: 0;
601
+ padding: 0;
602
+ display: block;
603
+ }
604
+
605
+ > li > label:has(> input[type="checkbox"]),
606
+ > li > label:has(> input[type="radio"]) {
607
+ @include flex;
608
+ @include items-center;
609
+ @include justify-center;
610
+ padding: var(--size-xs) var(--size-md);
611
+ border-radius: calc(var(--radius) - 2px);
612
+ @include cursor-pointer;
613
+ @include text-sm;
614
+ @include font-medium;
615
+ color: var(--fg-muted);
616
+ @include transition;
617
+ user-select: none;
618
+ margin: 0;
619
+ text-align: center;
620
+ border: none;
621
+ background: transparent;
622
+
623
+ > input {
624
+ position: absolute;
625
+ width: 1px;
626
+ height: 1px;
627
+ padding: 0;
628
+ margin: -1px;
629
+ overflow: hidden;
630
+ clip: rect(0, 0, 0, 0);
631
+ border: 0;
632
+ }
633
+
634
+ &:hover {
635
+ color: var(--color-fg);
636
+ }
637
+
638
+ &:has(> input:checked) {
639
+ background-color: var(--bg-base);
640
+ color: var(--color-fg);
641
+ @include font-semibold;
642
+ box-shadow: var(--shadow-resting);
643
+ }
644
+
645
+ &:has(> input:focus-visible) {
646
+ @include focus-ring;
647
+ }
648
+ }
649
+ }
650
+
519
651
  @mixin toggle-switch {
520
652
  appearance: none;
521
653
  position: relative;
@@ -616,6 +748,50 @@
616
748
  li label { @include pill; }
617
749
  }
618
750
 
751
+ // ─── Check list nav — filter list styled with the nav-link vocabulary ──
752
+ // Same checkbox-filter list as check-list, but the selected item uses the
753
+ // sidebar nav-link active treatment (accent-tint wash + accent text +
754
+ // semibold) instead of the solid-accent pill fill. Shared by the filter
755
+ // popover (@mixin popover) and the ln-table column-filter dropdown
756
+ // (@mixin filter-dropdown) so every filter list matches the sidebar nav
757
+ // (nav-links-rounded). Extends check-list to keep the hidden-input filled
758
+ // structure; overrides only color, weight and size. Consumers that need
759
+ // the checkbox kept keyboard-focusable (popover a11y) re-reveal the input
760
+ // themselves after including this.
761
+ @mixin check-list-nav {
762
+ @include check-list;
763
+
764
+ li label {
765
+ // Smaller — match the nav link (13px).
766
+ @include typography(body-sm);
767
+ // Muted idle text, transparent surface (nav idle).
768
+ --color-fg: var(--fg-muted);
769
+ color: var(--color-fg);
770
+ background: transparent;
771
+ border: none;
772
+ // Tighter vertical padding — match nav link height (~31px).
773
+ --padding-y: var(--size-xs-up);
774
+ padding: var(--padding-y) var(--padding-x);
775
+
776
+ // Hover (unchecked) — subtle accent wash (nav hover).
777
+ &:hover:not(:has(> input:checked)) {
778
+ background-color: color-mix(in srgb, var(--color-accent) 7%, transparent);
779
+ }
780
+
781
+ // Selected — nav active vocabulary (replaces solid-accent + white).
782
+ &:has(> input:checked) {
783
+ background-color: var(--color-accent-tint-strong);
784
+ color: var(--color-accent);
785
+ @include font-semibold;
786
+ }
787
+
788
+ // Selected + hover — keep the wash, don't darken (nav has no darken).
789
+ &:hover:has(> input:checked) {
790
+ background-color: var(--color-accent-tint-strong);
791
+ }
792
+ }
793
+ }
794
+
619
795
  // ─── Validation states ─────────────────────────────────────────
620
796
  // Status colors (error/success) stay raw — no logical --color-status
621
797
  // token in scope (see parent plan §6.2).
@@ -642,7 +818,17 @@
642
818
  // Structural error-list margin (not shell rhythm).
643
819
  --margin-block: var(--size-xs);
644
820
  margin-top: var(--margin-block);
645
- font-size: var(--text-caption);
646
- line-height: var(--lh-caption);
821
+ @include typography(caption);
647
822
  color: hsl(var(--color-error));
823
+ min-height: 1.25rem; // Reserves space to prevent form layout shifts when errors appear
824
+ list-style: none;
825
+ padding: 0;
826
+ margin-bottom: 0;
827
+
828
+ li {
829
+ margin: 0;
830
+ &.hidden {
831
+ display: none;
832
+ }
833
+ }
648
834
  }
@@ -25,11 +25,13 @@
25
25
  @forward 'nav';
26
26
  @forward 'btn';
27
27
  @forward 'form';
28
+ @forward 'filter';
28
29
  @forward 'modal';
29
30
  @forward 'breadcrumbs';
30
31
  @forward 'loader';
31
32
  @forward 'tabs';
32
33
  @forward 'table';
34
+ @forward 'sort';
33
35
  @forward 'avatar';
34
36
  @forward 'footer';
35
37
  @forward 'sidebar';
@@ -42,6 +44,7 @@
42
44
  @forward 'timeline';
43
45
  @forward 'stat-card';
44
46
  @forward 'prose';
47
+ @forward 'editor';
45
48
  @forward 'kbd';
46
49
  @forward 'tooltip';
47
50
  @forward 'popover';
@@ -53,6 +56,6 @@
53
56
  @forward 'toast';
54
57
  @forward 'upload';
55
58
  @forward 'translations';
56
- @forward 'data-table';
57
59
  @forward 'ln-table';
58
60
  @forward 'ajax';
61
+ @forward 'diagnostics';