@livenetworks/ashlar 1.3.2

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 (232) hide show
  1. package/README.md +177 -0
  2. package/js/COMPONENTS.md +1102 -0
  3. package/js/index.js +41 -0
  4. package/js/ln-accordion/README.md +137 -0
  5. package/js/ln-accordion/ln-accordion.js +1 -0
  6. package/js/ln-accordion/src/ln-accordion.js +41 -0
  7. package/js/ln-ajax/README.md +91 -0
  8. package/js/ln-ajax/ln-ajax.js +1 -0
  9. package/js/ln-ajax/src/ln-ajax.js +277 -0
  10. package/js/ln-api-connector/README.md +150 -0
  11. package/js/ln-api-connector/ln-api-connector.js +1 -0
  12. package/js/ln-api-connector/src/ln-api-connector.js +265 -0
  13. package/js/ln-autoresize/README.md +80 -0
  14. package/js/ln-autoresize/ln-autoresize.js +1 -0
  15. package/js/ln-autoresize/src/ln-autoresize.js +47 -0
  16. package/js/ln-autosave/README.md +92 -0
  17. package/js/ln-autosave/ln-autosave.js +1 -0
  18. package/js/ln-autosave/src/ln-autosave.js +147 -0
  19. package/js/ln-circular-progress/README.md +161 -0
  20. package/js/ln-circular-progress/ln-circular-progress.js +1 -0
  21. package/js/ln-circular-progress/src/ln-circular-progress.js +133 -0
  22. package/js/ln-confirm/README.md +86 -0
  23. package/js/ln-confirm/_ln-confirm.scss +13 -0
  24. package/js/ln-confirm/ln-confirm.js +1 -0
  25. package/js/ln-confirm/src/ln-confirm.js +131 -0
  26. package/js/ln-core/crypto.js +83 -0
  27. package/js/ln-core/helpers.js +411 -0
  28. package/js/ln-core/index.js +5 -0
  29. package/js/ln-core/persist.js +71 -0
  30. package/js/ln-core/positioning.js +207 -0
  31. package/js/ln-core/reactive.js +74 -0
  32. package/js/ln-couchdb-connector/README.md +156 -0
  33. package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  34. package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +348 -0
  35. package/js/ln-data-coordinator/README.md +165 -0
  36. package/js/ln-data-coordinator/ln-data-coordinator.js +1 -0
  37. package/js/ln-data-coordinator/src/ln-data-coordinator.js +249 -0
  38. package/js/ln-data-store/README.md +94 -0
  39. package/js/ln-data-store/ln-data-store.js +1 -0
  40. package/js/ln-data-store/src/ln-data-store.js +699 -0
  41. package/js/ln-data-table/README.md +110 -0
  42. package/js/ln-data-table/ln-data-table.js +1 -0
  43. package/js/ln-data-table/ln-data-table.scss +10 -0
  44. package/js/ln-data-table/src/ln-data-table.js +1103 -0
  45. package/js/ln-date/README.md +151 -0
  46. package/js/ln-date/ln-date.js +1 -0
  47. package/js/ln-date/src/ln-date.js +442 -0
  48. package/js/ln-dropdown/README.md +117 -0
  49. package/js/ln-dropdown/ln-dropdown.js +1 -0
  50. package/js/ln-dropdown/ln-dropdown.scss +15 -0
  51. package/js/ln-dropdown/src/ln-dropdown.js +174 -0
  52. package/js/ln-external-links/README.md +341 -0
  53. package/js/ln-external-links/ln-external-links.js +1 -0
  54. package/js/ln-external-links/src/ln-external-links.js +116 -0
  55. package/js/ln-filter/README.md +99 -0
  56. package/js/ln-filter/ln-filter.js +1 -0
  57. package/js/ln-filter/ln-filter.scss +7 -0
  58. package/js/ln-filter/src/ln-filter.js +404 -0
  59. package/js/ln-form/README.md +101 -0
  60. package/js/ln-form/ln-form.js +1 -0
  61. package/js/ln-form/src/ln-form.js +199 -0
  62. package/js/ln-http/README.md +89 -0
  63. package/js/ln-http/ln-http.js +1 -0
  64. package/js/ln-http/src/ln-http.js +219 -0
  65. package/js/ln-icons/README.md +88 -0
  66. package/js/ln-icons/ln-icons.js +1 -0
  67. package/js/ln-icons/src/ln-icons.js +169 -0
  68. package/js/ln-link/README.md +303 -0
  69. package/js/ln-link/ln-link.js +1 -0
  70. package/js/ln-link/src/ln-link.js +196 -0
  71. package/js/ln-modal/README.md +154 -0
  72. package/js/ln-modal/ln-modal.js +1 -0
  73. package/js/ln-modal/ln-modal.scss +11 -0
  74. package/js/ln-modal/src/ln-modal.js +201 -0
  75. package/js/ln-nav/README.md +70 -0
  76. package/js/ln-nav/ln-nav.js +1 -0
  77. package/js/ln-nav/src/ln-nav.js +177 -0
  78. package/js/ln-number/README.md +122 -0
  79. package/js/ln-number/ln-number.js +1 -0
  80. package/js/ln-number/src/ln-number.js +302 -0
  81. package/js/ln-popover/README.md +127 -0
  82. package/js/ln-popover/ln-popover.js +1 -0
  83. package/js/ln-popover/src/ln-popover.js +288 -0
  84. package/js/ln-progress/README.md +442 -0
  85. package/js/ln-progress/ln-progress.js +1 -0
  86. package/js/ln-progress/src/ln-progress.js +150 -0
  87. package/js/ln-search/README.md +83 -0
  88. package/js/ln-search/ln-search.js +1 -0
  89. package/js/ln-search/ln-search.scss +7 -0
  90. package/js/ln-search/src/ln-search.js +114 -0
  91. package/js/ln-sortable/README.md +95 -0
  92. package/js/ln-sortable/ln-sortable.js +1 -0
  93. package/js/ln-sortable/src/ln-sortable.js +203 -0
  94. package/js/ln-table/README.md +101 -0
  95. package/js/ln-table/ln-table-sort.js +1 -0
  96. package/js/ln-table/ln-table.js +1 -0
  97. package/js/ln-table/ln-table.scss +11 -0
  98. package/js/ln-table/src/ln-table-sort.js +168 -0
  99. package/js/ln-table/src/ln-table.js +473 -0
  100. package/js/ln-tabs/README.md +137 -0
  101. package/js/ln-tabs/ln-tabs.js +1 -0
  102. package/js/ln-tabs/src/ln-tabs.js +171 -0
  103. package/js/ln-time/README.md +81 -0
  104. package/js/ln-time/ln-time.js +1 -0
  105. package/js/ln-time/src/ln-time.js +192 -0
  106. package/js/ln-toast/README.md +122 -0
  107. package/js/ln-toast/ln-toast.js +15 -0
  108. package/js/ln-toast/src/ln-toast.js +210 -0
  109. package/js/ln-toast/template.html +14 -0
  110. package/js/ln-toggle/README.md +137 -0
  111. package/js/ln-toggle/ln-toggle.js +1 -0
  112. package/js/ln-toggle/src/ln-toggle.js +139 -0
  113. package/js/ln-tooltip/README.md +58 -0
  114. package/js/ln-tooltip/ln-tooltip.js +1 -0
  115. package/js/ln-tooltip/ln-tooltip.scss +9 -0
  116. package/js/ln-tooltip/src/ln-tooltip.js +169 -0
  117. package/js/ln-translations/README.md +96 -0
  118. package/js/ln-translations/ln-translations.js +1 -0
  119. package/js/ln-translations/src/ln-translations.js +275 -0
  120. package/js/ln-upload/README.md +180 -0
  121. package/js/ln-upload/ln-upload.js +1 -0
  122. package/js/ln-upload/ln-upload.scss +20 -0
  123. package/js/ln-upload/src/ln-upload.js +407 -0
  124. package/js/ln-validate/README.md +108 -0
  125. package/js/ln-validate/ln-validate.js +1 -0
  126. package/js/ln-validate/src/ln-validate.js +160 -0
  127. package/package.json +55 -0
  128. package/scss/base/_global.scss +83 -0
  129. package/scss/base/_reset.scss +17 -0
  130. package/scss/base/_typography.scss +125 -0
  131. package/scss/components/_accordion.scss +34 -0
  132. package/scss/components/_ajax.scss +15 -0
  133. package/scss/components/_alert.scss +5 -0
  134. package/scss/components/_app-shell.scss +15 -0
  135. package/scss/components/_avatar.scss +6 -0
  136. package/scss/components/_breadcrumbs.scss +33 -0
  137. package/scss/components/_button.scss +20 -0
  138. package/scss/components/_card.scss +10 -0
  139. package/scss/components/_chip.scss +5 -0
  140. package/scss/components/_circular-progress.scss +29 -0
  141. package/scss/components/_confirm.scss +5 -0
  142. package/scss/components/_data-table.scss +83 -0
  143. package/scss/components/_dropdown.scss +25 -0
  144. package/scss/components/_empty-state.scss +22 -0
  145. package/scss/components/_form.scss +100 -0
  146. package/scss/components/_layout.scss +8 -0
  147. package/scss/components/_link.scss +11 -0
  148. package/scss/components/_ln-table.scss +60 -0
  149. package/scss/components/_loader.scss +6 -0
  150. package/scss/components/_modal.scss +20 -0
  151. package/scss/components/_nav.scss +9 -0
  152. package/scss/components/_page-header.scss +10 -0
  153. package/scss/components/_popover.scss +10 -0
  154. package/scss/components/_progress.scss +17 -0
  155. package/scss/components/_prose.scss +5 -0
  156. package/scss/components/_scrollbar.scss +32 -0
  157. package/scss/components/_sections.scss +12 -0
  158. package/scss/components/_sidebar.scss +5 -0
  159. package/scss/components/_stat-card.scss +5 -0
  160. package/scss/components/_status-badge.scss +4 -0
  161. package/scss/components/_stepper.scss +5 -0
  162. package/scss/components/_table.scss +19 -0
  163. package/scss/components/_tabs.scss +21 -0
  164. package/scss/components/_timeline.scss +14 -0
  165. package/scss/components/_toast.scss +41 -0
  166. package/scss/components/_toggle.scss +81 -0
  167. package/scss/components/_tooltip.scss +18 -0
  168. package/scss/components/_translations.scss +111 -0
  169. package/scss/components/_upload.scss +51 -0
  170. package/scss/config/_breakpoints.scss +72 -0
  171. package/scss/config/_density.scss +117 -0
  172. package/scss/config/_icons.scss +37 -0
  173. package/scss/config/_mixins.scss +13 -0
  174. package/scss/config/_theme.scss +216 -0
  175. package/scss/config/_tokens.scss +419 -0
  176. package/scss/config/mixins/_accordion.scss +52 -0
  177. package/scss/config/mixins/_ajax.scss +39 -0
  178. package/scss/config/mixins/_alert.scss +82 -0
  179. package/scss/config/mixins/_app-shell.scss +312 -0
  180. package/scss/config/mixins/_avatar.scss +109 -0
  181. package/scss/config/mixins/_borders.scss +36 -0
  182. package/scss/config/mixins/_breadcrumbs.scss +72 -0
  183. package/scss/config/mixins/_breakpoints.scss +62 -0
  184. package/scss/config/mixins/_btn.scss +179 -0
  185. package/scss/config/mixins/_card.scss +338 -0
  186. package/scss/config/mixins/_chip.scss +66 -0
  187. package/scss/config/mixins/_circular-progress.scss +71 -0
  188. package/scss/config/mixins/_collapsible.scss +24 -0
  189. package/scss/config/mixins/_colors.scss +46 -0
  190. package/scss/config/mixins/_confirm.scss +31 -0
  191. package/scss/config/mixins/_data-table.scss +346 -0
  192. package/scss/config/mixins/_display.scss +32 -0
  193. package/scss/config/mixins/_dropdown.scss +143 -0
  194. package/scss/config/mixins/_empty-state.scss +30 -0
  195. package/scss/config/mixins/_focus.scss +55 -0
  196. package/scss/config/mixins/_footer.scss +42 -0
  197. package/scss/config/mixins/_form.scss +601 -0
  198. package/scss/config/mixins/_index.scss +58 -0
  199. package/scss/config/mixins/_interaction.scss +15 -0
  200. package/scss/config/mixins/_kbd.scss +22 -0
  201. package/scss/config/mixins/_layout.scss +117 -0
  202. package/scss/config/mixins/_link.scss +55 -0
  203. package/scss/config/mixins/_ln-table.scss +420 -0
  204. package/scss/config/mixins/_loader.scss +26 -0
  205. package/scss/config/mixins/_modal.scss +66 -0
  206. package/scss/config/mixins/_motion.scss +19 -0
  207. package/scss/config/mixins/_nav.scss +273 -0
  208. package/scss/config/mixins/_page-header.scss +69 -0
  209. package/scss/config/mixins/_popover.scss +25 -0
  210. package/scss/config/mixins/_position.scss +32 -0
  211. package/scss/config/mixins/_progress.scss +56 -0
  212. package/scss/config/mixins/_prose.scss +127 -0
  213. package/scss/config/mixins/_shadows.scss +8 -0
  214. package/scss/config/mixins/_sidebar.scss +95 -0
  215. package/scss/config/mixins/_sizing.scss +6 -0
  216. package/scss/config/mixins/_spacing.scss +19 -0
  217. package/scss/config/mixins/_stat-card.scss +68 -0
  218. package/scss/config/mixins/_status-badge.scss +83 -0
  219. package/scss/config/mixins/_stepper.scss +78 -0
  220. package/scss/config/mixins/_table.scss +215 -0
  221. package/scss/config/mixins/_tabs.scss +64 -0
  222. package/scss/config/mixins/_timeline.scss +69 -0
  223. package/scss/config/mixins/_toast.scss +148 -0
  224. package/scss/config/mixins/_tooltip.scss +111 -0
  225. package/scss/config/mixins/_transitions.scss +10 -0
  226. package/scss/config/mixins/_translations.scss +124 -0
  227. package/scss/config/mixins/_typography.scss +57 -0
  228. package/scss/config/mixins/_upload.scss +168 -0
  229. package/scss/ln-ashlar.scss +62 -0
  230. package/scss/tabler-icons.txt +5039 -0
  231. package/scss/utilities/_animations.scss +83 -0
  232. package/scss/utilities/_utilities.scss +49 -0
