@loidolt/theme-styles 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,201 @@
1
+ .ldt-field {
2
+ display: grid;
3
+ gap: 0.35rem;
4
+ min-width: 0;
5
+ }
6
+ .ldt-label,
7
+ .ldt-field__label,
8
+ .ldt-legend {
9
+ display: flex;
10
+ justify-content: space-between;
11
+ color: var(--loidolt-text-muted);
12
+ font: var(--loidolt-font-size-xs) var(--loidolt-font-utility);
13
+ letter-spacing: var(--loidolt-font-tracking-utility);
14
+ text-transform: uppercase;
15
+ }
16
+ .ldt-field__description {
17
+ margin: 0;
18
+ color: var(--loidolt-text-muted);
19
+ font: var(--loidolt-font-size-xs) / 1.5 var(--loidolt-font-utility);
20
+ }
21
+ .ldt-field__error {
22
+ margin: 0;
23
+ /* Status *as text on a surface* — not the fill role, which is tuned for `on-danger` ink. */
24
+ color: var(--loidolt-text-danger);
25
+ font: var(--loidolt-font-size-xs) / 1.5 var(--loidolt-font-utility);
26
+ }
27
+ .ldt-input,
28
+ .ldt-select,
29
+ .ldt-textarea,
30
+ .ldt-number-field__input {
31
+ width: 100%;
32
+ min-height: var(--loidolt-size-control);
33
+ border: 0;
34
+ border-bottom: var(--loidolt-border-width) solid var(--loidolt-border-control);
35
+ border-radius: 0;
36
+ padding: 0.45rem 0.15rem;
37
+ color: var(--loidolt-text);
38
+ background: transparent;
39
+ }
40
+ .ldt-input--boxed,
41
+ .ldt-select--boxed,
42
+ .ldt-textarea--boxed,
43
+ .ldt-number-field--boxed {
44
+ border: var(--loidolt-border-width) solid var(--loidolt-border-control);
45
+ padding-inline: 0.65rem;
46
+ background: var(--loidolt-surface-input);
47
+ }
48
+ /* The focus ring itself comes from the `loidolt.a11y` layer; this only tints the border. */
49
+ .ldt-input:focus-visible,
50
+ .ldt-select:focus-visible,
51
+ .ldt-textarea:focus-visible,
52
+ .ldt-number-field__input:focus-visible {
53
+ border-color: var(--loidolt-accent);
54
+ }
55
+ .ldt-input[aria-invalid='true'],
56
+ .ldt-select[aria-invalid='true'],
57
+ .ldt-textarea[aria-invalid='true'] {
58
+ border-color: var(--loidolt-danger);
59
+ }
60
+ .ldt-textarea {
61
+ min-height: 6rem;
62
+ resize: vertical;
63
+ }
64
+ .ldt-choice {
65
+ /* The label is the pointer target for the input it wraps, so it carries the 24px minimum. */
66
+ min-height: 1.5rem;
67
+ display: inline-flex;
68
+ align-items: center;
69
+ gap: 0.55rem;
70
+ color: var(--loidolt-text-muted);
71
+ font: var(--loidolt-font-size-sm) var(--loidolt-font-utility);
72
+ letter-spacing: 0.06em;
73
+ }
74
+ .ldt-choice input {
75
+ width: 1rem;
76
+ height: 1rem;
77
+ accent-color: var(--loidolt-accent);
78
+ }
79
+ .ldt-fieldset {
80
+ min-width: 0;
81
+ margin: 0;
82
+ padding: 0;
83
+ border: 0;
84
+ }
85
+ /* A legend is shrink-to-fit by default, which collapses the space-between that pushes the
86
+ "optional" marker to the far end. */
87
+ .ldt-legend {
88
+ width: 100%;
89
+ }
90
+ .ldt-fieldset__body {
91
+ display: grid;
92
+ gap: var(--ldt-gap, var(--loidolt-space-3));
93
+ margin-top: 0.35rem;
94
+ }
95
+ .ldt-fieldset > :where(.ldt-field__description, .ldt-field__error) {
96
+ margin-top: 0.35rem;
97
+ }
98
+ .ldt-radio-group {
99
+ display: flex;
100
+ flex-wrap: wrap;
101
+ gap: 0.75rem 1rem;
102
+ }
103
+ .ldt-radio-group--vertical {
104
+ flex-direction: column;
105
+ }
106
+ .ldt-switch {
107
+ min-height: 1.5rem;
108
+ display: inline-flex;
109
+ align-items: center;
110
+ gap: 0.65rem;
111
+ border: 0;
112
+ padding: 0;
113
+ background: transparent;
114
+ }
115
+ .ldt-switch__track {
116
+ width: 2.4rem;
117
+ height: 1.25rem;
118
+ padding: 2px;
119
+ border: var(--loidolt-border-width) solid var(--loidolt-border-control);
120
+ background: var(--loidolt-surface-alt);
121
+ }
122
+ .ldt-switch__thumb {
123
+ display: block;
124
+ width: 0.9rem;
125
+ height: 0.9rem;
126
+ background: var(--loidolt-text-muted);
127
+ transition: transform var(--loidolt-motion-fast) var(--loidolt-motion-easing);
128
+ }
129
+ .ldt-switch[aria-checked='true'] .ldt-switch__track {
130
+ border-color: var(--loidolt-accent);
131
+ background: var(--loidolt-accent);
132
+ }
133
+ .ldt-switch[aria-checked='true'] .ldt-switch__thumb {
134
+ background: var(--loidolt-on-accent);
135
+ transform: translateX(1.1rem);
136
+ }
137
+ .ldt-number-field {
138
+ display: grid;
139
+ grid-template-columns: 2rem minmax(0, 1fr) 2rem;
140
+ border-bottom: var(--loidolt-border-width) solid var(--loidolt-border-control);
141
+ }
142
+ .ldt-number-field .ldt-number-field__input {
143
+ border: 0;
144
+ text-align: center;
145
+ }
146
+ .ldt-number-field__button {
147
+ border: 0;
148
+ background: transparent;
149
+ }
150
+ .ldt-number-field__button:hover:not(:disabled, [aria-disabled='true']) {
151
+ color: var(--loidolt-text-accent);
152
+ background: var(--loidolt-surface-input);
153
+ }
154
+ .ldt-number-field__button[aria-disabled='true'] {
155
+ opacity: 0.4;
156
+ cursor: not-allowed;
157
+ }
158
+ /* A file picker in the system's input clothes. */
159
+ .ldt-file-input::file-selector-button {
160
+ margin-inline-end: var(--loidolt-space-2);
161
+ border: var(--loidolt-border-width) solid var(--loidolt-border-control);
162
+ padding: 0.25rem 0.65rem;
163
+ color: var(--loidolt-text);
164
+ background: var(--loidolt-surface);
165
+ font: var(--loidolt-font-size-xs) var(--loidolt-font-utility);
166
+ letter-spacing: 0.08em;
167
+ text-transform: uppercase;
168
+ cursor: pointer;
169
+ }
170
+ .ldt-file-input::file-selector-button:hover {
171
+ color: var(--loidolt-text-accent);
172
+ }
173
+ /* A palette as a real radio group: one pressed swatch, arrow-key movement. */
174
+ .ldt-swatch-group {
175
+ display: flex;
176
+ flex-wrap: wrap;
177
+ gap: var(--loidolt-space-1);
178
+ }
179
+ .ldt-swatch-group__swatch {
180
+ display: inline-block;
181
+ width: 1.75rem;
182
+ height: 1.75rem;
183
+ border: 2px solid transparent;
184
+ background: var(--ldt-swatch);
185
+ cursor: pointer;
186
+ }
187
+ .ldt-swatch-group--sm .ldt-swatch-group__swatch {
188
+ width: 1.5rem;
189
+ height: 1.5rem;
190
+ }
191
+ .ldt-swatch-group__swatch:has(input:checked) {
192
+ border-color: var(--loidolt-accent);
193
+ }
194
+ .ldt-swatch-group__swatch:has(input:focus-visible) {
195
+ outline: 2px solid var(--loidolt-focus-ring);
196
+ outline-offset: 1px;
197
+ }
198
+ .ldt-swatch-group__swatch:has(input:disabled) {
199
+ opacity: 0.4;
200
+ cursor: not-allowed;
201
+ }
@@ -0,0 +1,331 @@
1
+ .ldt-app-shell {
2
+ min-height: 100dvh;
3
+ display: grid;
4
+ grid-template-rows: auto minmax(0, 1fr) auto;
5
+ /*
6
+ * Without an explicit column the implicit one is content-sized, so anything too wide for the
7
+ * viewport — a topbar with many links, a wide table — stretched the entire shell and put the
8
+ * page into horizontal scroll. `minmax(0, 1fr)` caps it at the container width.
9
+ */
10
+ grid-template-columns: minmax(0, 1fr);
11
+ color: var(--loidolt-text);
12
+ background: var(--loidolt-background);
13
+ }
14
+ .ldt-app-shell > :where(header, main, footer) {
15
+ min-width: 0;
16
+ }
17
+ .ldt-app-shell > main,
18
+ .ldt-workspace-container {
19
+ /* Workspace normally sits directly in AppShell's main row. Naming that row as an inline-size
20
+ container lets the same compact layout work when the shell itself is embedded or split. The
21
+ utility covers standalone workspaces without imposing a wrapper element in the component. */
22
+ container: loidolt-workspace / inline-size;
23
+ }
24
+ .ldt-topbar {
25
+ min-width: 0;
26
+ min-height: var(--loidolt-size-topbar);
27
+ display: flex;
28
+ /* Reflow at whatever width runs out of room, rather than only at the compact breakpoint. */
29
+ flex-wrap: wrap;
30
+ align-items: center;
31
+ gap: 1.25rem;
32
+ padding: 0 1.35rem;
33
+ color: var(--loidolt-text);
34
+ background: var(--loidolt-surface);
35
+ }
36
+ .ldt-brand {
37
+ display: inline-flex;
38
+ flex: 0 0 auto;
39
+ align-items: baseline;
40
+ gap: 0.55rem;
41
+ color: var(--loidolt-text);
42
+ text-decoration: none;
43
+ }
44
+ .ldt-brand:hover {
45
+ text-decoration: none;
46
+ }
47
+ .ldt-brand__name {
48
+ font-size: var(--loidolt-font-size-lg);
49
+ font-weight: 500;
50
+ letter-spacing: -0.02em;
51
+ }
52
+ .ldt-brand__meta {
53
+ color: var(--loidolt-text-accent);
54
+ font: var(--loidolt-font-size-xs) var(--loidolt-font-utility);
55
+ letter-spacing: 0.12em;
56
+ text-transform: uppercase;
57
+ }
58
+ .ldt-topbar__nav {
59
+ /*
60
+ * Flex items refuse to shrink below their content width without `min-width: 0`, so a nav
61
+ * with more links than room pushed the whole page into horizontal scroll. Let it shrink,
62
+ * and scroll on its own when even the wrapped layout cannot fit the links.
63
+ */
64
+ min-width: 0;
65
+ display: flex;
66
+ align-self: stretch;
67
+ overflow-x: auto;
68
+ scrollbar-width: thin;
69
+ }
70
+ .ldt-topbar__actions {
71
+ min-width: 0;
72
+ margin-left: auto;
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 0.4rem;
76
+ }
77
+ .ldt-nav-menu {
78
+ min-height: var(--loidolt-size-control);
79
+ display: inline-flex;
80
+ align-items: center;
81
+ gap: 0.35rem;
82
+ border: 0;
83
+ border-bottom: 2px solid transparent;
84
+ padding: 0 0.75rem;
85
+ color: var(--loidolt-text-muted);
86
+ background: transparent;
87
+ font: var(--loidolt-font-size-sm) var(--loidolt-font-utility);
88
+ letter-spacing: 0.08em;
89
+ text-transform: uppercase;
90
+ }
91
+ .ldt-nav-menu:hover:not(:disabled, [aria-disabled='true']) {
92
+ color: var(--loidolt-text);
93
+ }
94
+ .ldt-nav-menu[data-state='open'] {
95
+ color: var(--loidolt-text);
96
+ }
97
+ .ldt-nav-menu--active,
98
+ .ldt-nav-menu[aria-current='page'] {
99
+ color: var(--loidolt-text);
100
+ border-bottom-color: var(--loidolt-accent);
101
+ }
102
+ /* Plain topbar navigation link — the anchor sibling of .ldt-nav-menu's dropdown trigger.
103
+ Router marks the active page with aria-current="page". */
104
+ .ldt-topbar__link {
105
+ display: inline-flex;
106
+ align-items: center;
107
+ align-self: stretch;
108
+ border-bottom: 2px solid transparent;
109
+ padding: 0 0.85rem;
110
+ color: var(--loidolt-text-muted);
111
+ font-size: var(--loidolt-font-size-sm);
112
+ text-decoration: none;
113
+ }
114
+ .ldt-topbar__link:hover {
115
+ color: var(--loidolt-text);
116
+ text-decoration: none;
117
+ }
118
+ .ldt-topbar__link[aria-current='page'] {
119
+ color: var(--loidolt-text);
120
+ border-bottom-color: var(--loidolt-accent);
121
+ }
122
+ .ldt-contextbar {
123
+ min-width: 0;
124
+ min-height: var(--loidolt-size-contextbar);
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: space-between;
128
+ gap: 1rem;
129
+ padding: 0.4rem 1.35rem;
130
+ border-block: var(--loidolt-border-width) solid var(--loidolt-border);
131
+ color: var(--loidolt-text);
132
+ background: var(--loidolt-surface-alt);
133
+ }
134
+ .ldt-contextbar__location {
135
+ min-width: 0;
136
+ display: flex;
137
+ align-items: baseline;
138
+ gap: 0.65rem;
139
+ }
140
+ .ldt-contextbar__section {
141
+ flex: 0 0 auto;
142
+ color: var(--loidolt-text-accent);
143
+ font: var(--loidolt-font-size-xs) var(--loidolt-font-utility);
144
+ letter-spacing: 0.1em;
145
+ text-transform: uppercase;
146
+ }
147
+ .ldt-contextbar__actions {
148
+ flex: 0 0 auto;
149
+ display: flex;
150
+ align-items: center;
151
+ gap: var(--ldt-gap, var(--loidolt-space-2));
152
+ }
153
+ .ldt-contextbar__title,
154
+ .ldt-contextbar__detail {
155
+ overflow: hidden;
156
+ text-overflow: ellipsis;
157
+ white-space: nowrap;
158
+ }
159
+ .ldt-contextbar__detail {
160
+ color: var(--loidolt-text-muted);
161
+ font: var(--loidolt-font-size-xs) var(--loidolt-font-utility);
162
+ }
163
+ .ldt-workspace {
164
+ min-width: 0;
165
+ min-height: 0;
166
+ display: grid;
167
+ grid-template-columns: minmax(0, 1fr);
168
+ }
169
+ .ldt-workspace--sidebar {
170
+ grid-template-columns: var(--ldt-sidebar-width, var(--loidolt-size-sidebar)) minmax(0, 1fr);
171
+ }
172
+ .ldt-workspace--inspector {
173
+ grid-template-columns: minmax(0, 1fr) var(--ldt-inspector-width, var(--loidolt-size-inspector));
174
+ }
175
+ .ldt-workspace--sidebar.ldt-workspace--inspector {
176
+ grid-template-columns: var(--ldt-sidebar-width, var(--loidolt-size-sidebar)) minmax(0, 1fr) var(
177
+ --ldt-inspector-width,
178
+ var(--loidolt-size-inspector)
179
+ );
180
+ }
181
+ /* An optional header row (ContextBar, toolbars) above the panes, spanning every column. */
182
+ .ldt-workspace--header {
183
+ grid-template-rows: auto minmax(0, 1fr);
184
+ }
185
+ .ldt-workspace__header {
186
+ grid-column: 1 / -1;
187
+ min-width: 0;
188
+ }
189
+ .ldt-sidebar {
190
+ min-width: 0;
191
+ overflow-y: auto;
192
+ padding: 1.25rem;
193
+ border-right: var(--loidolt-border-width) solid var(--loidolt-border);
194
+ color: var(--loidolt-text);
195
+ background: var(--loidolt-surface);
196
+ }
197
+ .ldt-sidebar--right {
198
+ border-right: 0;
199
+ border-left: var(--loidolt-border-width) solid var(--loidolt-border);
200
+ }
201
+ .ldt-workspace__main {
202
+ min-width: 0;
203
+ padding: var(--loidolt-space-6);
204
+ }
205
+ /* A toolbar floated over a positioned container (canvas, map, viewer). The parent must be
206
+ position: relative; the bar is a pointer-events island above the stage content. */
207
+ .ldt-floating-bar {
208
+ position: absolute;
209
+ display: flex;
210
+ align-items: center;
211
+ gap: var(--loidolt-space-1);
212
+ padding: var(--loidolt-space-1);
213
+ border: var(--loidolt-border-width) solid var(--loidolt-border);
214
+ border-radius: var(--loidolt-border-radius);
215
+ background: var(--loidolt-surface);
216
+ z-index: var(--loidolt-z-raised);
217
+ pointer-events: auto;
218
+ }
219
+ .ldt-floating-bar--vertical {
220
+ flex-direction: column;
221
+ }
222
+ .ldt-floating-bar--top-start {
223
+ top: var(--loidolt-space-3);
224
+ left: var(--loidolt-space-3);
225
+ }
226
+ .ldt-floating-bar--top-end {
227
+ top: var(--loidolt-space-3);
228
+ right: var(--loidolt-space-3);
229
+ }
230
+ .ldt-floating-bar--bottom-start {
231
+ bottom: var(--loidolt-space-3);
232
+ left: var(--loidolt-space-3);
233
+ }
234
+ .ldt-floating-bar--bottom-end {
235
+ bottom: var(--loidolt-space-3);
236
+ right: var(--loidolt-space-3);
237
+ }
238
+ .ldt-page-header {
239
+ display: flex;
240
+ align-items: end;
241
+ justify-content: space-between;
242
+ gap: 1rem;
243
+ margin-bottom: var(--loidolt-space-6);
244
+ padding-bottom: var(--loidolt-space-4);
245
+ border-bottom: var(--loidolt-border-width) solid var(--loidolt-border);
246
+ }
247
+ .ldt-page-header__title {
248
+ margin: 0;
249
+ font-size: var(--loidolt-font-size-xl);
250
+ }
251
+ .ldt-page-header__description {
252
+ max-width: 48rem;
253
+ margin: 0.4rem 0 0;
254
+ color: var(--loidolt-text-muted);
255
+ }
256
+ .ldt-section {
257
+ padding-block: var(--loidolt-space-6);
258
+ border-bottom: var(--loidolt-border-width) solid var(--loidolt-border-soft);
259
+ }
260
+ .ldt-section__heading {
261
+ display: flex;
262
+ align-items: baseline;
263
+ justify-content: space-between;
264
+ gap: 1rem;
265
+ margin-bottom: var(--loidolt-space-4);
266
+ }
267
+ .ldt-section__title {
268
+ margin: 0;
269
+ font-size: var(--loidolt-font-size-lg);
270
+ }
271
+
272
+ /* Single breakpoint, mirroring `tokens.breakpoints.compact`. */
273
+ @media (max-width: 760px) {
274
+ .ldt-topbar {
275
+ min-height: auto;
276
+ padding-block: 0.75rem;
277
+ gap: 0.75rem;
278
+ }
279
+ .ldt-topbar__actions {
280
+ width: 100%;
281
+ margin-left: 0;
282
+ overflow-x: auto;
283
+ }
284
+ .ldt-contextbar {
285
+ align-items: start;
286
+ }
287
+ .ldt-contextbar__location {
288
+ display: grid;
289
+ gap: 0.15rem;
290
+ }
291
+ .ldt-contextbar__detail {
292
+ display: none;
293
+ }
294
+ .ldt-workspace,
295
+ .ldt-workspace--sidebar,
296
+ .ldt-workspace--inspector,
297
+ .ldt-workspace--sidebar.ldt-workspace--inspector {
298
+ display: block;
299
+ }
300
+ .ldt-sidebar,
301
+ .ldt-sidebar--right {
302
+ border: 0;
303
+ border-bottom: var(--loidolt-border-width) solid var(--loidolt-border);
304
+ }
305
+ .ldt-workspace__main {
306
+ padding: var(--loidolt-space-4);
307
+ }
308
+ .ldt-page-header {
309
+ align-items: start;
310
+ flex-direction: column;
311
+ }
312
+ }
313
+
314
+ /* Container-aware counterpart to the viewport fallback above. Consumers rendering Workspace
315
+ outside AppShell can put `ldt-workspace-container` on its nearest sizing wrapper. */
316
+ @container loidolt-workspace (max-width: 760px) {
317
+ .ldt-workspace,
318
+ .ldt-workspace--sidebar,
319
+ .ldt-workspace--inspector,
320
+ .ldt-workspace--sidebar.ldt-workspace--inspector {
321
+ display: block;
322
+ }
323
+ .ldt-sidebar,
324
+ .ldt-sidebar--right {
325
+ border: 0;
326
+ border-bottom: var(--loidolt-border-width) solid var(--loidolt-border);
327
+ }
328
+ .ldt-workspace__main {
329
+ padding: var(--loidolt-space-4);
330
+ }
331
+ }