@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.
- package/README.md +177 -0
- package/js/COMPONENTS.md +1102 -0
- package/js/index.js +41 -0
- package/js/ln-accordion/README.md +137 -0
- package/js/ln-accordion/ln-accordion.js +1 -0
- package/js/ln-accordion/src/ln-accordion.js +41 -0
- package/js/ln-ajax/README.md +91 -0
- package/js/ln-ajax/ln-ajax.js +1 -0
- package/js/ln-ajax/src/ln-ajax.js +277 -0
- package/js/ln-api-connector/README.md +150 -0
- package/js/ln-api-connector/ln-api-connector.js +1 -0
- package/js/ln-api-connector/src/ln-api-connector.js +265 -0
- package/js/ln-autoresize/README.md +80 -0
- package/js/ln-autoresize/ln-autoresize.js +1 -0
- package/js/ln-autoresize/src/ln-autoresize.js +47 -0
- package/js/ln-autosave/README.md +92 -0
- package/js/ln-autosave/ln-autosave.js +1 -0
- package/js/ln-autosave/src/ln-autosave.js +147 -0
- package/js/ln-circular-progress/README.md +161 -0
- package/js/ln-circular-progress/ln-circular-progress.js +1 -0
- package/js/ln-circular-progress/src/ln-circular-progress.js +133 -0
- package/js/ln-confirm/README.md +86 -0
- package/js/ln-confirm/_ln-confirm.scss +13 -0
- package/js/ln-confirm/ln-confirm.js +1 -0
- package/js/ln-confirm/src/ln-confirm.js +131 -0
- package/js/ln-core/crypto.js +83 -0
- package/js/ln-core/helpers.js +411 -0
- package/js/ln-core/index.js +5 -0
- package/js/ln-core/persist.js +71 -0
- package/js/ln-core/positioning.js +207 -0
- package/js/ln-core/reactive.js +74 -0
- package/js/ln-couchdb-connector/README.md +156 -0
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
- package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +348 -0
- package/js/ln-data-coordinator/README.md +165 -0
- package/js/ln-data-coordinator/ln-data-coordinator.js +1 -0
- package/js/ln-data-coordinator/src/ln-data-coordinator.js +249 -0
- package/js/ln-data-store/README.md +94 -0
- package/js/ln-data-store/ln-data-store.js +1 -0
- package/js/ln-data-store/src/ln-data-store.js +699 -0
- package/js/ln-data-table/README.md +110 -0
- package/js/ln-data-table/ln-data-table.js +1 -0
- package/js/ln-data-table/ln-data-table.scss +10 -0
- package/js/ln-data-table/src/ln-data-table.js +1103 -0
- package/js/ln-date/README.md +151 -0
- package/js/ln-date/ln-date.js +1 -0
- package/js/ln-date/src/ln-date.js +442 -0
- package/js/ln-dropdown/README.md +117 -0
- package/js/ln-dropdown/ln-dropdown.js +1 -0
- package/js/ln-dropdown/ln-dropdown.scss +15 -0
- package/js/ln-dropdown/src/ln-dropdown.js +174 -0
- package/js/ln-external-links/README.md +341 -0
- package/js/ln-external-links/ln-external-links.js +1 -0
- package/js/ln-external-links/src/ln-external-links.js +116 -0
- package/js/ln-filter/README.md +99 -0
- package/js/ln-filter/ln-filter.js +1 -0
- package/js/ln-filter/ln-filter.scss +7 -0
- package/js/ln-filter/src/ln-filter.js +404 -0
- package/js/ln-form/README.md +101 -0
- package/js/ln-form/ln-form.js +1 -0
- package/js/ln-form/src/ln-form.js +199 -0
- package/js/ln-http/README.md +89 -0
- package/js/ln-http/ln-http.js +1 -0
- package/js/ln-http/src/ln-http.js +219 -0
- package/js/ln-icons/README.md +88 -0
- package/js/ln-icons/ln-icons.js +1 -0
- package/js/ln-icons/src/ln-icons.js +169 -0
- package/js/ln-link/README.md +303 -0
- package/js/ln-link/ln-link.js +1 -0
- package/js/ln-link/src/ln-link.js +196 -0
- package/js/ln-modal/README.md +154 -0
- package/js/ln-modal/ln-modal.js +1 -0
- package/js/ln-modal/ln-modal.scss +11 -0
- package/js/ln-modal/src/ln-modal.js +201 -0
- package/js/ln-nav/README.md +70 -0
- package/js/ln-nav/ln-nav.js +1 -0
- package/js/ln-nav/src/ln-nav.js +177 -0
- package/js/ln-number/README.md +122 -0
- package/js/ln-number/ln-number.js +1 -0
- package/js/ln-number/src/ln-number.js +302 -0
- package/js/ln-popover/README.md +127 -0
- package/js/ln-popover/ln-popover.js +1 -0
- package/js/ln-popover/src/ln-popover.js +288 -0
- package/js/ln-progress/README.md +442 -0
- package/js/ln-progress/ln-progress.js +1 -0
- package/js/ln-progress/src/ln-progress.js +150 -0
- package/js/ln-search/README.md +83 -0
- package/js/ln-search/ln-search.js +1 -0
- package/js/ln-search/ln-search.scss +7 -0
- package/js/ln-search/src/ln-search.js +114 -0
- package/js/ln-sortable/README.md +95 -0
- package/js/ln-sortable/ln-sortable.js +1 -0
- package/js/ln-sortable/src/ln-sortable.js +203 -0
- package/js/ln-table/README.md +101 -0
- package/js/ln-table/ln-table-sort.js +1 -0
- package/js/ln-table/ln-table.js +1 -0
- package/js/ln-table/ln-table.scss +11 -0
- package/js/ln-table/src/ln-table-sort.js +168 -0
- package/js/ln-table/src/ln-table.js +473 -0
- package/js/ln-tabs/README.md +137 -0
- package/js/ln-tabs/ln-tabs.js +1 -0
- package/js/ln-tabs/src/ln-tabs.js +171 -0
- package/js/ln-time/README.md +81 -0
- package/js/ln-time/ln-time.js +1 -0
- package/js/ln-time/src/ln-time.js +192 -0
- package/js/ln-toast/README.md +122 -0
- package/js/ln-toast/ln-toast.js +15 -0
- package/js/ln-toast/src/ln-toast.js +210 -0
- package/js/ln-toast/template.html +14 -0
- package/js/ln-toggle/README.md +137 -0
- package/js/ln-toggle/ln-toggle.js +1 -0
- package/js/ln-toggle/src/ln-toggle.js +139 -0
- package/js/ln-tooltip/README.md +58 -0
- package/js/ln-tooltip/ln-tooltip.js +1 -0
- package/js/ln-tooltip/ln-tooltip.scss +9 -0
- package/js/ln-tooltip/src/ln-tooltip.js +169 -0
- package/js/ln-translations/README.md +96 -0
- package/js/ln-translations/ln-translations.js +1 -0
- package/js/ln-translations/src/ln-translations.js +275 -0
- package/js/ln-upload/README.md +180 -0
- package/js/ln-upload/ln-upload.js +1 -0
- package/js/ln-upload/ln-upload.scss +20 -0
- package/js/ln-upload/src/ln-upload.js +407 -0
- package/js/ln-validate/README.md +108 -0
- package/js/ln-validate/ln-validate.js +1 -0
- package/js/ln-validate/src/ln-validate.js +160 -0
- package/package.json +55 -0
- package/scss/base/_global.scss +83 -0
- package/scss/base/_reset.scss +17 -0
- package/scss/base/_typography.scss +125 -0
- package/scss/components/_accordion.scss +34 -0
- package/scss/components/_ajax.scss +15 -0
- package/scss/components/_alert.scss +5 -0
- package/scss/components/_app-shell.scss +15 -0
- package/scss/components/_avatar.scss +6 -0
- package/scss/components/_breadcrumbs.scss +33 -0
- package/scss/components/_button.scss +20 -0
- package/scss/components/_card.scss +10 -0
- package/scss/components/_chip.scss +5 -0
- package/scss/components/_circular-progress.scss +29 -0
- package/scss/components/_confirm.scss +5 -0
- package/scss/components/_data-table.scss +83 -0
- package/scss/components/_dropdown.scss +25 -0
- package/scss/components/_empty-state.scss +22 -0
- package/scss/components/_form.scss +100 -0
- package/scss/components/_layout.scss +8 -0
- package/scss/components/_link.scss +11 -0
- package/scss/components/_ln-table.scss +60 -0
- package/scss/components/_loader.scss +6 -0
- package/scss/components/_modal.scss +20 -0
- package/scss/components/_nav.scss +9 -0
- package/scss/components/_page-header.scss +10 -0
- package/scss/components/_popover.scss +10 -0
- package/scss/components/_progress.scss +17 -0
- package/scss/components/_prose.scss +5 -0
- package/scss/components/_scrollbar.scss +32 -0
- package/scss/components/_sections.scss +12 -0
- package/scss/components/_sidebar.scss +5 -0
- package/scss/components/_stat-card.scss +5 -0
- package/scss/components/_status-badge.scss +4 -0
- package/scss/components/_stepper.scss +5 -0
- package/scss/components/_table.scss +19 -0
- package/scss/components/_tabs.scss +21 -0
- package/scss/components/_timeline.scss +14 -0
- package/scss/components/_toast.scss +41 -0
- package/scss/components/_toggle.scss +81 -0
- package/scss/components/_tooltip.scss +18 -0
- package/scss/components/_translations.scss +111 -0
- package/scss/components/_upload.scss +51 -0
- package/scss/config/_breakpoints.scss +72 -0
- package/scss/config/_density.scss +117 -0
- package/scss/config/_icons.scss +37 -0
- package/scss/config/_mixins.scss +13 -0
- package/scss/config/_theme.scss +216 -0
- package/scss/config/_tokens.scss +419 -0
- package/scss/config/mixins/_accordion.scss +52 -0
- package/scss/config/mixins/_ajax.scss +39 -0
- package/scss/config/mixins/_alert.scss +82 -0
- package/scss/config/mixins/_app-shell.scss +312 -0
- package/scss/config/mixins/_avatar.scss +109 -0
- package/scss/config/mixins/_borders.scss +36 -0
- package/scss/config/mixins/_breadcrumbs.scss +72 -0
- package/scss/config/mixins/_breakpoints.scss +62 -0
- package/scss/config/mixins/_btn.scss +179 -0
- package/scss/config/mixins/_card.scss +338 -0
- package/scss/config/mixins/_chip.scss +66 -0
- package/scss/config/mixins/_circular-progress.scss +71 -0
- package/scss/config/mixins/_collapsible.scss +24 -0
- package/scss/config/mixins/_colors.scss +46 -0
- package/scss/config/mixins/_confirm.scss +31 -0
- package/scss/config/mixins/_data-table.scss +346 -0
- package/scss/config/mixins/_display.scss +32 -0
- package/scss/config/mixins/_dropdown.scss +143 -0
- package/scss/config/mixins/_empty-state.scss +30 -0
- package/scss/config/mixins/_focus.scss +55 -0
- package/scss/config/mixins/_footer.scss +42 -0
- package/scss/config/mixins/_form.scss +601 -0
- package/scss/config/mixins/_index.scss +58 -0
- package/scss/config/mixins/_interaction.scss +15 -0
- package/scss/config/mixins/_kbd.scss +22 -0
- package/scss/config/mixins/_layout.scss +117 -0
- package/scss/config/mixins/_link.scss +55 -0
- package/scss/config/mixins/_ln-table.scss +420 -0
- package/scss/config/mixins/_loader.scss +26 -0
- package/scss/config/mixins/_modal.scss +66 -0
- package/scss/config/mixins/_motion.scss +19 -0
- package/scss/config/mixins/_nav.scss +273 -0
- package/scss/config/mixins/_page-header.scss +69 -0
- package/scss/config/mixins/_popover.scss +25 -0
- package/scss/config/mixins/_position.scss +32 -0
- package/scss/config/mixins/_progress.scss +56 -0
- package/scss/config/mixins/_prose.scss +127 -0
- package/scss/config/mixins/_shadows.scss +8 -0
- package/scss/config/mixins/_sidebar.scss +95 -0
- package/scss/config/mixins/_sizing.scss +6 -0
- package/scss/config/mixins/_spacing.scss +19 -0
- package/scss/config/mixins/_stat-card.scss +68 -0
- package/scss/config/mixins/_status-badge.scss +83 -0
- package/scss/config/mixins/_stepper.scss +78 -0
- package/scss/config/mixins/_table.scss +215 -0
- package/scss/config/mixins/_tabs.scss +64 -0
- package/scss/config/mixins/_timeline.scss +69 -0
- package/scss/config/mixins/_toast.scss +148 -0
- package/scss/config/mixins/_tooltip.scss +111 -0
- package/scss/config/mixins/_transitions.scss +10 -0
- package/scss/config/mixins/_translations.scss +124 -0
- package/scss/config/mixins/_typography.scss +57 -0
- package/scss/config/mixins/_upload.scss +168 -0
- package/scss/ln-ashlar.scss +62 -0
- package/scss/tabler-icons.txt +5039 -0
- package/scss/utilities/_animations.scss +83 -0
- package/scss/utilities/_utilities.scss +49 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
@use 'spacing' as *;
|
|
2
|
+
@use 'display' as *;
|
|
3
|
+
@use 'breakpoints' as *;
|
|
4
|
+
@use 'sizing' as *;
|
|
5
|
+
@use 'typography' as *;
|
|
6
|
+
@use 'colors' as *;
|
|
7
|
+
@use 'borders' as *;
|
|
8
|
+
@use 'transitions' as *;
|
|
9
|
+
@use 'motion' as *;
|
|
10
|
+
@use 'layout' as *;
|
|
11
|
+
@use 'interaction' as *;
|
|
12
|
+
@use 'position' as *;
|
|
13
|
+
@use 'focus' as *;
|
|
14
|
+
@use 'btn' as *;
|
|
15
|
+
|
|
16
|
+
// ─── Form layout ────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
@mixin form-label {
|
|
19
|
+
@include block;
|
|
20
|
+
font-size: var(--text-label-md);
|
|
21
|
+
line-height: var(--lh-label-md);
|
|
22
|
+
@include font-medium;
|
|
23
|
+
color: var(--color-fg);
|
|
24
|
+
// Structural label-to-input gap (not shell rhythm).
|
|
25
|
+
--margin-block: var(--size-xs);
|
|
26
|
+
margin-bottom: var(--margin-block);
|
|
27
|
+
|
|
28
|
+
// Required indicator — red * after label text
|
|
29
|
+
// Triggered by sibling input[required] via parent :has()
|
|
30
|
+
.form-element:has([required]) > & {
|
|
31
|
+
&::after {
|
|
32
|
+
content: ' *';
|
|
33
|
+
color: hsl(var(--color-error));
|
|
34
|
+
@include font-bold;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@mixin form-grid {
|
|
40
|
+
--gap: var(--size-md);
|
|
41
|
+
container-type: inline-size;
|
|
42
|
+
container-name: form-grid;
|
|
43
|
+
display: grid;
|
|
44
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
45
|
+
gap: var(--gap);
|
|
46
|
+
|
|
47
|
+
> * { margin: 0; }
|
|
48
|
+
|
|
49
|
+
@include cq-down(md, form-grid) {
|
|
50
|
+
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@mixin form-actions {
|
|
55
|
+
--gap: var(--size-sm-up);
|
|
56
|
+
@include flex;
|
|
57
|
+
@include justify-end;
|
|
58
|
+
gap: var(--gap);
|
|
59
|
+
// Structural margin-top (not shell rhythm).
|
|
60
|
+
--margin-block: var(--size-lg);
|
|
61
|
+
margin-top: var(--margin-block);
|
|
62
|
+
// Structural padding-top (border-t offset). Kept literal to avoid
|
|
63
|
+
// cascading --padding-y into nested anchors, which use button-standard
|
|
64
|
+
// --padding-y from :root.
|
|
65
|
+
--padding-y: var(--size-md);
|
|
66
|
+
padding-top: var(--padding-y);
|
|
67
|
+
@include border-t;
|
|
68
|
+
|
|
69
|
+
a {
|
|
70
|
+
// Anchor-as-button: inherit neutral button chrome from the library,
|
|
71
|
+
// then reset the --gap rebind (form-actions parent sets a wider
|
|
72
|
+
// action-button gap; anchors want the tighter icon+text gap) and
|
|
73
|
+
// strip default anchor underline.
|
|
74
|
+
--gap: var(--size-sm);
|
|
75
|
+
@include button-base;
|
|
76
|
+
text-decoration: none;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── Focus treatment (shared by form-input and icon-group) ─────
|
|
81
|
+
|
|
82
|
+
@mixin _form-focus-style {
|
|
83
|
+
border-color: var(--color-accent);
|
|
84
|
+
@include focus-background-shift;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Text inputs, textareas, selects ────────────────────────────
|
|
88
|
+
|
|
89
|
+
@mixin form-input {
|
|
90
|
+
@include w-full;
|
|
91
|
+
--padding-y: var(--size-sm);
|
|
92
|
+
--padding-x: var(--size-md);
|
|
93
|
+
padding: var(--padding-y) var(--padding-x);
|
|
94
|
+
font-size: var(--font-size);
|
|
95
|
+
line-height: var(--line-height);
|
|
96
|
+
color: var(--color-fg);
|
|
97
|
+
background: var(--color-bg);
|
|
98
|
+
@include border;
|
|
99
|
+
border-radius: var(--radius);
|
|
100
|
+
@include transition;
|
|
101
|
+
outline: none;
|
|
102
|
+
|
|
103
|
+
&:focus-visible {
|
|
104
|
+
@include _form-focus-style;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
&::placeholder {
|
|
108
|
+
--color-fg: var(--fg-subtle);
|
|
109
|
+
color: var(--color-fg);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
&:disabled {
|
|
113
|
+
@include opacity-50;
|
|
114
|
+
@include cursor-not-allowed;
|
|
115
|
+
--color-bg: var(--bg-sunken);
|
|
116
|
+
background: var(--color-bg);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Hide native UA clear button on <input type="search"> — we never use it;
|
|
120
|
+
// it's visually inconsistent across browsers and fights our icon-group layout.
|
|
121
|
+
&[type="search"]::-webkit-search-cancel-button {
|
|
122
|
+
-webkit-appearance: none;
|
|
123
|
+
appearance: none;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@mixin form-textarea {
|
|
128
|
+
min-height: 6rem;
|
|
129
|
+
resize: vertical;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@mixin form-select {
|
|
133
|
+
appearance: none;
|
|
134
|
+
// Arrow icon — override --select-arrow to change per-theme
|
|
135
|
+
// (SVG data-URIs can't reference CSS custom properties, so the icon is static)
|
|
136
|
+
--select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%239ca3af' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
|
|
137
|
+
background-image: var(--select-arrow);
|
|
138
|
+
background-repeat: no-repeat;
|
|
139
|
+
// Intrinsic arrow offset (not shell padding rhythm).
|
|
140
|
+
background-position: right var(--size-sm-up) center;
|
|
141
|
+
padding-right: 2.5rem;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── Input group — label wrapping icon(s) + input ──────────────────
|
|
145
|
+
//
|
|
146
|
+
// Pattern:
|
|
147
|
+
// <label>
|
|
148
|
+
// <svg class="ln-icon"/> ← leading icon (optional)
|
|
149
|
+
// <input type="…">
|
|
150
|
+
// <svg class="ln-icon"/> ← trailing icon (optional)
|
|
151
|
+
// </label>
|
|
152
|
+
//
|
|
153
|
+
// The <label> becomes the visual container (border, padding, focus
|
|
154
|
+
// ring). The nested <input> strips its form-input container chrome so
|
|
155
|
+
// that horizontal rhythm is owned by the label's px + gap, and vertical
|
|
156
|
+
// rhythm by the label's py. Without this, the input keeps its own
|
|
157
|
+
// padding and fights the wrapper — see the asymmetric spacing that
|
|
158
|
+
// motivated this mixin.
|
|
159
|
+
//
|
|
160
|
+
// Applied via semantic selector in components/_form.scss — no class
|
|
161
|
+
// needed on the <label>.
|
|
162
|
+
|
|
163
|
+
@mixin form-input-icon-group {
|
|
164
|
+
@include inline-flex;
|
|
165
|
+
@include items-center;
|
|
166
|
+
gap: var(--gap);
|
|
167
|
+
// Compact padding: tight vertical to keep total height close to a
|
|
168
|
+
// regular form-input despite the icon's fixed size.
|
|
169
|
+
--padding-y: var(--size-xs);
|
|
170
|
+
--padding-x: var(--size-sm);
|
|
171
|
+
padding: var(--padding-y) var(--padding-x);
|
|
172
|
+
background: var(--color-bg);
|
|
173
|
+
@include border;
|
|
174
|
+
border-radius: var(--radius);
|
|
175
|
+
@include transition;
|
|
176
|
+
font-size: var(--font-size);
|
|
177
|
+
line-height: var(--line-height);
|
|
178
|
+
// Wrapping-label reset: undo form-label defaults that would otherwise leak.
|
|
179
|
+
margin-bottom: 0;
|
|
180
|
+
|
|
181
|
+
&:focus-within {
|
|
182
|
+
@include _form-focus-style;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
> .ln-icon {
|
|
186
|
+
@include flex-shrink-0;
|
|
187
|
+
--color-fg: var(--fg-subtle);
|
|
188
|
+
color: var(--color-fg);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
> input {
|
|
192
|
+
// Strip container chrome inherited from form-input — label owns it now.
|
|
193
|
+
padding: 0;
|
|
194
|
+
@include border-none;
|
|
195
|
+
background: transparent;
|
|
196
|
+
outline: none;
|
|
197
|
+
box-shadow: none;
|
|
198
|
+
|
|
199
|
+
// Tighten leading for a single-line control. form-input inherits
|
|
200
|
+
// --lh-body-md (1.6) which is tuned for prose readability, not form
|
|
201
|
+
// controls — that leaves ~7px of wasted leading above and below the
|
|
202
|
+
// glyph inside the input's own line box, which compounds with the
|
|
203
|
+
// label's padding and makes icon-group feel airy. 1.25 keeps
|
|
204
|
+
// descenders (g/p/j/y) safe and brings the label down to ~36px total.
|
|
205
|
+
line-height: 1.25;
|
|
206
|
+
|
|
207
|
+
// Take the remaining flex space. width: auto cancels form-input's
|
|
208
|
+
// width: 100%; flex-1 + min-width: 0 lets the field shrink below
|
|
209
|
+
// its intrinsic min-content inside a narrow label.
|
|
210
|
+
width: auto;
|
|
211
|
+
@include flex-1;
|
|
212
|
+
min-width: 0;
|
|
213
|
+
|
|
214
|
+
&:focus-visible {
|
|
215
|
+
border-color: transparent;
|
|
216
|
+
box-shadow: none;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Hide clear button when input is empty
|
|
220
|
+
&:placeholder-shown ~ [data-ln-search-clear] {
|
|
221
|
+
@include hidden;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
> [data-ln-search-clear] {
|
|
226
|
+
@include flex-shrink-0;
|
|
227
|
+
--padding-y: var(--size-2xs);
|
|
228
|
+
--padding-x: var(--size-2xs);
|
|
229
|
+
padding: var(--padding-y) var(--padding-x);
|
|
230
|
+
@include cursor-pointer;
|
|
231
|
+
--color-fg: var(--fg-subtle);
|
|
232
|
+
color: var(--color-fg);
|
|
233
|
+
@include transition-colors;
|
|
234
|
+
|
|
235
|
+
&:hover {
|
|
236
|
+
color: var(--color-fg);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ─── Checkbox & Radio ───────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
@mixin form-check {
|
|
244
|
+
appearance: none;
|
|
245
|
+
@include size(1.25rem);
|
|
246
|
+
padding: 0;
|
|
247
|
+
@include flex-shrink-0;
|
|
248
|
+
// Non-standard 1.5px stroke — optical weight at small checkbox size.
|
|
249
|
+
border: 1.5px solid var(--color-border);
|
|
250
|
+
background: var(--color-bg);
|
|
251
|
+
@include cursor-pointer;
|
|
252
|
+
@include transition;
|
|
253
|
+
vertical-align: middle;
|
|
254
|
+
|
|
255
|
+
&:checked {
|
|
256
|
+
background-color: var(--color-accent);
|
|
257
|
+
border-color: var(--color-accent);
|
|
258
|
+
// Inline SVG: native <input type="checkbox"> cannot contain child elements,
|
|
259
|
+
// so a data URI is the only way to embed a checkmark here.
|
|
260
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4L19 7'/%3E%3C/svg%3E");
|
|
261
|
+
background-size: 0.75rem;
|
|
262
|
+
background-position: center;
|
|
263
|
+
background-repeat: no-repeat;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
&:focus-visible {
|
|
267
|
+
@include focus-ring;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
&:disabled {
|
|
271
|
+
@include opacity-50;
|
|
272
|
+
@include cursor-not-allowed;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@mixin form-checkbox {
|
|
277
|
+
@include form-check;
|
|
278
|
+
@include rounded-sm;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@mixin form-radio {
|
|
282
|
+
@include form-check;
|
|
283
|
+
border-radius: 50%;
|
|
284
|
+
|
|
285
|
+
&:checked {
|
|
286
|
+
background-image: none;
|
|
287
|
+
box-shadow: inset 0 0 0 3px var(--color-bg);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ─── Pill outline — base label (structure + visible border) ────
|
|
292
|
+
//
|
|
293
|
+
// Self-contained outline variant: transparent background, visible native
|
|
294
|
+
// input indicator, accent border + accent text on checked.
|
|
295
|
+
//
|
|
296
|
+
// Must be idempotent when re-applied over `@include pill` (the filled
|
|
297
|
+
// default installed globally by components/_form.scss on fieldset pills).
|
|
298
|
+
// Each rule below resets a behavior that `pill` adds: bg-sunken fill,
|
|
299
|
+
// hidden input, accent-fg text on checked.
|
|
300
|
+
|
|
301
|
+
@mixin pill-outline {
|
|
302
|
+
@include flex;
|
|
303
|
+
@include items-center;
|
|
304
|
+
gap: var(--gap);
|
|
305
|
+
--padding-y: var(--size-sm);
|
|
306
|
+
--padding-x: var(--size-md);
|
|
307
|
+
padding: var(--padding-y) var(--padding-x);
|
|
308
|
+
@include cursor-pointer;
|
|
309
|
+
@include transition;
|
|
310
|
+
@include text-sm;
|
|
311
|
+
@include font-medium;
|
|
312
|
+
margin-bottom: 0;
|
|
313
|
+
border-radius: var(--radius);
|
|
314
|
+
border: var(--border-width) solid var(--color-border);
|
|
315
|
+
// Reset pill's filled bg. Outline = transparent surface.
|
|
316
|
+
background: transparent;
|
|
317
|
+
position: relative;
|
|
318
|
+
|
|
319
|
+
// Restore the native input indicator. `display: revert` returns the
|
|
320
|
+
// UA default (inline-block for checkbox/radio) and cleanly undoes
|
|
321
|
+
// pill's `display: none` when pill-outline is applied as an override.
|
|
322
|
+
> input { display: revert; }
|
|
323
|
+
|
|
324
|
+
&:has(> input:checked) {
|
|
325
|
+
border-color: var(--color-accent);
|
|
326
|
+
color: var(--color-accent);
|
|
327
|
+
// Reset pill's accent fill on checked — outline stays transparent.
|
|
328
|
+
background: transparent;
|
|
329
|
+
z-index: 1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
&:hover:not(:has(> input:checked)) {
|
|
333
|
+
border-color: var(--color-accent);
|
|
334
|
+
z-index: 1;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ─── Pill filled — extends pill-outline, hides input ───────────
|
|
339
|
+
|
|
340
|
+
@mixin pill {
|
|
341
|
+
@include pill-outline;
|
|
342
|
+
--color-bg: var(--bg-sunken);
|
|
343
|
+
// Derive hover from --color-accent at this scope (label element).
|
|
344
|
+
// Lets .error/.success/.warning/.info parents cascade to the checked-
|
|
345
|
+
// state hover via the .success { --color-primary } chain. See
|
|
346
|
+
// _btn.scss for the syntax rationale (number form, not percentage).
|
|
347
|
+
--color-accent-hover: hsl(from var(--color-accent) h s calc(l - 8));
|
|
348
|
+
|
|
349
|
+
background-color: var(--color-bg);
|
|
350
|
+
border-color: transparent;
|
|
351
|
+
|
|
352
|
+
> input { display: none; }
|
|
353
|
+
|
|
354
|
+
&:has(> input:checked) {
|
|
355
|
+
background-color: var(--color-accent);
|
|
356
|
+
color: var(--color-accent-fg);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
&:hover:not(:has(> input:checked)) {
|
|
360
|
+
border-color: var(--color-border);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
&:hover:has(> input:checked) {
|
|
364
|
+
background-color: var(--color-accent-hover);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
@mixin pill-group {
|
|
369
|
+
@include inline-flex;
|
|
370
|
+
|
|
371
|
+
li label {
|
|
372
|
+
border-radius: 0;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
li:first-child label {
|
|
376
|
+
border-radius: var(--radius) 0 0 var(--radius);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
li:last-child label {
|
|
380
|
+
border-radius: 0 var(--radius) var(--radius) 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
li:only-child label {
|
|
384
|
+
border-radius: var(--radius);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ─── Pills — GCD for joined-pill checkbox/radio lists ──────────
|
|
389
|
+
// Joined-group filled pills: pill on each <li><label><input>, with
|
|
390
|
+
// joined-corner border-radius (square middles, partial-radius first/
|
|
391
|
+
// last) inlined here rather than composed via pill-group. Inlining
|
|
392
|
+
// is required because pill-group's `li label` selectors (0,1,5) are
|
|
393
|
+
// out-specificityed by pill's :has selector (0,2,5), which would
|
|
394
|
+
// reassert full radius on every pill. The selectors below match
|
|
395
|
+
// pill's :has shape so source-order/specificity resolves cleanly.
|
|
396
|
+
// Apply to a <ul> whose direct children are <li><label><input> pairs.
|
|
397
|
+
//
|
|
398
|
+
// Usage: project SCSS applies this on its own ul selector:
|
|
399
|
+
//
|
|
400
|
+
// #my-filter-pills { @include pills; }
|
|
401
|
+
//
|
|
402
|
+
// where the structure is:
|
|
403
|
+
//
|
|
404
|
+
// <ul id="my-filter-pills">
|
|
405
|
+
// <li><label><input type="checkbox"> Label</label></li>
|
|
406
|
+
// ...
|
|
407
|
+
// </ul>
|
|
408
|
+
|
|
409
|
+
@mixin pills {
|
|
410
|
+
@include inline-flex;
|
|
411
|
+
|
|
412
|
+
> li > label:has(> input[type="checkbox"]),
|
|
413
|
+
> li > label:has(> input[type="radio"]) {
|
|
414
|
+
@include pill;
|
|
415
|
+
border-radius: 0;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
> li:first-child > label:has(> input[type="checkbox"]),
|
|
419
|
+
> li:first-child > label:has(> input[type="radio"]) {
|
|
420
|
+
border-radius: var(--radius) 0 0 var(--radius);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
> li:last-child > label:has(> input[type="checkbox"]),
|
|
424
|
+
> li:last-child > label:has(> input[type="radio"]) {
|
|
425
|
+
border-radius: 0 var(--radius) var(--radius) 0;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
> li:only-child > label:has(> input[type="checkbox"]),
|
|
429
|
+
> li:only-child > label:has(> input[type="radio"]) {
|
|
430
|
+
border-radius: var(--radius);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@mixin pills-outline {
|
|
435
|
+
@include inline-flex;
|
|
436
|
+
|
|
437
|
+
> li {
|
|
438
|
+
position: relative;
|
|
439
|
+
|
|
440
|
+
&:not(:first-child) {
|
|
441
|
+
margin-left: calc(var(--border-width) * -1);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
&:hover,
|
|
445
|
+
&:has(> label > input:checked) {
|
|
446
|
+
z-index: 1;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
> li > label:has(> input[type="checkbox"]),
|
|
451
|
+
> li > label:has(> input[type="radio"]) {
|
|
452
|
+
@include pill-outline;
|
|
453
|
+
border-radius: 0;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
> li:first-child > label:has(> input[type="checkbox"]),
|
|
457
|
+
> li:first-child > label:has(> input[type="radio"]) {
|
|
458
|
+
border-radius: var(--radius) 0 0 var(--radius);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
> li:last-child > label:has(> input[type="checkbox"]),
|
|
462
|
+
> li:last-child > label:has(> input[type="radio"]) {
|
|
463
|
+
border-radius: 0 var(--radius) var(--radius) 0;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
> li:only-child > label:has(> input[type="checkbox"]),
|
|
467
|
+
> li:only-child > label:has(> input[type="radio"]) {
|
|
468
|
+
border-radius: var(--radius);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
@mixin toggle-switch {
|
|
473
|
+
appearance: none;
|
|
474
|
+
position: relative;
|
|
475
|
+
width: 2.5rem;
|
|
476
|
+
height: 1.5rem;
|
|
477
|
+
--color-bg: var(--border-strong);
|
|
478
|
+
background: var(--color-bg);
|
|
479
|
+
border-radius: var(--radius-full);
|
|
480
|
+
cursor: pointer;
|
|
481
|
+
vertical-align: middle;
|
|
482
|
+
flex-shrink: 0;
|
|
483
|
+
border: none;
|
|
484
|
+
transition: none;
|
|
485
|
+
|
|
486
|
+
@include motion-safe {
|
|
487
|
+
transition: background-color var(--transition-fast);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
&::before {
|
|
491
|
+
content: '';
|
|
492
|
+
position: absolute;
|
|
493
|
+
top: 2px;
|
|
494
|
+
left: 2px;
|
|
495
|
+
width: 1.25rem;
|
|
496
|
+
height: 1.25rem;
|
|
497
|
+
background: hsl(var(--color-white));
|
|
498
|
+
border-radius: 50%;
|
|
499
|
+
--shadow: var(--shadow-resting);
|
|
500
|
+
box-shadow: var(--shadow);
|
|
501
|
+
|
|
502
|
+
@include motion-safe {
|
|
503
|
+
transition: transform var(--transition-fast);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
&:checked {
|
|
508
|
+
background: var(--color-accent);
|
|
509
|
+
|
|
510
|
+
&::before {
|
|
511
|
+
transform: translateX(1rem);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
&:focus-visible {
|
|
516
|
+
@include focus-ring;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
&:disabled {
|
|
520
|
+
opacity: 0.5;
|
|
521
|
+
cursor: not-allowed;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
@mixin pill-switch {
|
|
526
|
+
@include row(var(--size-sm));
|
|
527
|
+
@include cursor-pointer;
|
|
528
|
+
@include text-sm;
|
|
529
|
+
@include font-medium;
|
|
530
|
+
color: var(--color-fg);
|
|
531
|
+
margin-bottom: 0;
|
|
532
|
+
|
|
533
|
+
> input[type="checkbox"] {
|
|
534
|
+
@include toggle-switch;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
@mixin pills-switch {
|
|
539
|
+
@include stack(var(--size-sm));
|
|
540
|
+
list-style: none;
|
|
541
|
+
padding: 0;
|
|
542
|
+
margin: 0;
|
|
543
|
+
|
|
544
|
+
> li > label {
|
|
545
|
+
@include pill-switch;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// ─── Check list outline — vertical list with outlined labels ───
|
|
550
|
+
// <ul><li><label><input> — same structure as pill-group but vertical.
|
|
551
|
+
// List reset + pill-outline on labels (visible input, bordered).
|
|
552
|
+
//
|
|
553
|
+
// Usage:
|
|
554
|
+
// #dept-filter { @include check-list-outline; }
|
|
555
|
+
|
|
556
|
+
@mixin check-list-outline {
|
|
557
|
+
li label { @include pill-outline; }
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// ─── Check list filled — vertical list with filled labels ──────
|
|
561
|
+
// Extends check-list-outline: hides input, fills bg on checked.
|
|
562
|
+
//
|
|
563
|
+
// Usage:
|
|
564
|
+
// #dept-filter { @include check-list; }
|
|
565
|
+
|
|
566
|
+
@mixin check-list {
|
|
567
|
+
@include check-list-outline;
|
|
568
|
+
|
|
569
|
+
li label { @include pill; }
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// ─── Validation states ─────────────────────────────────────────
|
|
573
|
+
// Status colors (error/success) stay raw — no logical --color-status
|
|
574
|
+
// token in scope (see parent plan §6.2).
|
|
575
|
+
|
|
576
|
+
@mixin form-validate-invalid {
|
|
577
|
+
border-color: hsl(var(--color-error));
|
|
578
|
+
|
|
579
|
+
&:focus-visible {
|
|
580
|
+
border-color: hsl(var(--color-error));
|
|
581
|
+
box-shadow: 0 0 0 3px hsl(var(--color-error) / 0.15);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
@mixin form-validate-valid {
|
|
586
|
+
border-color: hsl(var(--color-success));
|
|
587
|
+
|
|
588
|
+
&:focus-visible {
|
|
589
|
+
border-color: hsl(var(--color-success));
|
|
590
|
+
box-shadow: 0 0 0 3px hsl(var(--color-success) / 0.15);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
@mixin form-validate-errors {
|
|
595
|
+
// Structural error-list margin (not shell rhythm).
|
|
596
|
+
--margin-block: var(--size-xs);
|
|
597
|
+
margin-top: var(--margin-block);
|
|
598
|
+
font-size: var(--text-caption);
|
|
599
|
+
line-height: var(--lh-caption);
|
|
600
|
+
color: hsl(var(--color-error));
|
|
601
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Mixins index — @forward all in correct order
|
|
2
|
+
// Primitives first, composites after (they depend on primitives)
|
|
3
|
+
|
|
4
|
+
// Primitives
|
|
5
|
+
@forward 'spacing';
|
|
6
|
+
@forward 'breakpoints';
|
|
7
|
+
@forward 'display';
|
|
8
|
+
@forward 'sizing';
|
|
9
|
+
@forward 'typography';
|
|
10
|
+
@forward 'colors';
|
|
11
|
+
@forward 'borders';
|
|
12
|
+
@forward 'shadows';
|
|
13
|
+
@forward 'transitions';
|
|
14
|
+
@forward 'motion';
|
|
15
|
+
@forward 'position';
|
|
16
|
+
@forward 'interaction';
|
|
17
|
+
@forward 'focus';
|
|
18
|
+
|
|
19
|
+
// Composites (depend on primitives)
|
|
20
|
+
@forward 'layout';
|
|
21
|
+
@forward 'collapsible';
|
|
22
|
+
@forward 'accordion';
|
|
23
|
+
@forward 'card';
|
|
24
|
+
@forward 'chip';
|
|
25
|
+
@forward 'nav';
|
|
26
|
+
@forward 'btn';
|
|
27
|
+
@forward 'form';
|
|
28
|
+
@forward 'modal';
|
|
29
|
+
@forward 'breadcrumbs';
|
|
30
|
+
@forward 'loader';
|
|
31
|
+
@forward 'tabs';
|
|
32
|
+
@forward 'table';
|
|
33
|
+
@forward 'avatar';
|
|
34
|
+
@forward 'footer';
|
|
35
|
+
@forward 'sidebar';
|
|
36
|
+
@forward 'app-shell';
|
|
37
|
+
@forward 'status-badge';
|
|
38
|
+
@forward 'alert';
|
|
39
|
+
@forward 'empty-state';
|
|
40
|
+
@forward 'page-header';
|
|
41
|
+
@forward 'stepper';
|
|
42
|
+
@forward 'timeline';
|
|
43
|
+
@forward 'stat-card';
|
|
44
|
+
@forward 'prose';
|
|
45
|
+
@forward 'kbd';
|
|
46
|
+
@forward 'tooltip';
|
|
47
|
+
@forward 'popover';
|
|
48
|
+
@forward 'progress';
|
|
49
|
+
@forward 'link';
|
|
50
|
+
@forward 'confirm';
|
|
51
|
+
@forward 'circular-progress';
|
|
52
|
+
@forward 'dropdown';
|
|
53
|
+
@forward 'toast';
|
|
54
|
+
@forward 'upload';
|
|
55
|
+
@forward 'translations';
|
|
56
|
+
@forward 'data-table';
|
|
57
|
+
@forward 'ln-table';
|
|
58
|
+
@forward 'ajax';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Cursor, Interaction & Z-Index
|
|
2
|
+
|
|
3
|
+
@mixin cursor-pointer { cursor: pointer; }
|
|
4
|
+
@mixin cursor-not-allowed { cursor: not-allowed; }
|
|
5
|
+
@mixin select-none { user-select: none; }
|
|
6
|
+
@mixin opacity-50 { opacity: 0.5; }
|
|
7
|
+
|
|
8
|
+
// Z-Index — references --z-* tokens
|
|
9
|
+
@mixin z-dropdown { z-index: var(--z-dropdown); }
|
|
10
|
+
@mixin z-sticky { z-index: var(--z-sticky); }
|
|
11
|
+
@mixin z-overlay { z-index: var(--z-overlay); }
|
|
12
|
+
@mixin z-modal { z-index: var(--z-modal); }
|
|
13
|
+
@mixin z-toast { z-index: var(--z-toast); }
|
|
14
|
+
|
|
15
|
+
@mixin z($val) { z-index: $val; }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@use 'spacing' as *;
|
|
2
|
+
@use 'typography' as *;
|
|
3
|
+
@use 'colors' as *;
|
|
4
|
+
@use 'borders' as *;
|
|
5
|
+
|
|
6
|
+
@mixin kbd {
|
|
7
|
+
--color-bg: var(--bg-sunken);
|
|
8
|
+
display: inline-block;
|
|
9
|
+
--padding-y: var(--size-2xs);
|
|
10
|
+
--padding-x: var(--size-xs-up);
|
|
11
|
+
padding: var(--padding-y) var(--padding-x);
|
|
12
|
+
@include typography(caption);
|
|
13
|
+
font-family: var(--font-mono);
|
|
14
|
+
color: var(--color-fg);
|
|
15
|
+
background: var(--color-bg);
|
|
16
|
+
border: var(--border-width) solid var(--color-border);
|
|
17
|
+
border-bottom-width: 2px;
|
|
18
|
+
--radius: var(--radius-sm);
|
|
19
|
+
border-radius: var(--radius);
|
|
20
|
+
line-height: 1;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
}
|