@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,216 @@
1
+ // ==========================================================================
2
+ // Theme — ln-ashlar
3
+ // ==========================================================================
4
+ // Dark-mode overrides. Inverts the neutral scale in place, re-tunes
5
+ // shadows and primary tint layers.
6
+ //
7
+ // Activation (any of the three):
8
+ // <html data-theme="dark"> ← explicit force
9
+ // @media (prefers-color-scheme: dark) ← system preference
10
+ // :root:not([data-theme="light"]) ← opt-out of auto
11
+ //
12
+ // See docs/css/theming.md for the full guide.
13
+ // ==========================================================================
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Shared dark overrides — used by both activation paths.
17
+ // ---------------------------------------------------------------------------
18
+ @mixin ln-dark-tokens {
19
+ // Neutral scale — inverted
20
+ --color-neutral-50: 220 20% 8%;
21
+ --color-neutral-100: 220 16% 12%;
22
+ --color-neutral-150: 220 14% 18%;
23
+ --color-neutral-200: 220 13% 24%;
24
+ --color-neutral-300: 220 13% 36%;
25
+ --color-neutral-400: 218 11% 52%;
26
+ --color-neutral-500: 220 9% 60%;
27
+ --color-neutral-600: 220 11% 72%;
28
+ --color-neutral-700: 220 14% 82%;
29
+ --color-neutral-800: 220 20% 90%;
30
+ --color-neutral-900: 221 30% 97%;
31
+
32
+ // Vocabulary — bg
33
+ // Explicit ladder: body (darkest) < base < elevated < sunken (mid).
34
+ // Dark UI convention keeps elevated = lighter; the neutral-scale
35
+ // inversion alone would flip elevation direction, so we restate
36
+ // the four vocabulary entries explicitly.
37
+ --bg-base: hsl(220 16% 13%);
38
+ --bg-elevated: hsl(220 16% 17%);
39
+ --bg-sunken: hsl(220 16% 20%);
40
+ --bg-recessed: hsl(220 16% 9%);
41
+
42
+ // Vocabulary — fg
43
+ --fg-default: hsl(0 0% 95%);
44
+ --fg-muted: hsl(220 9% 60%);
45
+ --fg-subtle: hsl(218 11% 52%);
46
+
47
+ // Vocabulary — border (collapses old --color-border-light bump)
48
+ --border-subtle: hsl(220 14% 20%);
49
+ --border-strong: hsl(220 13% 36%);
50
+ --border-strong-hover: hsl(218 11% 52%);
51
+
52
+ // Scrim — keep dark overlay in dark mode (neutral-900 inverts to
53
+ // near-white, which would produce a LIGHT scrim; explicit override
54
+ // preserves conventional modal-darkening behavior).
55
+ --color-scrim: hsl(0 0% 0% / 0.6);
56
+
57
+ // Primary tint layers — re-tuned for dark surfaces
58
+ --color-primary-light: 232 60% 22%;
59
+ --color-primary-lighter: 232 50% 15%;
60
+
61
+ // Status tint layers — re-tuned for dark surfaces (parallel to primary)
62
+ --color-success-light: 142 50% 18%;
63
+ --color-success-lighter: 142 40% 13%;
64
+ --color-warning-light: 32 55% 20%;
65
+ --color-warning-lighter: 32 45% 14%;
66
+ --color-info-light: 217 55% 22%;
67
+ --color-info-lighter: 217 45% 15%;
68
+ --color-error-light: 0 50% 20%;
69
+ --color-error-lighter: 0 40% 14%;
70
+
71
+ // Secondary tint layers — re-tuned for dark surfaces
72
+ --color-secondary-light: 160 55% 20%;
73
+ --color-secondary-lighter: 160 45% 14%;
74
+
75
+ // Shadows — solid black, boosted alpha, no hue (cool-tint
76
+ // disappears on dark surfaces)
77
+ --shadow-xs:
78
+ 0 1px 2px 0 hsl(0 0% 0% / 0.32);
79
+ --shadow-sm:
80
+ 0 1px 3px 0 hsl(0 0% 0% / 0.40),
81
+ 0 1px 2px -1px hsl(0 0% 0% / 0.30);
82
+ --shadow-md:
83
+ 0 4px 12px -2px hsl(0 0% 0% / 0.48),
84
+ 0 2px 4px -2px hsl(0 0% 0% / 0.32);
85
+ --shadow-lg:
86
+ 0 12px 24px -6px hsl(0 0% 0% / 0.56),
87
+ 0 8px 12px -4px hsl(0 0% 0% / 0.40);
88
+ --shadow-xl:
89
+ 0 24px 48px -12px hsl(0 0% 0% / 0.64),
90
+ 0 12px 24px -6px hsl(0 0% 0% / 0.48);
91
+ --shadow-2xl:
92
+ 0 36px 72px -18px hsl(0 0% 0% / 0.72),
93
+ 0 18px 36px -9px hsl(0 0% 0% / 0.56);
94
+ --shadow-inner:
95
+ inset 0 2px 4px 0 hsl(0 0% 0% / 0.32);
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Explicit attribute activation
100
+ // ---------------------------------------------------------------------------
101
+ [data-theme="dark"] {
102
+ @include ln-dark-tokens;
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // System preference activation (opt-out via data-theme="light")
107
+ // ---------------------------------------------------------------------------
108
+ @media (prefers-color-scheme: dark) {
109
+ :root:not([data-theme="light"]) {
110
+ @include ln-dark-tokens;
111
+ }
112
+ }
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Named presets — demo theme picker. Activation:
116
+ // <html data-theme="ocean"> <html data-theme="sunset"> etc.
117
+ // ---------------------------------------------------------------------------
118
+
119
+ [data-theme="ocean"] {
120
+ --color-primary: 190 80% 35%;
121
+ --color-primary-light: 190 60% 90%;
122
+ --color-primary-lighter: 190 50% 95%;
123
+ }
124
+
125
+ [data-theme="sunset"] {
126
+ --color-primary: 10 80% 50%;
127
+ --color-primary-light: 10 70% 92%;
128
+ --color-primary-lighter: 10 60% 96%;
129
+ }
130
+
131
+ [data-theme="midnight"] {
132
+ @include ln-dark-tokens;
133
+ --color-primary: 265 70% 60%;
134
+ --color-primary-light: 265 50% 22%;
135
+ --color-primary-lighter: 265 40% 15%;
136
+ }
137
+
138
+ [data-theme="glass"] {
139
+ @include ln-dark-tokens;
140
+
141
+ // Electric blue accent — luminescent on dark navy glass
142
+ --color-primary: 218 95% 62%;
143
+ --color-primary-light: 218 55% 24%;
144
+ --color-primary-lighter: 218 50% 16%;
145
+
146
+ // Flat — zero radius everywhere
147
+ --radius-sm: 0;
148
+ --radius-md: 0;
149
+ --radius-lg: 0;
150
+ --radius-xl: 0;
151
+ --radius-full: 0;
152
+
153
+ // Flat — no elevation shadows. --shadow-inner kept for recessed
154
+ // surfaces (code blocks, progress tracks) which read it as a depth
155
+ // affordance rather than elevation.
156
+ --shadow-xs: none;
157
+ --shadow-sm: none;
158
+ --shadow-md: none;
159
+ --shadow-lg: none;
160
+ --shadow-xl: none;
161
+ --shadow-2xl: none;
162
+
163
+ // ── Buttons — neutral chrome via --btn-* rebind, accent variant via
164
+ // cross-cutting --color-accent-bg-* companion tokens. Theme rebinds
165
+ // at :root only; library owns structure. See CLAUDE.md
166
+ // ## Theme Architecture for the contract.
167
+ --btn-bg: var(--bg-elevated);
168
+ --btn-bg-hover: var(--bg-sunken);
169
+ --btn-border: var(--color-accent);
170
+ --btn-border-hover: var(--color-accent-hover);
171
+
172
+ // ── Accent buttons — translucent fill via consumer-scope rebind
173
+ // Rebind --btn-* on the button itself (not at theme :root) so
174
+ // var(--color-primary) inside the values resolves on the button's
175
+ // cascade — letting .success / .error / .warning / .info status
176
+ // overrides reach the fill and text color. Token-only rebinds; no
177
+ // background/color/border-color declarations, no &:hover blocks
178
+ // (button-base already handles state via --btn-*-hover).
179
+ button[type="submit"],
180
+ .btn {
181
+ --btn-bg: hsl(var(--color-primary) / 0.2);
182
+ --btn-fg: hsl(var(--color-primary));
183
+ --btn-bg-hover: hsl(var(--color-primary) / 0.3);
184
+ --btn-fg-hover: hsl(var(--color-primary));
185
+ }
186
+
187
+ // ── Nav surface — palette-only rebind ──────────────────────────
188
+ // @mixin nav reads --nav-link-border-color* via fallback (defaults:
189
+ // transparent). Glass rebinds them so hover/active draw an accent
190
+ // ring. --nav-list-bleed shifts the ul to the sidebar's vertical
191
+ // borders so dividers reach edge-to-edge; @mixin nav reads it with
192
+ // `0` fallback so default theme is unaffected.
193
+ --nav-link-border-color-hover: var(--color-accent);
194
+ --nav-link-border-color-active: var(--color-accent);
195
+ --nav-list-bleed: calc(-1 * var(--padding-x));
196
+
197
+ // Active-nav wash — Glass shifts --color-accent-tint one step
198
+ // darker (var(--color-primary-light) instead of -lighter) for
199
+ // stronger contrast on the deep dark surface. Replaces the
200
+ // pre-refactor descendant override on a.active. Side-effect:
201
+ // dropdown active items + upload drop-zone also pick up the
202
+ // slightly darker tint — improvement, not regression.
203
+ --color-accent-tint: hsl(var(--color-primary-light));
204
+
205
+ // ── Dropdown / menu-items dividers ─────────────────────────────
206
+ // @mixin menu-items reads SOFT --border-block-start on `li + li`
207
+ // and `hr` with `none` fallback. Glass rebinds the slot in scope
208
+ // so the divider appears for both. Scoped (not theme :root)
209
+ // because --border-block-start is read by ~10 other mixins
210
+ // (cards, tables, accordions) where the divider should NOT appear.
211
+ [data-ln-dropdown-menu],
212
+ .menu-items {
213
+ --border-block-start: var(--border-width) solid var(--color-border);
214
+ }
215
+
216
+ }
@@ -0,0 +1,419 @@
1
+ @use 'mixins/breakpoints' as *;
2
+
3
+ // ==========================================================================
4
+ // Design Tokens — ln-ashlar
5
+ // ==========================================================================
6
+ // Single source of truth for all design values.
7
+ // All components reference these CSS variables.
8
+ // For theming: override at :root or scope to an element.
9
+ //
10
+ // Colors are stored as bare HSL triplets to enable alpha transparency:
11
+ // hsl(var(--color-primary)) → solid color
12
+ // hsl(var(--color-primary) / 0.5) → 50% transparent
13
+ //
14
+ // Theming example:
15
+ // .dark-theme { --color-bg: ...; }
16
+ // ==========================================================================
17
+
18
+ :root {
19
+
20
+ // -----------------------------------------------------------------------
21
+ // Colors — Primary
22
+ // -----------------------------------------------------------------------
23
+ --color-primary: 216 95% 42%;
24
+ --color-primary-light: 216 95% 93%;
25
+ --color-primary-lighter: 216 95% 97%;
26
+
27
+ // -----------------------------------------------------------------------
28
+ // Colors — Secondary (brand accent — surface/element color, NOT text)
29
+ // For subdued text color, see --fg-muted in the logical token section.
30
+ // -----------------------------------------------------------------------
31
+ --color-secondary: 160 76% 40%;
32
+ --color-secondary-light: 160 76% 93%;
33
+ --color-secondary-lighter: 160 76% 97%;
34
+
35
+ // -----------------------------------------------------------------------
36
+ // Colors — Status
37
+ // -----------------------------------------------------------------------
38
+ --color-success: 142 76% 36%;
39
+ --color-success-light: 142 76% 93%;
40
+ --color-success-lighter: 142 76% 97%;
41
+
42
+ --color-error: 0 84% 50%;
43
+ --color-error-light: 0 84% 93%;
44
+ --color-error-lighter: 0 84% 97%;
45
+
46
+ --color-warning: 32 95% 44%;
47
+ --color-warning-light: 32 95% 93%;
48
+ --color-warning-lighter: 32 95% 97%;
49
+
50
+ --color-info: 217 91% 60%;
51
+ --color-info-light: 217 91% 93%;
52
+ --color-info-lighter: 217 91% 97%;
53
+
54
+ // -----------------------------------------------------------------------
55
+ // Colors — Neutral scale (canonical greys)
56
+ // -----------------------------------------------------------------------
57
+ // Mixins may reach into the neutral scale directly when no semantic
58
+ // token matches the intent. Every such reference must be verified
59
+ // during dark-mode audits -- semantic tokens inherit dark adjustments
60
+ // automatically, direct-neutral references do not.
61
+ //
62
+ --color-neutral-50: 215 20% 98%;
63
+ --color-neutral-100: 215 16% 96%;
64
+ --color-neutral-150: 215 14% 93%;
65
+ --color-neutral-200: 215 13% 91%;
66
+ --color-neutral-300: 215 13% 83%;
67
+ --color-neutral-400: 215 11% 65%;
68
+ --color-neutral-500: 215 9% 46%;
69
+ --color-neutral-600: 215 11% 34%;
70
+ --color-neutral-700: 215 14% 24%;
71
+ --color-neutral-800: 215 20% 15%;
72
+ --color-neutral-900: 215 39% 11%;
73
+
74
+ // -----------------------------------------------------------------------
75
+ // Colors — Backgrounds
76
+ // -----------------------------------------------------------------------
77
+ --color-white: 0 0% 100%;
78
+
79
+ // -----------------------------------------------------------------------
80
+ // Size scale — canonical spacing primitives
81
+ // -----------------------------------------------------------------------
82
+ // Public API surface — full scale exposed. All padding, margin, gap,
83
+ // inset, and positional-offset spacing references one of these steps.
84
+ // Intermediate steps use t-shirt + `-up` suffix.
85
+ //
86
+ // Mixin bodies read --size-* only when a logical token (--padding-x,
87
+ // --padding-y, --gap) is not semantically appropriate. Otherwise they
88
+ // read the logical token, which is re-bound under density / theme /
89
+ // region scopes (see Logical tokens block below).
90
+ // -----------------------------------------------------------------------
91
+ --size-0: 0;
92
+ --size-2xs: 0.125rem; // 2px
93
+ --size-xs: 0.25rem; // 4px
94
+ --size-xs-up: 0.375rem; // 6px
95
+ --size-sm: 0.5rem; // 8px
96
+ --size-sm-up: 0.75rem; // 12px
97
+ --size-md: 1rem; // 16px
98
+ --size-md-up: 1.25rem; // 20px
99
+ --size-lg: 1.5rem; // 24px
100
+ --size-xl: 2rem; // 32px
101
+ --size-2xl: 3rem; // 48px
102
+ --size-3xl: 4rem; // 64px
103
+
104
+ // -----------------------------------------------------------------------
105
+ // Border Width
106
+ // -----------------------------------------------------------------------
107
+ // Public API surface — full scale exposed; internal usage may skip levels.
108
+ --border-width: 1px;
109
+ --border-width-strong: 2px;
110
+
111
+ // -----------------------------------------------------------------------
112
+ // Content widths
113
+ // -----------------------------------------------------------------------
114
+ // Public API surface — full scale exposed; internal usage may skip levels.
115
+ --max-w-prose: 65ch;
116
+ --max-w-form: 32rem;
117
+ --max-w-content: 48rem;
118
+ --max-w-container: 80rem;
119
+
120
+ // -----------------------------------------------------------------------
121
+ // Border Radius
122
+ // -----------------------------------------------------------------------
123
+ --radius-none: 0;
124
+ --radius-sm: 0.25rem; // 4px
125
+ --radius-md: 0.5rem; // 8px
126
+ --radius-lg: 0.75rem; // 12px
127
+ --radius-xl: 1rem; // 16px
128
+ --radius-full: 9999px;
129
+
130
+ // -----------------------------------------------------------------------
131
+ // Shadows — cool-tinted, dual-layer (v1.1)
132
+ // -----------------------------------------------------------------------
133
+ // Public API surface — full scale exposed; internal usage may skip levels.
134
+ --shadow-none: none;
135
+
136
+ --shadow-xs:
137
+ 0 1px 2px 0 hsl(220 40% 15% / 0.04);
138
+
139
+ --shadow-sm:
140
+ 0 1px 3px 0 hsl(220 40% 15% / 0.08),
141
+ 0 1px 2px -1px hsl(220 40% 15% / 0.04);
142
+
143
+ --shadow-md:
144
+ 0 4px 12px -2px hsl(220 40% 15% / 0.10),
145
+ 0 2px 4px -2px hsl(220 40% 15% / 0.06);
146
+
147
+ --shadow-lg:
148
+ 0 12px 24px -6px hsl(220 40% 15% / 0.12),
149
+ 0 8px 12px -4px hsl(220 40% 15% / 0.08);
150
+
151
+ --shadow-xl:
152
+ 0 24px 48px -12px hsl(220 40% 15% / 0.16),
153
+ 0 12px 24px -6px hsl(220 40% 15% / 0.10);
154
+
155
+ --shadow-2xl:
156
+ 0 36px 72px -18px hsl(220 40% 15% / 0.20),
157
+ 0 18px 36px -9px hsl(220 40% 15% / 0.12);
158
+
159
+ --shadow-inner:
160
+ inset 0 2px 4px 0 hsl(220 40% 15% / 0.06);
161
+
162
+ // Color-aware shadows (for focus halos and coloured CTAs)
163
+ --shadow-primary: 0 8px 24px -6px hsl(var(--color-primary) / 0.28);
164
+ --shadow-success: 0 8px 24px -6px hsl(var(--color-success) / 0.28);
165
+ --shadow-error: 0 8px 24px -6px hsl(var(--color-error) / 0.28);
166
+
167
+ // -----------------------------------------------------------------------
168
+ // Transitions — durations
169
+ // -----------------------------------------------------------------------
170
+ --transition-base: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
171
+ --transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1);
172
+ --transition-slow: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
173
+
174
+ // -----------------------------------------------------------------------
175
+ // Easings — curves only (no duration)
176
+ // -----------------------------------------------------------------------
177
+ // Public API surface — full scale exposed; internal usage may skip levels.
178
+ --easing-standard: cubic-bezier(0.4, 0, 0.2, 1);
179
+ --easing-decelerate: cubic-bezier(0, 0, 0.2, 1);
180
+ --easing-accelerate: cubic-bezier(0.4, 0, 1, 1);
181
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
182
+
183
+ // -----------------------------------------------------------------------
184
+ // Typography
185
+ // -----------------------------------------------------------------------
186
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
187
+ --font-mono: ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace;
188
+
189
+ --text-xs: 0.75rem; // 12px
190
+ --text-sm: 0.875rem; // 14px
191
+ --text-base: 1rem; // 16px
192
+ --text-lg: 1.125rem; // 18px
193
+ --text-xl: 1.25rem; // 20px
194
+ --text-2xl: 1.5rem; // 24px
195
+
196
+ // -----------------------------------------------------------------------
197
+ // Typography — semantic role tokens (v1.1)
198
+ // -----------------------------------------------------------------------
199
+ // Public API surface — full scale exposed; internal usage may skip levels.
200
+ --text-display-lg: 3.75rem; --lh-display-lg: 1.1;
201
+ --text-display-md: 3rem; --lh-display-md: 1.1;
202
+ --text-display-sm: 2.25rem; --lh-display-sm: 1.15;
203
+
204
+ --text-heading-lg: 1.875rem; --lh-heading-lg: 1.2;
205
+ --text-heading-md: 1.5rem; --lh-heading-md: 1.25;
206
+ --text-heading-sm: 1.25rem; --lh-heading-sm: 1.3;
207
+
208
+ --text-title-md: 1.125rem; --lh-title-md: 1.4;
209
+ --text-title-sm: 1rem; --lh-title-sm: 1.4;
210
+
211
+ --text-body-lg: 1.125rem; --lh-body-lg: 1.6;
212
+ --text-body-md: 1rem; --lh-body-md: 1.6;
213
+ --text-body-sm: 0.875rem; --lh-body-sm: 1.5;
214
+
215
+ --text-label-md: 0.875rem; --lh-label-md: 1.4;
216
+ --text-label-sm: 0.75rem; --lh-label-sm: 1.4;
217
+
218
+ --text-caption: 0.75rem; --lh-caption: 1.4;
219
+
220
+ // -----------------------------------------------------------------------
221
+ // Letter spacing
222
+ // -----------------------------------------------------------------------
223
+ --tracking-tight: -0.025em;
224
+ --tracking-normal: 0;
225
+ --tracking-wide: 0.025em;
226
+
227
+ --font-normal: 400;
228
+ --font-medium: 500;
229
+ --font-semibold: 600;
230
+ --font-bold: 700;
231
+
232
+ // -----------------------------------------------------------------------
233
+ // Z-Index Scale
234
+ // -----------------------------------------------------------------------
235
+ --z-sticky: 10;
236
+ --z-dropdown: 20;
237
+ --z-overlay: 30;
238
+ --z-modal: 40;
239
+ --z-toast: 50;
240
+
241
+ // -----------------------------------------------------------------------
242
+ // App shell — intrinsic dimensions (not spacing rhythm)
243
+ // -----------------------------------------------------------------------
244
+ --app-header-height: 3.5rem;
245
+ --app-sidebar-width: 16rem;
246
+ --app-scrim-bg: hsl(var(--color-neutral-900) / 0.4);
247
+
248
+ // -----------------------------------------------------------------------
249
+ // Logical tokens — the public contract mixins read
250
+ // -----------------------------------------------------------------------
251
+ // Rule: mixin bodies reference ONLY these tokens (and --text-*/--lh-*/
252
+ // --radius-*/--shadow-*/--transition-*/--easing-* role scales). Back-end
253
+ // scales (--size-*, --color-neutral-*) are re-bound here and in context
254
+ // scopes (density, theme, region overrides) — never read directly
255
+ // inside a component mixin.
256
+ //
257
+ // See .claude/plans/refactor-logical-tokens-architecture.md for the
258
+ // full contract.
259
+ // -----------------------------------------------------------------------
260
+
261
+ // -- Structure & rhythm -----------------------------------------------
262
+ --padding-x: var(--size-sm-up);
263
+ --padding-y: var(--size-sm);
264
+ --gap: var(--size-sm);
265
+ --radius: var(--radius-md);
266
+
267
+ // -----------------------------------------------------------------------
268
+ // Value vocabulary — what bg/fg/border/shadow values exist
269
+ // -----------------------------------------------------------------------
270
+ // Mixins read primitives (--color-bg, --color-fg, --color-border,
271
+ // --shadow). Vocabulary provides the value choices. Components rebind
272
+ // the primitive on their own scope to pick a different vocabulary
273
+ // value. Themes override vocabulary at theme :root.
274
+ // -----------------------------------------------------------------------
275
+
276
+ --bg-base: hsl(var(--color-white));
277
+ --bg-elevated: var(--bg-base);
278
+ --bg-sunken: hsl(var(--color-neutral-100));
279
+ --bg-recessed: hsl(var(--color-neutral-50));
280
+
281
+ --fg-default: hsl(var(--color-neutral-900));
282
+ --fg-muted: hsl(var(--color-neutral-500));
283
+ --fg-subtle: hsl(var(--color-neutral-400));
284
+
285
+ --border-subtle: hsl(var(--color-neutral-200));
286
+ --border-strong: hsl(var(--color-neutral-300));
287
+ --border-strong-hover: hsl(var(--color-neutral-400));
288
+
289
+ --shadow-resting: var(--shadow-sm);
290
+ --shadow-floating: var(--shadow-md);
291
+ --shadow-overlay: var(--shadow-xl);
292
+
293
+ // -- Surface colors — primitives -------------------------------------
294
+ // Mixins read these four primitives. Vocabulary provides the value
295
+ // choices (--bg-*, --fg-*, --border-*, --shadow-*). Components rebind
296
+ // a primitive on their own scope to pick a different vocabulary entry.
297
+ --color-bg: var(--bg-base);
298
+ --color-fg: var(--fg-default);
299
+ --color-border: var(--border-subtle);
300
+ --shadow: var(--shadow-resting);
301
+ --color-scrim: hsl(var(--color-neutral-900) / 0.5);
302
+
303
+ // -- Accent -----------------------------------------------------------
304
+ --color-accent: hsl(var(--color-primary));
305
+ --color-accent-fg: hsl(var(--color-white));
306
+ --color-accent-tint: hsl(var(--color-primary-lighter));
307
+ --color-accent-tint-strong: hsl(var(--color-primary-light));
308
+
309
+ // Translucent accent surface — companions for themes that opt in to a
310
+ // non-solid accent button. NO defaults — `@mixin btn` reads these via
311
+ // fallback to `--color-accent` / `--color-accent-fg`, so the default
312
+ // theme stays solid + white.
313
+ //
314
+ // IMPORTANT — declare at theme :root ONLY when the value is a literal
315
+ // (no nested var(--color-primary)). If a theme needs the translucent
316
+ // fill computed FROM --color-primary, the rebind must happen at
317
+ // consumer scope (e.g. `[data-theme="..."] button[type="submit"], .btn
318
+ // { --btn-bg: ...; }`) so the nested var() resolves at the button —
319
+ // otherwise --color-primary freezes at theme :root and the
320
+ // .success/.error/.warning/.info status cascade breaks. Glass uses
321
+ // the consumer-scope pattern; see scss/config/_theme.scss.
322
+ // --color-accent-bg
323
+ // --color-accent-bg-hover
324
+ // --color-accent-bg-fg
325
+
326
+ // Nav surface — companions for themes that opt in to a bordered/inset
327
+ // nav appearance (e.g. Glass). NO defaults — `@mixin nav` reads these
328
+ // via fallback to no-op values (transparent border, zero bleed), so
329
+ // the default theme renders unchanged. Themes rebind at theme :root.
330
+ // --nav-link-border-color
331
+ // --nav-link-border-color-hover
332
+ // --nav-link-border-color-active
333
+ // --nav-list-bleed
334
+
335
+ // -- Button surface (neutral default) --------------------------------
336
+ // Read by @mixin button-base across idle + hover + active. The
337
+ // neutral default wires here. Accent variants (submit + @mixin btn)
338
+ // REBIND --btn-* on the consumer element (not at :root) — this
339
+ // preserves the .success/.error/.warning/.info semantic-color
340
+ // cascade because var() resolves at the declaration site. A
341
+ // companion --btn-accent-* surface AT :ROOT would freeze
342
+ // --color-accent at :root and break that cascade; the fix is
343
+ // consumer-scoped rebinding, not a parallel :root surface. See
344
+ // scss/config/mixins/_btn.scss header for the full rationale.
345
+ --btn-bg: var(--color-bg);
346
+ --btn-fg: var(--color-fg);
347
+ --btn-border: var(--border-strong);
348
+ --btn-bg-hover: var(--bg-sunken);
349
+ --btn-fg-hover: var(--color-fg);
350
+ --btn-border-hover: var(--border-strong-hover);
351
+
352
+ // Button padding tokens
353
+ --btn-padding-x: var(--size-md);
354
+ --btn-padding-y: var(--size-sm);
355
+
356
+ // -- Typography ------------------------------------------------------
357
+ --font-size: var(--text-body-md);
358
+ --line-height: var(--lh-body-md);
359
+
360
+ // -- Motion & depth --------------------------------------------------
361
+ --transition: var(--transition-base);
362
+
363
+ // -- Per-side borders (SOFT — no default) ----------------------------
364
+ // --border-block-start, --border-block-end,
365
+ // --border-inline-start, --border-inline-end
366
+ //
367
+ // Read by structural mixins (card, section-card, floating-panel,
368
+ // stat-card, app-header, app-footer, accordion items, table cells,
369
+ // tab-nav, sidebar edges, page-header, etc.) with a per-mixin
370
+ // fallback. NOT defined here — that is intentional. A scope that
371
+ // re-binds e.g. `--border-block-start: none` flattens the top edge
372
+ // of the matched element so siblings can join with a shared rule.
373
+ //
374
+ // Joining example:
375
+ // .joined-stack > * + * { --border-block-start: none; }
376
+
377
+ // -- Margin (SOFT — no default) --------------------------------
378
+ // --margin-block, --margin-inline
379
+ //
380
+ // Read by structural rhythm rules (label-to-input gap, section
381
+ // margin, header→body separation, sibling stacking,
382
+ // element-pushing horizontal offsets) with a per-mixin rebind.
383
+ // NOT defined here — the value varies too widely across
384
+ // consumers for a single :root default to be useful. Each
385
+ // consumer rebinds locally:
386
+ //
387
+ // @mixin section {
388
+ // --margin-block: var(--size-xl);
389
+ // margin-bottom: var(--margin-block);
390
+ // }
391
+ //
392
+ // Density-compact reacts automatically: density-compact rebinds
393
+ // the underlying --size-* scale, and the consumer's
394
+ // `--margin-block: var(--size-md)` re-resolves at the consumer
395
+ // element under the compact scope.
396
+ //
397
+ // --margin-inline is the horizontal companion. Used for
398
+ // element-pushing structural offsets (icon→text gap inside a
399
+ // row, sibling element offsets). NOT for flex/grid sibling
400
+ // spacing — that is --gap.
401
+ }
402
+
403
+ // ---------------------------------------------------------------------------
404
+ // Mobile rhythm — logical tokens tighten at ≤ md viewport.
405
+ // Not the same as `.density-compact` (that's a user opt-in).
406
+ // This is viewport-driven structural compression: content dominates chrome
407
+ // at narrow widths. Components reading --padding-x/--padding-y/--gap
408
+ // react via the cascade.
409
+ //
410
+ // Interaction with .density-compact: stacks. Compact user + mobile viewport =
411
+ // both apply; the underlying --size-* scale is already tighter in
412
+ // .density-compact so logical tokens resolve to the tightest value.
413
+ // ---------------------------------------------------------------------------
414
+ @include mq-down(md) {
415
+ :root {
416
+ --padding-y: var(--size-xs); // 12px → 4px
417
+ --gap: var(--size-xs-up); // 8px → 6px
418
+ }
419
+ }
@@ -0,0 +1,52 @@
1
+ // Accordion — styled list with chevron rotation on open
2
+ //
3
+ // Usage:
4
+ // [data-ln-accordion] { @include accordion; } // library default
5
+ // #my-list { @include accordion; } // custom selector
6
+ //
7
+ // Expected DOM (see docs/css/mixins.md §accordion):
8
+ // <ul data-ln-accordion>
9
+ // <li>
10
+ // <header data-ln-toggle-for="p1">Title</header>
11
+ // <main id="p1" data-ln-toggle class="collapsible">
12
+ // <div class="collapsible-body"><p>...</p></div>
13
+ // </main>
14
+ // </li>
15
+ // </ul>
16
+
17
+ @use 'spacing' as *;
18
+ @use 'display' as *;
19
+ @use 'borders' as *;
20
+ @use 'interaction' as *;
21
+
22
+ @mixin accordion {
23
+ @include border;
24
+ --radius: var(--radius-lg);
25
+ border-radius: var(--radius);
26
+ overflow: hidden;
27
+
28
+ > li {
29
+ border-block-end: var(--border-block-end, var(--border-width) solid var(--color-border));
30
+
31
+ &:last-child { border-block-end: none; }
32
+
33
+ // Trigger
34
+ > [data-ln-toggle-for] {
35
+ @include flex;
36
+ @include justify-between;
37
+ @include items-center;
38
+ --padding-y: var(--size-md);
39
+ --padding-x: var(--size-md);
40
+ padding: var(--padding-y) var(--padding-x);
41
+ @include cursor-pointer;
42
+ }
43
+
44
+ // Content body padding — on children, not the overflow:hidden wrapper
45
+ .collapsible-body > * {
46
+ --padding-y: var(--size-sm-up);
47
+ --padding-x: var(--size-md);
48
+ padding: var(--padding-y) var(--padding-x);
49
+ margin: 0;
50
+ }
51
+ }
52
+ }