@dorsk/tsumikit 0.82.0 → 0.82.1

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.
@@ -57,8 +57,9 @@
57
57
  /** `'sidebar-full'`: the sidebar spans the whole height on desktop/tablet
58
58
  * and the header only covers the content column (brand sits top-left). */
59
59
  layout?: 'header-top' | 'sidebar-full';
60
- /** Pin the desktop sidebar to the viewport (100dvh, own scroll) so its
61
- * footer stays visible on long pages. */
60
+ /** Pin the desktop sidebar to the viewport (own scroll) so its footer
61
+ * stays visible on long pages. Sits below the header in `header-top`,
62
+ * spans the full viewport height in `sidebar-full`. */
62
63
  stickySidebar?: boolean;
63
64
  /** Inner padding of the sidebar; `'none'` when the nav owns its gutters. */
64
65
  sidebarPadding?: 'none' | 'sm' | 'md';
@@ -414,9 +415,13 @@
414
415
  }
415
416
  .shell.sticky-sidebar .shell-sidebar {
416
417
  position: sticky;
418
+ top: var(--shell-header-h);
419
+ height: calc(100dvh - var(--shell-header-h));
420
+ align-self: start;
421
+ }
422
+ .shell.sticky-sidebar.sidebar-full .shell-sidebar {
417
423
  top: 0;
418
424
  height: 100dvh;
419
- align-self: start;
420
425
  }
421
426
  .shell-scrim,
422
427
  .shell-menu-btn {
@@ -18,8 +18,9 @@ type $$ComponentProps = {
18
18
  /** `'sidebar-full'`: the sidebar spans the whole height on desktop/tablet
19
19
  * and the header only covers the content column (brand sits top-left). */
20
20
  layout?: 'header-top' | 'sidebar-full';
21
- /** Pin the desktop sidebar to the viewport (100dvh, own scroll) so its
22
- * footer stays visible on long pages. */
21
+ /** Pin the desktop sidebar to the viewport (own scroll) so its footer
22
+ * stays visible on long pages. Sits below the header in `header-top`,
23
+ * spans the full viewport height in `sidebar-full`. */
23
24
  stickySidebar?: boolean;
24
25
  /** Inner padding of the sidebar; `'none'` when the nav owns its gutters. */
25
26
  sidebarPadding?: 'none' | 'sm' | 'md';
@@ -231,12 +231,15 @@
231
231
 
232
232
  <style>
233
233
  /* Reset the UA dialog box: we own the surface via the inner .sheet. The
234
- dialog itself is just the centering/backdrop layer. */
234
+ dialog itself is just the centering/backdrop layer. It must never scroll:
235
+ the UA `overflow: auto` lets the initial focus scroll the sheet into view
236
+ while it still sits at `translate: 0 100%`, which eats the slide-in. */
235
237
  .modal {
236
238
  margin: 0;
237
239
  padding: 0;
238
240
  border: 0;
239
241
  background: none;
242
+ overflow: clip;
240
243
  max-width: 100vw;
241
244
  max-height: 100dvh;
242
245
  width: 100%;
@@ -83,6 +83,7 @@
83
83
 
84
84
  const uid = Math.random().toString(36).slice(2, 8);
85
85
  const listId = `rs-list-${uid}`;
86
+ const nameId = `rs-name-${uid}`;
86
87
  const optionId = (i: number) => `rs-opt-${uid}-${i}`;
87
88
 
88
89
  let root = $state<HTMLDivElement | null>(null);
@@ -96,6 +97,7 @@
96
97
 
97
98
  const selectedIndex = $derived(options.findIndex((o) => getKey(o) === value));
98
99
  const selectedOption = $derived(selectedIndex >= 0 ? options[selectedIndex] : undefined);
100
+ const triggerName = $derived(selectedOption === undefined ? label : `${label}, ${getLabel(selectedOption)}`);
99
101
  const typeahead = createTypeahead();
100
102
 
101
103
  const triggerEl = () => root?.querySelector<HTMLElement>('.rs-trigger') ?? null;
@@ -150,6 +152,7 @@
150
152
  if (key !== value) {
151
153
  value = key;
152
154
  onchange?.(key, next);
155
+ if (!closeAfter) announcement = getLabel(next);
153
156
  playSound(closeAfter ? 'click' : 'tick', sound);
154
157
  }
155
158
  if (closeAfter) {
@@ -354,6 +357,7 @@
354
357
  onopen={onOpen}
355
358
  onclose={onClose}
356
359
  aria-controls={listId}
360
+ aria-labelledby={nameId}
357
361
  aria-activedescendant={open && active >= 0 ? optionId(active) : undefined}
358
362
  >
359
363
  {#snippet trigger()}
@@ -407,6 +411,7 @@
407
411
  <div class="rs-footer">{@render footer({ close })}</div>
408
412
  {/if}
409
413
  </Popover>
414
+ <span class="sr-only" id={nameId}>{triggerName}</span>
410
415
  <span class="sr-only" aria-live="polite">{announcement}</span>
411
416
  </div>
412
417
 
@@ -487,8 +492,10 @@
487
492
  .rich-select.is-open .rs-chevron {
488
493
  rotate: 180deg;
489
494
  }
490
- .rich-select :global(.pop-panel.rs-panel) {
495
+ .rich-select :global(.pop-panel.rs-panel:popover-open) {
491
496
  display: flex;
497
+ }
498
+ .rich-select :global(.pop-panel.rs-panel) {
492
499
  flex-direction: column;
493
500
  gap: var(--sp-1);
494
501
  box-sizing: border-box;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.82.0",
3
+ "version": "0.82.1",
4
4
  "description": "Minimal, dependency-free Svelte 5 + pure-CSS UI kit. Token-driven atoms, molecules & layouts with theming out of the box.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -72,7 +72,8 @@
72
72
  "design": "npm run package && npm run design:bundle && npm run design:verify",
73
73
  "design:bundle": "node .design-sync/build-bundle.mjs",
74
74
  "design:verify": "node .design-sync/verify-bundle.mjs",
75
- "design:clean": "rm -rf ds-bundle .ds-proto"
75
+ "design:clean": "rm -rf ds-bundle .ds-proto",
76
+ "demo:props": "node scripts/gen-demo-props.mjs"
76
77
  },
77
78
  "peerDependencies": {
78
79
  "svelte": "^5.0.0"