@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,62 @@
1
+ @use 'sass:map';
2
+ @use '../breakpoints' as bp;
3
+
4
+ // ==========================================================================
5
+ // Breakpoint mixins — resolve against $breakpoints map in
6
+ // scss/config/_breakpoints.scss. Never hardcode px inside @media or
7
+ // @container; always use these.
8
+ //
9
+ // Usage:
10
+ // @include mq-up(md) → @media (min-width: 768px)
11
+ // @include mq-down(lg) → @media (max-width: 1023px)
12
+ // @include cq-up(medium, page-header) → @container page-header (min-width: 880px)
13
+ // @include cq-down(compact) → anonymous @container, max-width: 579px
14
+ //
15
+ // Unknown $name raises @error.
16
+ // ==========================================================================
17
+
18
+ @function _resolve($name) {
19
+ $value: map.get(bp.$breakpoints, $name);
20
+ @if $value == null {
21
+ @error "Unknown breakpoint name: #{$name}. Valid keys: #{map.keys(bp.$breakpoints)}";
22
+ }
23
+ @return $value;
24
+ }
25
+
26
+ @mixin mq-up($name) {
27
+ @media (min-width: _resolve($name)) {
28
+ @content;
29
+ }
30
+ }
31
+
32
+ @mixin mq-down($name) {
33
+ @media (max-width: _resolve($name) - 1px) {
34
+ @content;
35
+ }
36
+ }
37
+
38
+ @mixin cq-up($name, $container: null) {
39
+ $min: _resolve($name);
40
+ @if $container {
41
+ @container #{$container} (min-width: #{$min}) {
42
+ @content;
43
+ }
44
+ } @else {
45
+ @container (min-width: #{$min}) {
46
+ @content;
47
+ }
48
+ }
49
+ }
50
+
51
+ @mixin cq-down($name, $container: null) {
52
+ $max: _resolve($name);
53
+ @if $container {
54
+ @container #{$container} (max-width: #{$max - 1px}) {
55
+ @content;
56
+ }
57
+ } @else {
58
+ @container (max-width: #{$max - 1px}) {
59
+ @content;
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,179 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'sizing' as *;
4
+ @use 'typography' as *;
5
+ @use 'colors' as *;
6
+ @use 'borders' as *;
7
+ @use 'transitions' as *;
8
+ @use 'interaction' as *;
9
+ @use 'focus' as *;
10
+
11
+ // Buttons
12
+ // button-base — shared neutral chrome (global button + anchor-as-button)
13
+ // btn — accent variant of button-base; rebinds --btn-* to accent colors
14
+ // btn-sm — smaller size modifier
15
+ // btn-lg — larger size modifier
16
+ // btn-group — inline list of peer action buttons (ghost rebind)
17
+
18
+ // ─── button-base ──────────────────────────────────────
19
+ // Neutral chrome shared by <button>, <input type="submit|reset|button">,
20
+ // and anchors used as buttons (e.g. inside form-actions). Reads the
21
+ // six-token --btn-* surface: --btn-bg/fg/border (idle) and
22
+ // --btn-bg-hover/fg-hover/border-hover (hover + active). Both the
23
+ // neutral default and the accent variant are fully token-driven —
24
+ // the difference is which values --btn-* resolves to.
25
+ //
26
+ // Neutral default: --btn-* read from :root, where they wire to
27
+ // --color-bg / --color-fg / --border-strong (and --border-strong-hover
28
+ // companions for the hover state). Theme overrides rebind --btn-*
29
+ // (e.g. Glass rebinds --btn-bg to --bg-elevated, --btn-border
30
+ // to --color-accent for an outlined neutral chrome).
31
+ //
32
+ // Accent variant (submit + @mixin btn): rebinds --btn-* on the
33
+ // consumer element to accent values. Because the rebind happens on
34
+ // the button itself (not at :root), and because @mixin btn first
35
+ // re-declares --color-accent: hsl(var(--color-primary)) at the same
36
+ // scope, .success/.error/.warning/.info classes that override
37
+ // --color-primary cascade through --color-accent → --btn-* →
38
+ // background/color/border-color cleanly. var() always resolves at
39
+ // the declaration site, and the declaration site for both the
40
+ // rebind and button-base's reads is the button element.
41
+ //
42
+ // Why cross-cutting --color-accent-bg-* companions, not per-component
43
+ // --btn-accent-* tokens at :root: a per-component surface declared at
44
+ // :root (--btn-accent-bg: var(--color-accent)) would freeze
45
+ // --color-accent at :root scope and break the
46
+ // .success/.error/.warning/.info cascade for descendant overrides.
47
+ // Cross-cutting --color-accent-bg-* companions (read via fallback to
48
+ // --color-accent / --color-accent-fg) sidestep that: the default
49
+ // theme leaves them unset (mixin falls back to solid + white), Glass
50
+ // rebinds them at theme :root to flip the accent button to
51
+ // translucent. Solid-accent consumers (toast-side, pill-checked,
52
+ // stepper active/complete) read --color-accent / --color-accent-fg
53
+ // directly and are unaffected by Glass's rebind because Glass leaves
54
+ // --color-accent-fg alone. See CLAUDE.md ## Theme Architecture for
55
+ // the full contract.
56
+
57
+ @mixin button-base {
58
+ @include inline-flex;
59
+ @include items-center;
60
+ @include justify-center;
61
+ gap: var(--gap);
62
+ padding: var(--padding-y) var(--padding-x);
63
+ @include text-sm;
64
+ @include font-medium;
65
+ border-radius: var(--radius);
66
+ white-space: nowrap;
67
+ border: var(--border-width) solid var(--btn-border);
68
+ outline: none;
69
+ background: var(--btn-bg);
70
+ color: var(--btn-fg);
71
+ @include transition-colors;
72
+
73
+ &:hover:not(:disabled) {
74
+ background: var(--btn-bg-hover);
75
+ color: var(--btn-fg-hover);
76
+ border-color: var(--btn-border-hover);
77
+ }
78
+
79
+ &:active:not(:disabled) {
80
+ background: var(--btn-bg-hover);
81
+ border-color: var(--btn-border-hover);
82
+ }
83
+
84
+ &:focus-visible {
85
+ @include focus-ring;
86
+ }
87
+
88
+ &:disabled {
89
+ @include opacity-50;
90
+ @include cursor-not-allowed;
91
+ }
92
+ }
93
+
94
+ @mixin btn {
95
+ // Accent variant — rebinds the --btn-* token surface to accent
96
+ // values. button-base reads --btn-bg/fg/border (idle) and
97
+ // --btn-bg-hover/fg-hover/border-hover (hover + active), so this
98
+ // mixin only needs to set tokens. No direct background/color/
99
+ // border-color, no &:hover/&:active blocks.
100
+ //
101
+ // Re-declares --color-accent on the consumer element so
102
+ // var(--color-primary) re-resolves at this scope.
103
+ // .success/.error/.warning/.info classes override --color-primary;
104
+ // without these re-declarations, --color-accent stays frozen at
105
+ // :root and semantic colors don't cascade.
106
+ //
107
+ // --color-accent-fg is NOT re-declared — the :root default
108
+ // (hsl(var(--color-white))) already cascades, and re-declaring
109
+ // here would shadow theme companion overrides on solid-only
110
+ // surfaces (which there are none here, but the redundancy is
111
+ // noise either way).
112
+ --color-accent: hsl(var(--color-primary));
113
+
114
+ // Hover state derived from --color-accent at THIS scope via CSS
115
+ // relative color syntax — same hue + saturation, lightness reduced
116
+ // by 8. Computing here (consumer scope) instead of reading a
117
+ // separate :root token means a consumer override (e.g.
118
+ // `style="--color-primary: var(--color-error)"` or a .error
119
+ // ancestor via @mixin semantic-color) cascades to BOTH the idle
120
+ // and hover surfaces — the whole state machine follows from a
121
+ // single token change.
122
+ //
123
+ // IMPORTANT — `calc(l - 8)` not `calc(l - 8%)`. Inside
124
+ // relative color syntax, channel keywords (h/s/l) evaluate to
125
+ // <number> tokens, not <percentage>. `l - 8%` mixes types →
126
+ // invalid color → browser falls back to `transparent` and the
127
+ // hover bg vanishes. The number form is what the spec mandates;
128
+ // hsl(216 95 34) without `%` is valid CSS Color 4 and reads as
129
+ // "lightness 34%" exactly like the legacy hsl(216 95% 34%) form.
130
+ //
131
+ // Pattern shared with @mixin pill, @mixin data-table-row-focused,
132
+ // the anchor hover in _global.scss, and @mixin semantic-color's
133
+ // status-family cascade. -light / -lighter remain explicit tokens
134
+ // (theme-controlled, not derivable from base).
135
+ --color-accent-hover: hsl(from var(--color-accent) h s calc(l - 8));
136
+
137
+ // --btn-bg / --btn-fg read translucent companions via fallback.
138
+ // Default theme: companions unset → falls back to --color-accent
139
+ // / --color-accent-fg (solid + white). Glass theme: companions
140
+ // rebound at :root → translucent fill + accent-colored fg. The
141
+ // var() fallback resolves at the consumer (this button), so the
142
+ // .success/.error/.warning/.info cascade through --color-primary
143
+ // still works in both themes.
144
+ --btn-bg: var(--color-accent-bg, var(--color-accent));
145
+ --btn-fg: var(--color-accent-bg-fg, var(--color-accent-fg));
146
+ --btn-border: var(--color-accent);
147
+ --btn-bg-hover: var(--color-accent-bg-hover, var(--color-accent-hover));
148
+ --btn-fg-hover: var(--color-accent-bg-fg, var(--color-accent-fg));
149
+ --btn-border-hover: var(--color-accent-hover);
150
+ }
151
+
152
+ @mixin btn-sm {
153
+ --btn-padding-x: var(--size-sm-up);
154
+ --gap: var(--size-xs);
155
+ gap: var(--gap);
156
+ @include text-xs;
157
+ }
158
+
159
+ @mixin btn-lg {
160
+ --btn-padding-x: var(--size-lg);
161
+ --btn-padding-y: var(--size-md);
162
+ --gap: var(--size-sm-up);
163
+ gap: var(--gap);
164
+ @include text-base;
165
+ }
166
+
167
+ @mixin btn-group {
168
+ --gap: var(--size-xs);
169
+ @include inline-flex;
170
+ @include items-center;
171
+ gap: var(--gap);
172
+
173
+ button {
174
+ --btn-bg: transparent;
175
+ --btn-border: transparent;
176
+ color: var(--fg-subtle);
177
+ }
178
+ }
179
+
@@ -0,0 +1,338 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'sizing' as *;
4
+ @use 'typography' as *;
5
+ @use 'colors' as *;
6
+ @use 'borders' as *;
7
+ @use 'shadows' as *;
8
+ @use 'transitions' as *;
9
+ @use 'position' as *;
10
+
11
+ // Card & Section — composite mixins
12
+
13
+ @mixin card {
14
+ background: var(--color-bg);
15
+ @include border;
16
+ @include w-full;
17
+ @include flex-col;
18
+ border-radius: var(--radius);
19
+ @include overflow-hidden;
20
+ @include transition;
21
+ --shadow: var(--shadow-resting);
22
+ box-shadow: var(--shadow);
23
+
24
+ &:hover {
25
+ --color-border: var(--border-strong);
26
+ --shadow: var(--shadow-floating);
27
+ }
28
+
29
+ // Direct-child selectors only — nested <header>/<main>/<footer>
30
+ // inside card content are consumer-owned and must not inherit the
31
+ // card's chrome. Same rationale as @mixin section-card.
32
+ > header { @include panel-header; }
33
+ > main { @include panel-body; }
34
+ > footer { @include panel-footer; }
35
+
36
+ // Card-tile behavior: main stretches to equalize heights when
37
+ // cards are placed in a grid row, and provides rhythm between
38
+ // stacked children (h3, p, .field) via the logical gap token.
39
+ > main {
40
+ @include flex-1;
41
+ gap: var(--gap);
42
+ }
43
+
44
+ // Whole-card-as-link: <a> wraps card content and fills the card.
45
+ > a {
46
+ @include flex-col;
47
+ @include flex-1;
48
+ text-decoration: none;
49
+ color: inherit;
50
+ }
51
+
52
+ // Tile-header h3: when a card carries a title inside > main
53
+ // without a <header> wrapper, the global h3 cascade (heading-sm)
54
+ // is too large for tile context. Tighten to tile-title sizing.
55
+ > main h3 {
56
+ font-size: var(--text-body-sm);
57
+ @include font-semibold;
58
+ color: inherit;
59
+ margin: 0;
60
+ }
61
+ }
62
+
63
+ @mixin panel-header {
64
+ @include flex;
65
+ @include items-center;
66
+ @include justify-between;
67
+ padding: var(--padding-y) var(--padding-x);
68
+ @include border-b;
69
+ --color-bg: var(--bg-sunken);
70
+ background: var(--color-bg);
71
+ @include transition;
72
+
73
+ h3 {
74
+ font-size: var(--text-body-sm);
75
+ line-height: var(--lh-body-sm);
76
+ @include font-medium;
77
+ color: var(--color-fg);
78
+ margin: 0;
79
+ }
80
+
81
+ // Equalize control chrome inside the header. Geometric height lock
82
+ // matching @mixin app-header-actions: icon-content (1.25rem) + 2 ×
83
+ // rebound --padding-y + 2 × border-width. Buttons inherit --padding-y
84
+ // from this rebind; search-icon labels read it from
85
+ // @mixin form-input-icon-group (same var(--size-xs) value), so the
86
+ // formula resolves identically for both. Descendant selector — these
87
+ // controls are commonly wrapped in <aside>/<nav>/<div> inside the
88
+ // header. panel-header is chrome (no body labels), so descendant
89
+ // `label` here is always a search-icon-group, never a field label.
90
+ label {
91
+ --padding-y: var(--size-xs);
92
+ }
93
+
94
+ button {
95
+ --btn-padding-y: var(--size-xs);
96
+ }
97
+
98
+ :is(button, label) {
99
+ min-height: calc(1.25rem + 2 * var(--padding-y) + 2 * var(--border-width));
100
+ }
101
+
102
+ // Close/dismiss and toggle buttons inside the header should be tight and flat.
103
+ button[data-ln-modal-close],
104
+ button[aria-label="Close"],
105
+ button[data-ln-toggle] {
106
+ --btn-padding-y: var(--size-2xs);
107
+ --btn-padding-x: var(--size-2xs);
108
+ --shadow: none;
109
+ }
110
+ }
111
+
112
+ @mixin panel-body {
113
+ padding: var(--padding-y) var(--padding-x);
114
+
115
+ // Auto-flush: ONLY when the table (or .table-container) is the
116
+ // sole child of main — i.e. the card is a pure table card.
117
+ // Strip padding so the table spans edge-to-edge, and drop the
118
+ // table's own radius/shadow (card's overflow:hidden handles
119
+ // rounded corners).
120
+ //
121
+ // If main has mixed content (intro <p>, demo-split, <h4>,
122
+ // then <table>), :only-child doesn't match, padding stays,
123
+ // and the table keeps its own inset.
124
+ &:has(> table:only-child),
125
+ &:has(> .table-container:only-child) {
126
+ padding: 0;
127
+
128
+ > table,
129
+ > .table-container > table {
130
+ border-radius: 0;
131
+ box-shadow: none;
132
+ margin-bottom: 0;
133
+ }
134
+ }
135
+ }
136
+
137
+ @mixin panel-footer {
138
+ padding: var(--padding-y) var(--padding-x);
139
+ @include border-t;
140
+ @include flex;
141
+ @include justify-end;
142
+ gap: var(--gap);
143
+ }
144
+
145
+ @mixin section {
146
+ --margin-block: var(--size-xl);
147
+ margin-bottom: var(--margin-block);
148
+
149
+ header {
150
+ @include flex;
151
+ @include items-center;
152
+ @include justify-between;
153
+ --margin-block: var(--size-md);
154
+ margin-bottom: var(--margin-block);
155
+ --padding-y: var(--size-sm);
156
+ padding-bottom: var(--padding-y);
157
+ @include border-b;
158
+
159
+ h2 {
160
+ @include text-xl;
161
+ @include font-bold;
162
+ color: var(--color-fg);
163
+ margin: 0;
164
+ }
165
+
166
+ .section-actions {
167
+ @include flex;
168
+ gap: var(--gap);
169
+ }
170
+ }
171
+
172
+ main {
173
+ padding: 0;
174
+ }
175
+ }
176
+
177
+ // ─── Card accents (Tabler-inspired colored lines) ──────
178
+ // Accent reads --color-accent (solid) for borders, --color-primary
179
+ // (raw triplet) for alpha hover tints — see plan §0.3.
180
+ // Usage: #my-card { @include card; @include card-accent-top; }
181
+
182
+ @mixin _card-accent-base {
183
+ &:hover {
184
+ background-color: color-mix(in srgb, var(--color-accent) 4%, transparent);
185
+ }
186
+ }
187
+
188
+ @mixin card-accent-top {
189
+ @include _card-accent-base;
190
+ border-top: 3px solid var(--color-accent);
191
+ &:hover {
192
+ border-top-color: var(--color-accent);
193
+ }
194
+ }
195
+
196
+ @mixin card-accent-bottom {
197
+ @include _card-accent-base;
198
+ border-bottom: 3px solid var(--color-accent);
199
+ &:hover {
200
+ border-bottom-color: var(--color-accent);
201
+ }
202
+ }
203
+
204
+ @mixin card-accent-left {
205
+ @include _card-accent-base;
206
+ border-left: 3px solid var(--color-accent);
207
+ &:hover {
208
+ border-left-color: var(--color-accent);
209
+ }
210
+ }
211
+
212
+ // ─── Card tinted background ────────────────────────────
213
+
214
+ @mixin card-bg {
215
+ background-color: color-mix(in srgb, var(--color-accent) 6%, transparent);
216
+ border-color: color-mix(in srgb, var(--color-accent) 15%, transparent);
217
+ }
218
+
219
+ // ─── Card stacked (visual depth illusion) ──────────────
220
+
221
+ @mixin card-stacked {
222
+ @include relative;
223
+
224
+ &::after {
225
+ content: '';
226
+ @include absolute;
227
+ bottom: -5px;
228
+ left: 6px;
229
+ right: 6px;
230
+ height: 10px;
231
+ background: var(--color-bg);
232
+ border-radius: var(--radius);
233
+ --shadow: var(--shadow-resting);
234
+ box-shadow: var(--shadow);
235
+ --color-border: var(--border-subtle);
236
+ border: var(--border-width) solid var(--color-border);
237
+ z-index: -1;
238
+ }
239
+ }
240
+
241
+ @mixin section-card {
242
+ background: var(--color-bg);
243
+ @include border;
244
+ border-radius: var(--radius);
245
+ // `overflow: clip` not `overflow: hidden` — clips at rounded corners
246
+ // without establishing a scroll container, so sticky descendants
247
+ // (e.g. ln-table's <thead>) bind to the outer .app-main > section
248
+ // scroll surface instead of being trapped inside this card.
249
+ overflow: clip;
250
+ @include transition;
251
+ --shadow: var(--shadow-resting);
252
+ box-shadow: var(--shadow);
253
+ --margin-block: var(--size-md);
254
+ margin-bottom: var(--margin-block);
255
+
256
+ // Direct-child selectors only — nested <header>/<main>/<footer>
257
+ // elements inside the card's content are consumer-owned and
258
+ // must not inherit the card's own chrome styles.
259
+ > header { @include panel-header; }
260
+ > main { @include panel-body; }
261
+ > footer { @include panel-footer; }
262
+ }
263
+
264
+ // ─── Floating panel ────────────────────────────────────
265
+ // Chrome recipe for floating overlays (popover, dropdown menu, toast).
266
+ // Rebinds --color-bg to elevated surface and --shadow to floating
267
+ // shadow so floating panels lift off the page surface correctly.
268
+
269
+ @mixin floating-panel {
270
+ --color-bg: var(--bg-elevated);
271
+ --color-border: var(--border-subtle);
272
+ --shadow: var(--shadow-floating);
273
+ background: var(--color-bg);
274
+ border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
275
+ border-block-end: var(--border-block-end, var(--border-width) solid var(--color-border));
276
+ border-inline-start: var(--border-inline-start, var(--border-width) solid var(--color-border));
277
+ border-inline-end: var(--border-inline-end, var(--border-width) solid var(--color-border));
278
+ border-radius: var(--radius);
279
+ box-shadow: var(--shadow);
280
+ z-index: var(--z-dropdown);
281
+ }
282
+
283
+ // ─── Card field list (label/value rows) ────────────────
284
+ // Dashboard/settings pattern: card body contains .field rows
285
+ // with .label + .value. Separator between rows, none after last.
286
+ // Apply on a scope that contains .field descendants — typically
287
+ // the `.card` global binding, but any container works.
288
+ //
289
+ // Expected HTML:
290
+ // <div class="card">
291
+ // <main>
292
+ // <div class="field"><span class="label">Name</span><span class="value">Marko</span></div>
293
+ // <div class="field"><span class="label">Email</span><span class="value">m@x</span></div>
294
+ // </main>
295
+ // </div>
296
+
297
+ @mixin card-field-list {
298
+ .field {
299
+ @include flex;
300
+ align-items: baseline;
301
+ @include justify-between;
302
+ gap: var(--gap);
303
+ --padding-y: var(--size-xs-up);
304
+ padding-block: var(--padding-y);
305
+ @include border-b;
306
+
307
+ &:last-child {
308
+ @include border-none;
309
+ padding-bottom: 0;
310
+ }
311
+
312
+ .label {
313
+ @include text-xs;
314
+ @include font-semibold;
315
+ @include uppercase;
316
+ @include tracking-wider;
317
+ --color-fg: var(--fg-muted);
318
+ color: var(--color-fg);
319
+ @include flex-shrink-0;
320
+ }
321
+
322
+ .value {
323
+ @include text-sm;
324
+ @include font-medium;
325
+ color: var(--color-fg);
326
+ text-align: right;
327
+ margin: 0;
328
+
329
+ em {
330
+ --color-fg: var(--fg-subtle);
331
+ color: var(--color-fg);
332
+ @include text-xs;
333
+ font-style: normal;
334
+ font-weight: normal;
335
+ }
336
+ }
337
+ }
338
+ }
@@ -0,0 +1,66 @@
1
+ @use 'spacing' as *;
2
+ @use 'display' as *;
3
+ @use 'typography' as *;
4
+ @use 'colors' as *;
5
+ @use 'borders' as *;
6
+ @use 'focus' as *;
7
+
8
+ @mixin chip {
9
+ @include inline-flex;
10
+ @include items-center;
11
+ --gap: var(--size-xs);
12
+ gap: var(--gap);
13
+ --padding-y: var(--size-xs);
14
+ --padding-x: var(--size-sm-up);
15
+ padding: var(--padding-y) var(--padding-x);
16
+ @include typography(label-sm);
17
+ --color-bg: var(--bg-recessed);
18
+ background: var(--color-bg);
19
+ color: var(--color-fg);
20
+ @include rounded-full;
21
+ white-space: nowrap;
22
+ line-height: 1.4;
23
+
24
+ > button {
25
+ all: unset;
26
+ cursor: pointer;
27
+ @include inline-flex-center;
28
+ width: 1rem;
29
+ height: 1rem;
30
+ border-radius: 50%;
31
+ --color-fg: var(--fg-muted);
32
+ color: var(--color-fg);
33
+
34
+ &:hover {
35
+ background: var(--color-border);
36
+ color: var(--color-fg);
37
+ }
38
+
39
+ &:focus-visible {
40
+ @include focus-ring;
41
+ }
42
+
43
+ svg.ln-icon {
44
+ width: 0.75rem;
45
+ height: 0.75rem;
46
+ }
47
+ }
48
+
49
+ // Tone variants
50
+ &.success {
51
+ background: hsl(var(--color-success) / 0.12);
52
+ color: hsl(var(--color-success));
53
+ }
54
+ &.warning {
55
+ background: hsl(var(--color-warning) / 0.12);
56
+ color: hsl(var(--color-warning));
57
+ }
58
+ &.error {
59
+ background: hsl(var(--color-error) / 0.12);
60
+ color: hsl(var(--color-error));
61
+ }
62
+ &.info {
63
+ background: hsl(var(--color-info) / 0.12);
64
+ color: hsl(var(--color-info));
65
+ }
66
+ }