@nordmanai/nif 0.2.5 → 0.2.8

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,41 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { Dialog as DialogPrimitive } from '../../lib/nm-motion.ts';
3
+ import { nmClass } from '../../lib/nm-class.ts';
4
+ import type { NmDrawerSide } from '../../config/nm-drawer.ts';
5
+
6
+ /**
7
+ * A panel that slides in from an edge, over the page, with a scrim behind it. The shadcn Sheet, on the Base UI
8
+ * Dialog, styled by NIF tokens. For navigation on a phone, filters, or a form that should not leave the page.
9
+ */
10
+ export function NmDrawer(props: DialogPrimitive.Root.Props) {
11
+ return <DialogPrimitive.Root data-slot="drawer" {...props} />;
12
+ }
13
+ export function NmDrawerTrigger(props: DialogPrimitive.Trigger.Props) {
14
+ return <DialogPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
15
+ }
16
+ export function NmDrawerClose(props: DialogPrimitive.Close.Props) {
17
+ return <DialogPrimitive.Close data-slot="drawer-close" {...props} />;
18
+ }
19
+ /** The panel. `side` is the edge it comes from. Its width is the --nm-width-drawer token. */
20
+ export function NmDrawerContent({ className, children, side = 'left', ...props }: DialogPrimitive.Popup.Props & { side?: NmDrawerSide }) {
21
+ return (
22
+ <DialogPrimitive.Portal>
23
+ <DialogPrimitive.Backdrop data-slot="drawer-overlay" className="nm-drawer__backdrop" />
24
+ <DialogPrimitive.Popup data-slot="drawer-content" data-side={side} from={side} className={nmClass('nm-drawer', className as string)} {...props}>
25
+ {children}
26
+ </DialogPrimitive.Popup>
27
+ </DialogPrimitive.Portal>
28
+ );
29
+ }
30
+ export function NmDrawerHeader({ className, ...props }: ComponentProps<'div'>) {
31
+ return <div data-slot="drawer-header" className={nmClass('nm-drawer__header', className)} {...props} />;
32
+ }
33
+ export function NmDrawerFooter({ className, ...props }: ComponentProps<'div'>) {
34
+ return <div data-slot="drawer-footer" className={nmClass('nm-drawer__footer', className)} {...props} />;
35
+ }
36
+ export function NmDrawerTitle({ className, ...props }: DialogPrimitive.Title.Props) {
37
+ return <DialogPrimitive.Title data-slot="drawer-title" className={nmClass('nm-drawer__title', className as string)} {...props} />;
38
+ }
39
+ export function NmDrawerDescription({ className, ...props }: DialogPrimitive.Description.Props) {
40
+ return <DialogPrimitive.Description data-slot="drawer-description" className={nmClass('nm-drawer__description', className as string)} {...props} />;
41
+ }
@@ -0,0 +1,647 @@
1
+ /* The provider holds the sidebar and the main area side by side. */
2
+ .nm-sidebar-provider {
3
+ --nm-sidebar-width: var(--nm-width-sidebar);
4
+ --nm-sidebar-width-icon: var(--nm-width-sidebar-icon);
5
+ position: relative;
6
+ box-sizing: border-box;
7
+ display: flex;
8
+ width: 100%;
9
+ min-height: 100%;
10
+ min-width: 0;
11
+ overflow: hidden;
12
+ background: var(--nm-canvas);
13
+ color: var(--nm-fg);
14
+ font-family: var(--nm-font);
15
+ font-size: var(--nm-type-ui);
16
+ }
17
+ /* A fixed height frames the provider as a card: a sidebar shown inside a page. */
18
+ .nm-sidebar-provider[data-height='sm'],
19
+ .nm-sidebar-provider[data-height='lg'] {
20
+ min-height: 0;
21
+ border: var(--nm-rule) solid var(--nm-line-strong);
22
+ border-radius: var(--nm-radius-lg);
23
+ }
24
+ .nm-sidebar-provider[data-height='sm'] {
25
+ height: var(--nm-height-sidebar-sm);
26
+ }
27
+ .nm-sidebar-provider[data-height='lg'] {
28
+ height: var(--nm-height-sidebar-lg);
29
+ }
30
+
31
+ /* The sidebar: a column that takes its width from the provider and gives it up when collapsed. */
32
+ .nm-sidebar {
33
+ position: relative;
34
+ box-sizing: border-box;
35
+ display: flex;
36
+ flex: 0 0 auto;
37
+ flex-direction: column;
38
+ width: var(--nm-sidebar-width);
39
+ min-height: 0;
40
+ overflow: hidden;
41
+ color: var(--nm-fg);
42
+ background: var(--nm-raised);
43
+ transition:
44
+ width var(--reveal) var(--ease),
45
+ padding var(--reveal) var(--ease);
46
+ }
47
+ .nm-sidebar[data-side='right'] {
48
+ order: 1;
49
+ }
50
+ .nm-sidebar[data-variant='sidebar'][data-side='left'] {
51
+ border-inline-end: var(--nm-rule) solid var(--nm-divider);
52
+ }
53
+ .nm-sidebar[data-variant='sidebar'][data-side='right'] {
54
+ border-inline-start: var(--nm-rule) solid var(--nm-divider);
55
+ }
56
+ .nm-sidebar[data-collapsible='offcanvas'] {
57
+ width: 0;
58
+ border-width: 0;
59
+ }
60
+ .nm-sidebar[data-collapsible='icon'],
61
+ .nm-sidebar[data-collapsible='toggle'] {
62
+ width: var(--nm-sidebar-width-icon);
63
+ }
64
+
65
+ /* The inner column keeps its width while the outer one closes, so nothing reflows on the way out. */
66
+ .nm-sidebar__inner {
67
+ box-sizing: border-box;
68
+ display: flex;
69
+ flex: 1;
70
+ flex-direction: column;
71
+ width: 100%;
72
+ min-width: 0;
73
+ min-height: 0;
74
+ }
75
+ .nm-sidebar[data-collapsible='offcanvas'] .nm-sidebar__inner {
76
+ width: var(--nm-sidebar-width);
77
+ }
78
+
79
+ /* Peek: the collapsed sidebar shown in full over the main area while the pointer rests on it or on the toggle.
80
+ The column keeps its collapsed width in the flow, and the inner panel floats out over the page. */
81
+ .nm-sidebar[data-peek] {
82
+ overflow: visible;
83
+ z-index: 1;
84
+ }
85
+ .nm-sidebar[data-mode='offcanvas'][data-peek] {
86
+ width: 0;
87
+ border-width: 0;
88
+ }
89
+ .nm-sidebar[data-mode='icon'][data-peek],
90
+ .nm-sidebar[data-mode='toggle'][data-peek] {
91
+ width: var(--nm-sidebar-width-icon);
92
+ }
93
+ .nm-sidebar[data-peek] .nm-sidebar__inner {
94
+ position: absolute;
95
+ inset-block: 0;
96
+ inset-inline-start: 0;
97
+ width: var(--nm-sidebar-width);
98
+ border-inline-end: var(--nm-rule) solid var(--nm-line-strong);
99
+ background: var(--nm-raised);
100
+ }
101
+ .nm-sidebar[data-side='right'][data-peek] .nm-sidebar__inner {
102
+ inset-inline-start: auto;
103
+ inset-inline-end: 0;
104
+ border-inline-end: 0;
105
+ border-inline-start: var(--nm-rule) solid var(--nm-line-strong);
106
+ }
107
+ .nm-sidebar[data-variant='floating'][data-peek] .nm-sidebar__inner,
108
+ .nm-sidebar[data-variant='inset'][data-peek] .nm-sidebar__inner {
109
+ inset-block: var(--nm-space-8);
110
+ inset-inline-start: var(--nm-space-8);
111
+ border: var(--nm-rule) solid var(--nm-line-strong);
112
+ border-radius: var(--nm-radius-lg);
113
+ }
114
+ .nm-sidebar[data-variant='floating'][data-side='right'][data-peek] .nm-sidebar__inner,
115
+ .nm-sidebar[data-variant='inset'][data-side='right'][data-peek] .nm-sidebar__inner {
116
+ inset-inline-start: auto;
117
+ inset-inline-end: var(--nm-space-8);
118
+ }
119
+
120
+ /* Floating: a raised card inside a margin. Inset: the sidebar is on the canvas and the main area is the card. */
121
+ .nm-sidebar[data-variant='floating'],
122
+ .nm-sidebar[data-variant='inset'] {
123
+ padding: var(--nm-space-8);
124
+ background: var(--nm-canvas);
125
+ }
126
+ .nm-sidebar[data-variant='floating'][data-side='left'],
127
+ .nm-sidebar[data-variant='inset'][data-side='left'] {
128
+ padding-inline-end: 0;
129
+ }
130
+ .nm-sidebar[data-variant='floating'][data-side='right'],
131
+ .nm-sidebar[data-variant='inset'][data-side='right'] {
132
+ padding-inline-start: 0;
133
+ }
134
+ .nm-sidebar[data-variant='floating'] .nm-sidebar__inner {
135
+ border: var(--nm-rule) solid var(--nm-line-strong);
136
+ border-radius: var(--nm-radius-lg);
137
+ background: var(--nm-raised);
138
+ }
139
+ .nm-sidebar[data-variant='floating'][data-collapsible='offcanvas'],
140
+ .nm-sidebar[data-variant='inset'][data-collapsible='offcanvas'],
141
+ .nm-sidebar[data-variant='floating'][data-mode='offcanvas'][data-peek],
142
+ .nm-sidebar[data-variant='inset'][data-mode='offcanvas'][data-peek] {
143
+ padding-inline: 0;
144
+ }
145
+
146
+ /* On a phone the sidebar is a drawer over the page: the NIF Drawer at the sidebar's phone width. */
147
+ .nm-sidebar__sheet {
148
+ --nm-width-drawer: var(--nm-width-sidebar-mobile);
149
+ }
150
+
151
+ /* The main area. With the inset variant it is the raised card. */
152
+ .nm-sidebar__inset {
153
+ position: relative;
154
+ box-sizing: border-box;
155
+ display: flex;
156
+ flex: 1;
157
+ flex-direction: column;
158
+ min-width: 0;
159
+ min-height: 0;
160
+ background: var(--nm-canvas);
161
+ }
162
+ .nm-sidebar[data-variant='inset'] ~ .nm-sidebar__inset {
163
+ margin: var(--nm-space-8);
164
+ overflow: hidden;
165
+ border-radius: var(--nm-radius-lg);
166
+ background: var(--nm-raised);
167
+ }
168
+
169
+ /* The button that toggles the sidebar: the quiet NIF icon button, kept in place in a bar or a header. */
170
+ .nm-sidebar__trigger {
171
+ flex-shrink: 0;
172
+ }
173
+ /* A header with the toggle in it is a row: the brand or switcher, then the toggle at the end. */
174
+ .nm-sidebar__header:has(> .nm-sidebar__trigger) {
175
+ flex-direction: row;
176
+ align-items: center;
177
+ }
178
+ .nm-sidebar__header:has(> .nm-sidebar__trigger) > :not(.nm-sidebar__trigger) {
179
+ flex: 1 1 auto;
180
+ min-width: 0;
181
+ }
182
+ .nm-sidebar[data-side='right'] .nm-sidebar__header:has(> .nm-sidebar__trigger) {
183
+ flex-direction: row-reverse;
184
+ }
185
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__header:has(> .nm-sidebar__trigger) {
186
+ flex-direction: column;
187
+ }
188
+
189
+ /* The thin strip on the edge that toggles the sidebar. A line appears on hover. */
190
+ .nm-sidebar__rail {
191
+ position: absolute;
192
+ inset-block: 0;
193
+ inset-inline-end: calc(var(--nm-space-8) * -1);
194
+ z-index: 1;
195
+ width: var(--nm-space-16);
196
+ padding: 0;
197
+ border: 0;
198
+ background: transparent;
199
+ cursor: w-resize;
200
+ outline: none;
201
+ }
202
+ .nm-sidebar[data-side='right'] .nm-sidebar__rail {
203
+ inset-inline-end: auto;
204
+ inset-inline-start: calc(var(--nm-space-8) * -1);
205
+ cursor: e-resize;
206
+ }
207
+ .nm-sidebar[data-state='collapsed'] .nm-sidebar__rail {
208
+ cursor: e-resize;
209
+ }
210
+ .nm-sidebar[data-state='collapsed'][data-side='right'] .nm-sidebar__rail {
211
+ cursor: w-resize;
212
+ }
213
+ .nm-sidebar__rail::after {
214
+ content: '';
215
+ position: absolute;
216
+ inset-block: 0;
217
+ left: 50%;
218
+ width: var(--nm-rule);
219
+ background: var(--nm-line-strong);
220
+ opacity: 0;
221
+ transition: opacity var(--reveal) var(--ease);
222
+ }
223
+ .nm-sidebar__rail:hover::after {
224
+ opacity: 1;
225
+ }
226
+
227
+ /* Header and footer stay put. The content between them scrolls. */
228
+ .nm-sidebar__header,
229
+ .nm-sidebar__footer {
230
+ display: flex;
231
+ flex-shrink: 0;
232
+ flex-direction: column;
233
+ gap: var(--nm-space-8);
234
+ padding: var(--nm-space-8);
235
+ }
236
+ .nm-sidebar__content {
237
+ display: flex;
238
+ flex: 1;
239
+ flex-direction: column;
240
+ min-height: 0;
241
+ overflow: hidden auto;
242
+ scrollbar-width: thin;
243
+ scrollbar-color: var(--nm-line-strong) transparent;
244
+ }
245
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__content {
246
+ overflow: hidden;
247
+ }
248
+ .nm-sidebar__separator {
249
+ width: auto;
250
+ margin-inline: var(--nm-space-8);
251
+ }
252
+ .nm-sidebar__input {
253
+ height: var(--control-h);
254
+ background: var(--nm-panel);
255
+ border-color: transparent;
256
+ }
257
+
258
+ /* A group: label, optional action, then its rows. */
259
+ .nm-sidebar__group {
260
+ position: relative;
261
+ display: flex;
262
+ flex-direction: column;
263
+ min-width: 0;
264
+ padding: var(--nm-space-8);
265
+ }
266
+ .nm-sidebar__group-label {
267
+ display: flex;
268
+ flex-shrink: 0;
269
+ align-items: center;
270
+ gap: var(--nm-space-8);
271
+ box-sizing: border-box;
272
+ height: var(--control-h);
273
+ padding: 0 var(--nm-icon-pad);
274
+ border-radius: var(--nm-control-r);
275
+ color: var(--nm-fg-subtle);
276
+ font-size: var(--nm-type-ui-sm);
277
+ font-weight: var(--nm-weight-medium);
278
+ line-height: var(--nm-leading-tight);
279
+ letter-spacing: var(--nm-track);
280
+ white-space: nowrap;
281
+ outline: none;
282
+ }
283
+ .nm-sidebar__group-label .nm-icon {
284
+ flex-shrink: 0;
285
+ width: var(--nm-icon-sm);
286
+ height: var(--nm-icon-sm);
287
+ }
288
+ /* Actions are the NIF small icon button, quiet. A tighter pad fits them inside a row. */
289
+ .nm-sidebar__group-action,
290
+ .nm-sidebar__menu-action {
291
+ --nm-icon-pad: var(--nm-space-6);
292
+ position: absolute;
293
+ inset-inline-end: var(--nm-space-4);
294
+ color: var(--nm-fg-subtle);
295
+ }
296
+ .nm-sidebar__group-action {
297
+ top: calc(var(--nm-space-8) + (var(--control-h) - var(--nm-icon-button) - var(--nm-space-6) * 2) / 2);
298
+ inset-inline-end: var(--nm-space-12);
299
+ }
300
+ .nm-sidebar__group-content {
301
+ width: 100%;
302
+ min-width: 0;
303
+ }
304
+
305
+ /* The menu is a plain list. */
306
+ .nm-sidebar__menu,
307
+ .nm-sidebar__menu-sub {
308
+ display: flex;
309
+ flex-direction: column;
310
+ gap: var(--nm-space-2);
311
+ width: 100%;
312
+ min-width: 0;
313
+ margin: 0;
314
+ padding: 0;
315
+ list-style: none;
316
+ }
317
+ .nm-sidebar__menu-item,
318
+ .nm-sidebar__menu-sub-item {
319
+ position: relative;
320
+ min-width: 0;
321
+ }
322
+
323
+ /* A row: an icon and a label. Quiet at rest, filled on hover, filled and firm when active. */
324
+ .nm-sidebar__menu-button {
325
+ display: flex;
326
+ align-items: center;
327
+ gap: var(--nm-space-8);
328
+ box-sizing: border-box;
329
+ width: 100%;
330
+ height: var(--control-h);
331
+ padding: 0 var(--nm-icon-pad);
332
+ border: var(--nm-rule) solid transparent;
333
+ border-radius: var(--nm-control-r);
334
+ background: transparent;
335
+ color: var(--nm-fg);
336
+ font: inherit;
337
+ font-size: var(--nm-type-ui);
338
+ font-weight: var(--nm-weight-regular);
339
+ line-height: var(--nm-leading-tight);
340
+ text-align: start;
341
+ text-decoration: none;
342
+ white-space: nowrap;
343
+ overflow: hidden;
344
+ cursor: pointer;
345
+ outline: none;
346
+ transition:
347
+ background-color var(--reveal) var(--ease),
348
+ color var(--reveal) var(--ease);
349
+ }
350
+ .nm-sidebar__menu-button > span:not(.nm-avatar) {
351
+ min-width: 0;
352
+ flex: 1 1 auto;
353
+ overflow: hidden;
354
+ text-overflow: ellipsis;
355
+ }
356
+ .nm-sidebar__menu-button > .nm-icon {
357
+ flex-shrink: 0;
358
+ width: var(--nm-icon-button);
359
+ height: var(--nm-icon-button);
360
+ color: var(--nm-fg-muted);
361
+ transition: color var(--reveal) var(--ease);
362
+ }
363
+ /* An avatar in a row: the NIF Avatar, on the strong line so it stays a disc when the row fills on hover or when active. */
364
+ .nm-sidebar__menu-button > .nm-avatar {
365
+ flex-shrink: 0;
366
+ background: var(--nm-line-strong);
367
+ color: var(--nm-fg);
368
+ }
369
+ .nm-sidebar__menu-button:hover,
370
+ .nm-sidebar__menu-button:focus-visible,
371
+ .nm-sidebar__menu-button[data-popup-open] {
372
+ background: var(--nm-line);
373
+ color: var(--nm-fg);
374
+ }
375
+ .nm-sidebar__menu-button:hover > .nm-icon,
376
+ .nm-sidebar__menu-button:focus-visible > .nm-icon,
377
+ .nm-sidebar__menu-button[data-active] > .nm-icon {
378
+ color: var(--nm-fg);
379
+ }
380
+ .nm-sidebar__menu-button:focus-visible {
381
+ outline: var(--nm-rule) solid var(--focus);
382
+ outline-offset: calc(var(--nm-rule) * -1);
383
+ }
384
+ .nm-sidebar__menu-button[data-active] {
385
+ background: var(--nm-panel);
386
+ color: var(--nm-fg);
387
+ font-weight: var(--nm-weight-medium);
388
+ }
389
+ .nm-sidebar__menu-button:disabled,
390
+ .nm-sidebar__menu-button[aria-disabled='true'] {
391
+ pointer-events: none;
392
+ opacity: var(--control-disabled-opacity);
393
+ }
394
+ .nm-sidebar__menu-button[data-variant='outline'] {
395
+ border-color: var(--nm-line-strong);
396
+ background: var(--nm-raised);
397
+ }
398
+ .nm-sidebar__menu-button[data-variant='outline']:hover,
399
+ .nm-sidebar__menu-button[data-variant='outline'][data-active] {
400
+ background: var(--nm-panel);
401
+ }
402
+ .nm-sidebar__menu-button[data-size='sm'] {
403
+ height: var(--control-h-sm);
404
+ font-size: var(--nm-type-ui-sm);
405
+ }
406
+ .nm-sidebar__menu-button[data-size='lg'] {
407
+ height: var(--control-h-lg);
408
+ }
409
+ /* A large row with two lines of text, such as the person in the footer. */
410
+ .nm-sidebar__menu-button > .nm-sidebar__text {
411
+ display: flex;
412
+ flex: 1 1 auto;
413
+ flex-direction: column;
414
+ gap: var(--nm-space-2);
415
+ min-width: 0;
416
+ line-height: var(--nm-leading-tight);
417
+ }
418
+ .nm-sidebar__text > span {
419
+ display: block;
420
+ overflow: hidden;
421
+ text-overflow: ellipsis;
422
+ white-space: nowrap;
423
+ }
424
+ .nm-sidebar__text > span + span {
425
+ color: var(--nm-fg-subtle);
426
+ font-size: var(--nm-type-ui-sm);
427
+ font-weight: var(--nm-weight-regular);
428
+ }
429
+ /* A row with a badge or an action leaves room for it. */
430
+ .nm-sidebar__menu-item:has(> .nm-sidebar__menu-badge) > .nm-sidebar__menu-button,
431
+ .nm-sidebar__menu-item:has(> .nm-sidebar__menu-action) > .nm-sidebar__menu-button {
432
+ padding-inline-end: var(--nm-icon-control);
433
+ }
434
+
435
+ /* The chevron of a row that opens. It points right when closed and down when open. */
436
+ .nm-sidebar__chevron {
437
+ flex-shrink: 0;
438
+ margin-inline-start: auto;
439
+ width: var(--nm-icon-sm);
440
+ height: var(--nm-icon-sm);
441
+ color: var(--nm-fg-subtle);
442
+ transform: rotate(-90deg);
443
+ transition: transform var(--reveal) var(--ease);
444
+ }
445
+ [aria-expanded='true'] > .nm-sidebar__chevron,
446
+ [data-panel-open] > .nm-sidebar__chevron,
447
+ [data-open] > .nm-sidebar__chevron,
448
+ .nm-sidebar__chevron[data-menu] {
449
+ transform: none;
450
+ }
451
+
452
+ /* The action at the end of a row sits over the row's end, centred on its height. */
453
+ .nm-sidebar__menu-action {
454
+ /* Centred by arithmetic, not a transform: the motion button owns transform. */
455
+ top: calc(50% - (var(--nm-icon-button) + var(--nm-space-6) * 2) / 2);
456
+ transition:
457
+ opacity var(--reveal) var(--ease),
458
+ background-color var(--reveal) var(--ease);
459
+ }
460
+ .nm-sidebar__menu-action[data-show-on-hover] {
461
+ opacity: 0;
462
+ }
463
+ .nm-sidebar__menu-item:hover > .nm-sidebar__menu-action[data-show-on-hover],
464
+ .nm-sidebar__menu-item:focus-within > .nm-sidebar__menu-action[data-show-on-hover],
465
+ .nm-sidebar__menu-item:has(> [data-active]) > .nm-sidebar__menu-action[data-show-on-hover],
466
+ .nm-sidebar__menu-action[data-show-on-hover][data-popup-open],
467
+ .nm-sidebar__menu-action[data-show-on-hover][aria-expanded='true'] {
468
+ opacity: 1;
469
+ }
470
+
471
+ /* A count at the end of a row: the NIF Chip, small, quiet until the row is hovered or active. */
472
+ .nm-sidebar__menu-badge {
473
+ position: absolute;
474
+ top: 50%;
475
+ inset-inline-end: var(--nm-icon-pad);
476
+ background: transparent;
477
+ color: var(--nm-fg-subtle);
478
+ font-variant-numeric: tabular-nums;
479
+ pointer-events: none;
480
+ user-select: none;
481
+ transform: translateY(-50%);
482
+ }
483
+ .nm-sidebar__menu-item:hover > .nm-sidebar__menu-badge,
484
+ .nm-sidebar__menu-item:has(> [data-active]) > .nm-sidebar__menu-badge {
485
+ color: var(--nm-fg);
486
+ }
487
+
488
+ /* A row's shape while it loads. */
489
+ .nm-sidebar__menu-skeleton {
490
+ display: flex;
491
+ align-items: center;
492
+ gap: var(--nm-space-8);
493
+ box-sizing: border-box;
494
+ height: var(--control-h);
495
+ padding: 0 var(--nm-icon-pad);
496
+ }
497
+ .nm-sidebar__menu-skeleton-icon {
498
+ flex-shrink: 0;
499
+ width: var(--nm-icon-button);
500
+ height: var(--nm-icon-button);
501
+ border-radius: var(--nm-radius-sm);
502
+ }
503
+ .nm-sidebar__menu-skeleton-text {
504
+ flex: 1 1 auto;
505
+ max-width: var(--nm-sidebar-skeleton-width, 100%);
506
+ height: var(--nm-space-12);
507
+ border-radius: var(--nm-radius-sm);
508
+ }
509
+
510
+ /* Rows under a row. A line runs down from the parent's icon, and the rows sit after it. */
511
+ .nm-sidebar__menu-sub {
512
+ box-sizing: border-box;
513
+ width: auto;
514
+ margin-inline-start: calc(var(--nm-icon-pad) + var(--nm-icon-button) / 2);
515
+ padding-inline-start: calc(var(--nm-icon-pad) + var(--nm-icon-button) / 2);
516
+ padding-block: var(--nm-space-2);
517
+ border-inline-start: var(--nm-rule) solid var(--nm-line);
518
+ }
519
+ .nm-sidebar__menu-sub-button {
520
+ display: flex;
521
+ align-items: center;
522
+ gap: var(--nm-space-8);
523
+ box-sizing: border-box;
524
+ width: 100%;
525
+ height: var(--control-h);
526
+ padding: 0 var(--nm-icon-pad);
527
+ border: 0;
528
+ border-radius: var(--nm-control-r);
529
+ background: transparent;
530
+ color: var(--nm-fg-muted);
531
+ font: inherit;
532
+ font-size: var(--nm-type-ui);
533
+ font-weight: var(--nm-weight-regular);
534
+ line-height: var(--nm-leading-tight);
535
+ text-align: start;
536
+ text-decoration: none;
537
+ white-space: nowrap;
538
+ overflow: hidden;
539
+ cursor: pointer;
540
+ outline: none;
541
+ transition:
542
+ background-color var(--reveal) var(--ease),
543
+ color var(--reveal) var(--ease);
544
+ }
545
+ .nm-sidebar__menu-sub-button > span {
546
+ min-width: 0;
547
+ overflow: hidden;
548
+ text-overflow: ellipsis;
549
+ }
550
+ .nm-sidebar__menu-sub-button > .nm-icon {
551
+ flex-shrink: 0;
552
+ width: var(--nm-icon-sm);
553
+ height: var(--nm-icon-sm);
554
+ color: var(--nm-fg-subtle);
555
+ }
556
+ .nm-sidebar__menu-sub-button:hover,
557
+ .nm-sidebar__menu-sub-button:focus-visible {
558
+ background: var(--nm-line);
559
+ color: var(--nm-fg);
560
+ }
561
+ .nm-sidebar__menu-sub-button:focus-visible {
562
+ outline: var(--nm-rule) solid var(--focus);
563
+ outline-offset: calc(var(--nm-rule) * -1);
564
+ }
565
+ .nm-sidebar__menu-sub-button[data-active] {
566
+ background: var(--nm-panel);
567
+ color: var(--nm-fg);
568
+ font-weight: var(--nm-weight-medium);
569
+ }
570
+ .nm-sidebar__menu-sub-button[data-size='sm'] {
571
+ height: var(--control-h-sm);
572
+ font-size: var(--nm-type-ui-sm);
573
+ }
574
+ .nm-sidebar__menu-sub-button:disabled,
575
+ .nm-sidebar__menu-sub-button[aria-disabled='true'] {
576
+ pointer-events: none;
577
+ opacity: var(--control-disabled-opacity);
578
+ }
579
+
580
+ /* Collapsed to icons: every row is a square with its icon in the middle. Labels, badges, actions and sub rows go. */
581
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__header,
582
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__footer,
583
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__group {
584
+ align-items: center;
585
+ }
586
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__group-label,
587
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__group-action,
588
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-badge,
589
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-action,
590
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-sub,
591
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__input,
592
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__chevron,
593
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-skeleton-text,
594
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-button:not(:has(.nm-icon, .nm-avatar)) {
595
+ display: none;
596
+ }
597
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-button {
598
+ width: var(--control-h);
599
+ height: var(--control-h);
600
+ padding: 0;
601
+ justify-content: center;
602
+ }
603
+ /* The label leaves the view but stays in the accessibility tree, so the row keeps its name. */
604
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-button > span:not(.nm-avatar),
605
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-button > .nm-sidebar__text {
606
+ position: absolute;
607
+ width: var(--nm-rule);
608
+ height: var(--nm-rule);
609
+ margin: calc(var(--nm-rule) * -1);
610
+ padding: 0;
611
+ overflow: hidden;
612
+ clip-path: inset(50%);
613
+ white-space: nowrap;
614
+ border: 0;
615
+ }
616
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-button[data-size='lg'] {
617
+ width: var(--control-h-lg);
618
+ height: var(--control-h-lg);
619
+ }
620
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-button[data-size='sm'] {
621
+ width: var(--control-h-sm);
622
+ height: var(--control-h-sm);
623
+ }
624
+ .nm-sidebar[data-collapsible='icon'] .nm-sidebar__menu-skeleton {
625
+ width: var(--control-h);
626
+ padding: 0;
627
+ justify-content: center;
628
+ }
629
+
630
+ /* Collapsed to the toggle: only the toggle in the header remains, centred in the narrow column. */
631
+ .nm-sidebar[data-collapsible='toggle'] .nm-sidebar__inner > :not(.nm-sidebar__header),
632
+ .nm-sidebar[data-collapsible='toggle'] .nm-sidebar__header > :not(.nm-sidebar__trigger) {
633
+ display: none;
634
+ }
635
+ .nm-sidebar[data-collapsible='toggle'] .nm-sidebar__header {
636
+ align-items: center;
637
+ justify-content: center;
638
+ }
639
+
640
+ @media (prefers-reduced-motion: reduce) {
641
+ .nm-sidebar,
642
+ .nm-sidebar__menu-button,
643
+ .nm-sidebar__chevron,
644
+ .nm-sidebar__menu-action {
645
+ transition: none;
646
+ }
647
+ }