@@ -0,0 +1,117 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'breakpoints' as *;
4
+
5
+ // Layout — grid, stack, container
6
+
7
+ @mixin grid {
8
+ display: grid;
9
+ --gap: var(--size-lg);
10
+ gap: var(--gap);
11
+ grid-template-columns: repeat(1, minmax(0, 1fr));
12
+
13
+ @include mq-up(md) {
14
+ grid-template-columns: repeat(2, minmax(0, 1fr));
15
+ }
16
+
17
+ @include mq-up(lg) {
18
+ grid-template-columns: repeat(3, minmax(0, 1fr));
19
+ }
20
+ }
21
+
22
+ @mixin grid-2 {
23
+ display: grid;
24
+ --gap: var(--size-lg);
25
+ gap: var(--gap);
26
+ grid-template-columns: repeat(1, minmax(0, 1fr));
27
+
28
+ @include mq-up(md) {
29
+ grid-template-columns: repeat(2, minmax(0, 1fr));
30
+ }
31
+ }
32
+
33
+ @mixin grid-4 {
34
+ display: grid;
35
+ --gap: var(--size-lg);
36
+ gap: var(--gap);
37
+ grid-template-columns: repeat(1, minmax(0, 1fr));
38
+
39
+ @include mq-up(md) {
40
+ grid-template-columns: repeat(2, minmax(0, 1fr));
41
+ }
42
+
43
+ @include mq-up(lg) {
44
+ grid-template-columns: repeat(4, minmax(0, 1fr));
45
+ }
46
+ }
47
+
48
+ @mixin stack($gap: var(--gap)) {
49
+ @include flex-col;
50
+ @include gap($gap);
51
+ }
52
+
53
+ @mixin row($gap: var(--gap)) {
54
+ @include flex-row;
55
+ @include items-center;
56
+ @include gap($gap);
57
+ }
58
+
59
+ @mixin row-between($gap: var(--gap)) {
60
+ @include row($gap);
61
+ @include justify-between;
62
+ }
63
+
64
+ @mixin row-center($gap: var(--gap)) {
65
+ @include row($gap);
66
+ @include justify-center;
67
+ }
68
+
69
+ // Container Queries
70
+ // Parent: @include container(name)
71
+ // Child: @include cq-up(compact, name) { ... }
72
+ //
73
+ // IMPORTANT: Do NOT combine container-type with overflow:hidden on the same element
74
+ //
75
+ // Breakpoints: 480px, 580px, 880px, 1120px
76
+ @mixin container($name: null) {
77
+ // Nameless form is intentional — models native anonymous @container queries.
78
+ // See docs/css/layout.md ("Anonymous container") and demo/admin/mixins.html.
79
+ container-type: inline-size;
80
+ @if $name { container-name: $name; }
81
+ }
82
+
83
+ // ─── Flat stack ────────────────────────────────────────
84
+ // Vertical joined-panel pattern: children sit flush with a single
85
+ // shared horizontal rule between them. Consumes the per-side border
86
+ // soft token `--border-block-start` (no :root default) to suppress
87
+ // each child's top edge after the first.
88
+ //
89
+ // Works on any child that reads --radius and --shadow for
90
+ // its own chrome (card, section-card, stat-card, bare <article>
91
+ // with @include card). Self-contained: also re-binds --radius: 0
92
+ // and --shadow: none on direct children so rounded corners
93
+ // and elevation shadows do not conflict with the flat shared rule.
94
+ //
95
+ // Cascade note: the --radius / --shadow re-binds cascade
96
+ // into grandchildren. If a consumer nests a rounded card inside a
97
+ // flat-stack panel, re-bind --radius on that nested element.
98
+ //
99
+ // Block-axis only. For horizontal joined controls use btn-group or
100
+ // pill-group — they use border-radius tricks on first/last children
101
+ // instead of border-block-* suppression.
102
+ //
103
+ // Usage:
104
+ // .flat-stack { @include flat-stack; } // prototype class
105
+ // #users > ul { @include flat-stack; } // project selector
106
+ @mixin flat-stack {
107
+ --gap: 0;
108
+
109
+ > * {
110
+ --radius: 0;
111
+ --shadow: none;
112
+ }
113
+
114
+ > * + * {
115
+ --border-block-start: none;
116
+ }
117
+ }
@@ -0,0 +1,55 @@
1
+ @use 'display' as *;
2
+ @use 'spacing' as *;
3
+ @use 'typography' as *;
4
+ @use 'colors' as *;
5
+ @use 'borders' as *;
6
+ @use 'position' as *;
7
+ @use 'interaction' as *;
8
+ @use 'motion' as *;
9
+
10
+ // Clickable rows — cursor + selection prevention for data-ln-link containers.
11
+ //
12
+ // Usage:
13
+ // [data-ln-link] tr { @include link-row; }
14
+
15
+ @mixin link-row {
16
+ @include cursor-pointer;
17
+ @include select-none;
18
+ }
19
+
20
+ // Status bar — mimics browser's native URL preview at bottom-left.
21
+ //
22
+ // Usage:
23
+ // .ln-link-status { @include link-status; }
24
+ //
25
+ // JS toggles .ln-link-status--visible to show/hide.
26
+
27
+ @mixin link-status {
28
+ @include fixed;
29
+ bottom: 0;
30
+ left: 0;
31
+ --padding-y: var(--size-xs);
32
+ --padding-x: var(--size-sm-up);
33
+ padding: var(--padding-y) var(--padding-x);
34
+ background: var(--color-bg);
35
+ @include border-t;
36
+ @include border-r;
37
+ @include text-xs;
38
+ --color-fg: var(--fg-muted);
39
+ color: var(--color-fg);
40
+ @include rounded-sm;
41
+ border-bottom-left-radius: 0;
42
+ border-top-left-radius: 0;
43
+ max-width: 50vw;
44
+ @include truncate;
45
+ @include z-toast;
46
+ opacity: 0;
47
+ pointer-events: none;
48
+ @include motion-safe {
49
+ transition: opacity var(--transition-fast);
50
+ }
51
+
52
+ &--visible {
53
+ opacity: 1;
54
+ }
55
+ }
@@ -0,0 +1,420 @@
1
+ @use 'display' as *;
2
+ @use 'spacing' as *;
3
+ @use 'sizing' as *;
4
+ @use 'typography' as *;
5
+ @use 'colors' as *;
6
+ @use 'borders' as *;
7
+ @use 'shadows' as *;
8
+ @use 'position' as *;
9
+ @use 'transitions' as *;
10
+ @use 'interaction' as *;
11
+ @use 'motion' as *;
12
+ @use 'layout' as *;
13
+ @use 'card' as *;
14
+
15
+ // ln-table -- Enhanced data table wrapper component.
16
+ //
17
+ // Provides toolbar, sticky headers, column filter dropdowns, sort
18
+ // indicators, footer, filter chips, empty state, timing badge.
19
+ //
20
+ // Usage:
21
+ // [data-ln-table] { @include ln-table; }
22
+ // .ln-table__toolbar { @include ln-table-toolbar; }
23
+ //
24
+ // State-driven CSS (spacer rows, sort icon .hidden toggle, content overflow
25
+ // fix) stays in the co-located JS SCSS file.
26
+
27
+ @mixin ln-table {
28
+ @include border;
29
+ --radius: var(--radius-lg);
30
+ border-radius: var(--radius);
31
+ background: var(--color-bg);
32
+ overflow: clip;
33
+
34
+ // Table overrides inside component
35
+ table {
36
+ overflow: visible;
37
+ border-collapse: separate;
38
+ border-spacing: 0;
39
+ border-radius: 0;
40
+ box-shadow: none;
41
+ }
42
+
43
+ // Whole thead sticky as one unit (toolbar row + column-headers row).
44
+ // Wrapper's `overflow: clip` + `rounded-lg` clip the top corners,
45
+ // so per-th radius is unnecessary.
46
+ thead {
47
+ @include sticky-top;
48
+ }
49
+
50
+ thead th {
51
+ background: var(--bg-sunken);
52
+ }
53
+
54
+ // First td override -- remove global bold/bg
55
+ table td:first-child {
56
+ background: transparent;
57
+ font-weight: normal;
58
+ --color-fg: var(--fg-muted);
59
+ color: var(--color-fg);
60
+ }
61
+ }
62
+
63
+ // ─── Toolbar ──────────────────────────────────────────────
64
+
65
+ @mixin ln-table-toolbar {
66
+ @include panel-header;
67
+ position: sticky;
68
+ top: 0;
69
+ z-index: var(--z-sticky);
70
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
71
+ }
72
+
73
+ // ─── In-thead toolbar (first thead row, single colspan th) ───────
74
+ //
75
+ // Applied to the <th colspan="N"> in the first thead row of an
76
+ // ln-table. Expected HTML:
77
+ //
78
+ // <tr><th colspan="N"><div> ...toolbar content... </div></th></tr>
79
+ //
80
+ // The <th> stays display:table-cell so colspan participates in
81
+ // table layout normally. The wrapper <div> carries the panel-header
82
+ // chrome (flex layout, padding, border-bottom, sunken bg). The
83
+ // <th> itself gets padding:0 plus explicit resets to break the
84
+ // @mixin table-base cascade (uppercase / text-label-sm /
85
+ // letter-spacing / semibold) so the toolbar's <h3>, <span>, <label>
86
+ // children render with normal text styling, not column-header
87
+ // styling.
88
+
89
+ @mixin ln-table-thead-toolbar {
90
+ // <th> resets — chrome lives on the inner <div>.
91
+ padding: 0;
92
+ text-align: left;
93
+ text-transform: none;
94
+ letter-spacing: normal;
95
+ font-size: var(--font-size);
96
+ font-weight: normal;
97
+
98
+ > div {
99
+ @include panel-header;
100
+ }
101
+ }
102
+
103
+ @mixin ln-table-toolbar-search {
104
+ width: 10rem;
105
+ }
106
+
107
+ // ─── Count badge ──────────────────────────────────────────
108
+
109
+ @mixin ln-table-count {
110
+ @include text-sm;
111
+ --color-fg: var(--fg-muted);
112
+ color: var(--color-fg);
113
+
114
+ strong {
115
+ color: var(--color-fg);
116
+ @include font-semibold;
117
+ }
118
+ }
119
+
120
+ // ─── Card mode container query ────────────────────────────
121
+
122
+ @mixin ln-table-card-mode {
123
+ @container table (max-width: 640px) {
124
+ colgroup { display: none; }
125
+ table { table-layout: auto !important; }
126
+ }
127
+ }
128
+
129
+ // ─── Column filter dropdowns in th ────────────────────────
130
+
131
+ @mixin ln-table-column-filter {
132
+ th [data-ln-dropdown] {
133
+ @include inline-flex;
134
+ @include items-center;
135
+ }
136
+
137
+ th [data-ln-dropdown] > button {
138
+ @include flex;
139
+ @include items-center;
140
+ --gap: var(--size-xs);
141
+ gap: var(--gap);
142
+ background: none;
143
+ border: none;
144
+ color: inherit;
145
+ font: inherit;
146
+ @include cursor-pointer;
147
+ --btn-padding-y: 0;
148
+ --btn-padding-x: 0;
149
+
150
+ .ln-icon-filter {
151
+ opacity: 0.4;
152
+ @include motion-safe {
153
+ transition: opacity var(--transition-fast);
154
+ }
155
+ }
156
+
157
+ &:hover .ln-icon-filter {
158
+ opacity: 1;
159
+ }
160
+ }
161
+
162
+ th [data-ln-dropdown-menu] button[data-ln-filter-key] {
163
+ @include w-full;
164
+ @include flex;
165
+ @include items-center;
166
+ @include text-left;
167
+ --btn-padding-y: var(--size-xs);
168
+ --btn-padding-x: var(--size-sm);
169
+ @include text-sm;
170
+ @include font-normal;
171
+ background: none;
172
+ border: none;
173
+ border-radius: 0;
174
+ color: var(--color-fg);
175
+ @include cursor-pointer;
176
+ @include transition-fast;
177
+
178
+ &:hover {
179
+ --color-bg: var(--bg-sunken);
180
+ background: var(--color-bg);
181
+ }
182
+
183
+ &[data-active] {
184
+ background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
185
+ color: var(--color-accent);
186
+ @include font-medium;
187
+ }
188
+ }
189
+ }
190
+
191
+ // ─── Sort indicators on th ────────────────────────────────
192
+
193
+ @mixin ln-table-sort {
194
+ th[data-ln-sort] {
195
+ @include cursor-pointer;
196
+ @include select-none;
197
+
198
+ [data-ln-sort-icon] {
199
+ @include size(0.875rem);
200
+ --margin-inline: var(--size-xs);
201
+ margin-left: var(--margin-inline);
202
+ vertical-align: middle;
203
+ opacity: 0.4;
204
+ @include motion-safe {
205
+ transition: opacity var(--transition-fast);
206
+ }
207
+ }
208
+
209
+ &:hover [data-ln-sort-icon] {
210
+ opacity: 0.7;
211
+ }
212
+
213
+ &[data-ln-sort-active] [data-ln-sort-icon] {
214
+ opacity: 1;
215
+ }
216
+ }
217
+ }
218
+
219
+ // ─── Footer ───────────────────────────────────────────────
220
+
221
+ @mixin ln-table-footer {
222
+ @include flex;
223
+ @include items-center;
224
+ @include justify-between;
225
+ --padding-y: var(--size-sm);
226
+ --padding-x: var(--size-md);
227
+ padding: var(--padding-y) var(--padding-x);
228
+ @include border-t;
229
+ --color-bg: var(--bg-sunken);
230
+ background: var(--color-bg);
231
+ border-radius: 0 0 var(--radius-lg) var(--radius-lg);
232
+ @include text-sm;
233
+ --color-fg: var(--fg-muted);
234
+ color: var(--color-fg);
235
+
236
+ strong {
237
+ color: var(--color-fg);
238
+ @include font-semibold;
239
+ }
240
+ }
241
+
242
+ // ─── Timing badge ─────────────────────────────────────────
243
+
244
+ @mixin ln-table-timing {
245
+ @include text-xs;
246
+ --color-fg: var(--fg-subtle);
247
+ color: var(--color-fg);
248
+ font-family: var(--font-mono);
249
+ }
250
+
251
+ // ─── Quick filter bar ─────────────────────────────────────
252
+
253
+ @mixin ln-table-filters {
254
+ @include flex;
255
+ @include flex-wrap;
256
+ @include items-center;
257
+ --gap: var(--size-lg);
258
+ gap: var(--gap);
259
+ --padding-y: var(--size-sm);
260
+ --padding-x: var(--size-md);
261
+ padding: var(--padding-y) var(--padding-x);
262
+ @include border-b;
263
+
264
+ fieldset {
265
+ border: none;
266
+ padding: 0;
267
+ margin: 0;
268
+ @include flex;
269
+ @include items-center;
270
+ --gap: var(--size-xs-up);
271
+ gap: var(--gap);
272
+
273
+ legend {
274
+ @include text-xs;
275
+ @include font-medium;
276
+ --color-fg: var(--fg-subtle);
277
+ color: var(--color-fg);
278
+ float: left;
279
+ --margin-inline: var(--size-sm);
280
+ margin-right: var(--margin-inline);
281
+ line-height: 1.75rem;
282
+ }
283
+
284
+ ul {
285
+ @include inline-flex;
286
+ }
287
+
288
+ li button {
289
+ --btn-padding-y: var(--size-2xs);
290
+ --btn-padding-x: var(--size-sm);
291
+ @include text-xs;
292
+ @include font-medium;
293
+ @include rounded-full;
294
+ @include transition-fast;
295
+ background: transparent;
296
+ --color-fg: var(--fg-muted);
297
+ color: var(--color-fg);
298
+ border: none;
299
+
300
+ &:hover {
301
+ --color-bg: var(--bg-sunken);
302
+ background-color: var(--color-bg);
303
+ color: var(--color-fg);
304
+ }
305
+
306
+ &[data-active] {
307
+ background-color: color-mix(in srgb, var(--color-accent) 10%, transparent);
308
+ color: var(--color-accent);
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ // ─── Active filter chips ──────────────────────────────────
315
+
316
+ @mixin ln-table-chips {
317
+ @include flex;
318
+ @include items-center;
319
+ @include flex-wrap;
320
+ --gap: var(--size-xs-up);
321
+ gap: var(--gap);
322
+ --padding-y: var(--size-xs-up);
323
+ --padding-x: var(--size-md);
324
+ padding: var(--padding-y) var(--padding-x);
325
+ @include border-b;
326
+ background-color: color-mix(in srgb, var(--color-accent) 3%, transparent);
327
+ }
328
+
329
+ @mixin ln-table-chip {
330
+ @include inline-flex;
331
+ @include items-center;
332
+ --gap: var(--size-xs);
333
+ gap: var(--gap);
334
+ --padding-y: var(--size-2xs);
335
+ --padding-x: var(--size-sm);
336
+ padding: var(--padding-y) var(--padding-x);
337
+ @include rounded-full;
338
+ @include text-xs;
339
+ @include tinted-surface(0.1);
340
+
341
+ strong {
342
+ @include font-semibold;
343
+ }
344
+
345
+ button {
346
+ background: none;
347
+ border: none;
348
+ color: var(--color-accent);
349
+ @include cursor-pointer;
350
+ @include text-sm;
351
+ @include font-bold;
352
+ line-height: 1;
353
+ --btn-padding-y: 0;
354
+ --btn-padding-x: 0;
355
+ --margin-inline: var(--size-2xs);
356
+ margin-left: var(--margin-inline);
357
+
358
+ &:hover {
359
+ color: hsl(var(--color-error));
360
+ }
361
+ }
362
+ }
363
+
364
+ @mixin ln-table-clear-all {
365
+ @include text-xs;
366
+ @include font-medium;
367
+ --color-fg: var(--fg-subtle);
368
+ color: var(--color-fg);
369
+ background: none;
370
+ border: none;
371
+ @include cursor-pointer;
372
+ --margin-inline: var(--size-xs);
373
+ margin-left: var(--margin-inline);
374
+
375
+ &:hover {
376
+ color: hsl(var(--color-error));
377
+ }
378
+ }
379
+
380
+ // ─── Empty state ──────────────────────────────────────────
381
+
382
+ @mixin ln-table-empty-state {
383
+ --padding-y: var(--size-2xl);
384
+ padding-block: var(--padding-y);
385
+ @include text-center;
386
+
387
+ svg.ln-icon {
388
+ @include block;
389
+ @include mx(auto);
390
+ --margin-block: var(--size-md);
391
+ margin-bottom: var(--margin-block);
392
+ opacity: 0.3;
393
+ }
394
+
395
+ h3 {
396
+ @include text-lg;
397
+ @include font-semibold;
398
+ color: var(--color-fg);
399
+ --margin-block: var(--size-sm);
400
+ margin: 0 0 var(--margin-block);
401
+ }
402
+
403
+ p {
404
+ @include text-sm;
405
+ --color-fg: var(--fg-muted);
406
+ color: var(--color-fg);
407
+ --margin-block: var(--size-lg);
408
+ margin: 0 0 var(--margin-block);
409
+ }
410
+ }
411
+
412
+ // Virtual-scroll spacer rows (JS-injected) — neutralize cell chrome including bg.
413
+
414
+ @mixin ln-table-spacer-row {
415
+ td {
416
+ padding: 0 !important;
417
+ border: none !important;
418
+ background: transparent !important;
419
+ }
420
+ }
@@ -0,0 +1,26 @@
1
+ @use 'display' as *;
2
+ @use 'spacing' as *;
3
+ @use 'sizing' as *;
4
+ @use 'position' as *;
5
+ @use 'colors' as *;
6
+ @use 'motion' as *;
7
+
8
+ @mixin loader {
9
+ color: var(--color-accent);
10
+ // Spinner is drawn with em-based box-shadow; font-size is the intrinsic
11
+ // size knob. Callers override per-use (e.g. font-size: 32px) — see
12
+ // docs/css/loader.md. Page-level margin is a binding concern, not a
13
+ // mixin concern, and lives on the consumer selector.
14
+ font-size: 90px;
15
+ text-indent: -9999em;
16
+ @include overflow-hidden;
17
+ width: 1em;
18
+ height: 1em;
19
+ border-radius: 50%;
20
+ @include mx(auto);
21
+ @include relative;
22
+ transform: translateZ(0);
23
+ @include motion-safe {
24
+ animation: loader-spin 1.7s infinite ease, loader-round 1.7s infinite ease;
25
+ }
26
+ }
@@ -0,0 +1,66 @@
1
+ @use 'display' as *;
2
+ @use 'spacing' as *;
3
+ @use 'position' as *;
4
+ @use 'motion' as *;
5
+ @use 'card' as *;
6
+
7
+ // Modal size mixins
8
+ // Usage: #my-modal > form { @include modal-lg; }
9
+
10
+ @mixin modal-sm { max-width: 28rem; }
11
+ @mixin modal-md { max-width: 32rem; }
12
+ @mixin modal-lg { max-width: 42rem; }
13
+ @mixin modal-xl { max-width: 48rem; }
14
+
15
+ // ─── Modal layout ──────────────────────────────────────
16
+
17
+ // Overlay backdrop — covers viewport, darkened + blurred.
18
+ // Default: hidden (display:none). JS sets [data-ln-modal="open"]
19
+ // to show — that state rule lives in the co-located JS SCSS.
20
+ @mixin modal-overlay {
21
+ display: none;
22
+ @include fixed;
23
+ @include inset-0;
24
+ background-color: var(--color-scrim);
25
+ backdrop-filter: blur(4px);
26
+ -webkit-backdrop-filter: blur(4px);
27
+ z-index: var(--z-modal);
28
+ @include items-center;
29
+ @include justify-center;
30
+ opacity: 0;
31
+ @include motion-safe {
32
+ transition: opacity var(--transition);
33
+ }
34
+ }
35
+
36
+ // Content panel — the <form> inside .ln-modal.
37
+ // Structurally IS a section-card: reuse its chrome and its nested
38
+ // > header / > main / > footer recipes. Add only the modal-specific
39
+ // behaviour on top: fixed size envelope, scrollable body, sticky
40
+ // footer, slide-in animation, deeper shadow.
41
+ @mixin modal-panel {
42
+ @include section-card;
43
+ margin-bottom: 0;
44
+ // Explicit role-scale read — modals use max elevation (deeper than
45
+ // the --shadow-floating default used by floating panels).
46
+ box-shadow: var(--shadow-xl);
47
+ display: grid;
48
+ grid-template-rows: auto 1fr auto;
49
+ max-width: 600px;
50
+ width: 90%;
51
+ max-height: 90vh;
52
+ @include motion-safe {
53
+ animation: ln-modal-slideIn var(--transition-slow);
54
+ }
55
+
56
+ > main {
57
+ min-height: 0;
58
+ overflow-y: auto;
59
+ overflow-x: hidden;
60
+ }
61
+
62
+ > footer {
63
+ position: sticky;
64
+ bottom: 0;
65
+ }
66
+ }
@@ -0,0 +1,19 @@
1
+ // Motion safety — gate animation-only declarations behind
2
+ // prefers-reduced-motion: no-preference.
3
+ //
4
+ // Usage:
5
+ // .ln-modal__overlay {
6
+ // @include motion-safe {
7
+ // animation: fadeIn var(--transition-base);
8
+ // }
9
+ // }
10
+ //
11
+ // Color transitions on hover are fine unwrapped — they do not trigger
12
+ // vestibular issues. Only wrap transform, opacity, translate, scale,
13
+ // rotate, and keyframe animations.
14
+
15
+ @mixin motion-safe {
16
+ @media (prefers-reduced-motion: no-preference) {
17
+ @content;
18
+ }
19
+ }