@scalewing/react 1.8.0 → 1.9.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/README.md +69 -2
  3. package/dist/anchored-position.d.ts +28 -0
  4. package/dist/anchored-position.js +17 -0
  5. package/dist/calendar-keys.d.ts +19 -0
  6. package/dist/calendar-keys.js +39 -0
  7. package/dist/calendar-labels.d.ts +12 -0
  8. package/dist/calendar-labels.js +32 -0
  9. package/dist/calendar-month.d.ts +36 -0
  10. package/dist/calendar-month.js +111 -0
  11. package/dist/calendar-years.d.ts +14 -0
  12. package/dist/calendar-years.js +20 -0
  13. package/dist/components/Accordion.d.ts +6 -1
  14. package/dist/components/Accordion.js +12 -3
  15. package/dist/components/ActionBar.d.ts +30 -0
  16. package/dist/components/ActionBar.js +13 -0
  17. package/dist/components/Box.d.ts +7 -3
  18. package/dist/components/Box.js +2 -2
  19. package/dist/components/DateField.d.ts +15 -1
  20. package/dist/components/DateField.js +58 -18
  21. package/dist/components/Field.d.ts +5 -1
  22. package/dist/components/Field.js +16 -4
  23. package/dist/components/FieldAdornment.d.ts +20 -0
  24. package/dist/components/FieldAdornment.js +34 -0
  25. package/dist/components/Grid.d.ts +4 -1
  26. package/dist/components/Grid.js +2 -2
  27. package/dist/components/Progress.d.ts +3 -1
  28. package/dist/components/Progress.js +2 -2
  29. package/dist/components/date-field/CalendarDialog.d.ts +26 -0
  30. package/dist/components/date-field/CalendarDialog.js +93 -0
  31. package/dist/components/date-field/CalendarGrid.d.ts +23 -0
  32. package/dist/components/date-field/CalendarGrid.js +36 -0
  33. package/dist/components/date-field/CalendarHeader.d.ts +18 -0
  34. package/dist/components/date-field/CalendarHeader.js +33 -0
  35. package/dist/components/date-field/DateFieldGlyph.d.ts +9 -0
  36. package/dist/components/date-field/DateFieldGlyph.js +15 -0
  37. package/dist/components/date-field/use-anchored-popover.d.ts +8 -0
  38. package/dist/components/date-field/use-anchored-popover.js +61 -0
  39. package/dist/components/date-field/use-date-entry.d.ts +20 -0
  40. package/dist/components/date-field/use-date-entry.js +61 -0
  41. package/dist/components/date-field/use-lang-locale.d.ts +7 -0
  42. package/dist/components/date-field/use-lang-locale.js +17 -0
  43. package/dist/css/css-accordion.js +55 -8
  44. package/dist/css/css-action-bar.d.ts +4 -0
  45. package/dist/css/css-action-bar.js +99 -0
  46. package/dist/css/css-action-menu.js +2 -1
  47. package/dist/css/css-box-border.d.ts +5 -0
  48. package/dist/css/css-box-border.js +24 -0
  49. package/dist/css/css-chrome.js +2 -1
  50. package/dist/css/css-components.js +10 -6
  51. package/dist/css/css-data.js +2 -1
  52. package/dist/css/css-date-field.js +241 -11
  53. package/dist/css/css-denomination-grid.js +2 -1
  54. package/dist/css/css-document.d.ts +2 -0
  55. package/dist/css/css-document.js +2 -1
  56. package/dist/css/css-field.js +61 -1
  57. package/dist/css/css-select.js +2 -1
  58. package/dist/css/css-tooltip.js +2 -1
  59. package/dist/css/stacking.d.ts +10 -0
  60. package/dist/css/stacking.js +47 -0
  61. package/dist/css/stylesheet.js +4 -0
  62. package/dist/date-entry-validity.d.ts +10 -0
  63. package/dist/date-entry-validity.js +16 -0
  64. package/dist/date-entry.d.ts +32 -0
  65. package/dist/date-entry.js +106 -0
  66. package/dist/date-field-labels.d.ts +38 -0
  67. package/dist/date-field-labels.js +22 -0
  68. package/dist/date-field-locale.d.ts +8 -0
  69. package/dist/date-field-locale.js +27 -0
  70. package/dist/date-formatters.d.ts +13 -0
  71. package/dist/date-formatters.js +28 -0
  72. package/dist/date-only.d.ts +23 -0
  73. package/dist/date-only.js +63 -9
  74. package/dist/index.d.ts +5 -3
  75. package/dist/index.js +3 -2
  76. package/dist/styles.css +388 -21
  77. package/package.json +1 -1
@@ -0,0 +1,9 @@
1
+ export type DateFieldGlyphName = 'calendar' | 'previous' | 'next';
2
+ /**
3
+ * Private control chrome for DateField. Stroke, size, and color come from
4
+ * the generated `sw-date-field-glyph` rule; the chevrons mirror in
5
+ * right-to-left text.
6
+ */
7
+ export declare function DateFieldGlyph({ name }: {
8
+ name: DateFieldGlyphName;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from '../../class-names.js';
3
+ const PATHS = {
4
+ calendar: 'M4 6.5A2.5 2.5 0 0 1 6.5 4h11A2.5 2.5 0 0 1 20 6.5v11a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 4 17.5zM4 10h16M8.5 2.5v3M15.5 2.5v3',
5
+ previous: 'M14.5 6l-6 6 6 6',
6
+ next: 'M9.5 6l6 6-6 6',
7
+ };
8
+ /**
9
+ * Private control chrome for DateField. Stroke, size, and color come from
10
+ * the generated `sw-date-field-glyph` rule; the chevrons mirror in
11
+ * right-to-left text.
12
+ */
13
+ export function DateFieldGlyph({ name }) {
14
+ return (_jsx("svg", { "aria-hidden": "true", className: cx('sw-date-field-glyph', name !== 'calendar' && 'sw-date-field-glyph-directional'), focusable: "false", viewBox: "0 0 24 24", children: _jsx("path", { d: PATHS[name] }) }));
15
+ }
@@ -0,0 +1,8 @@
1
+ import { type RefObject } from 'react';
2
+ /**
3
+ * Shows a mounted element on the top layer as a manual popover, keeps it
4
+ * beside `anchorRef` through scroll and resize, and calls `onDismiss` for a
5
+ * press outside it and outside `ignoreRef` (the button that toggles it).
6
+ * Browsers without the popover API get the same fixed placement.
7
+ */
8
+ export declare function useAnchoredPopover(popoverRef: RefObject<HTMLElement | null>, anchorRef: RefObject<HTMLElement | null>, ignoreRef: RefObject<HTMLElement | null>, onDismiss: () => void): void;
@@ -0,0 +1,61 @@
1
+ import { spacingScale } from '@scalewing/tokens';
2
+ import { useLayoutEffect, useRef } from 'react';
3
+ import { anchoredPosition } from '../../anchored-position.js';
4
+ function placePopover(node, anchor) {
5
+ const position = anchoredPosition(anchor.getBoundingClientRect(), { width: node.offsetWidth, height: node.offsetHeight }, { width: window.innerWidth, height: window.innerHeight }, {
6
+ gap: spacingScale[1],
7
+ inset: spacingScale[2],
8
+ rtl: getComputedStyle(anchor).direction === 'rtl',
9
+ });
10
+ node.style.left = `${position.left}px`;
11
+ node.style.top = `${position.top}px`;
12
+ }
13
+ /**
14
+ * Shows a mounted element on the top layer as a manual popover, keeps it
15
+ * beside `anchorRef` through scroll and resize, and calls `onDismiss` for a
16
+ * press outside it and outside `ignoreRef` (the button that toggles it).
17
+ * Browsers without the popover API get the same fixed placement.
18
+ */
19
+ export function useAnchoredPopover(popoverRef, anchorRef, ignoreRef, onDismiss) {
20
+ const dismissRef = useRef(onDismiss);
21
+ useLayoutEffect(() => {
22
+ dismissRef.current = onDismiss;
23
+ });
24
+ useLayoutEffect(() => {
25
+ const node = popoverRef.current;
26
+ const anchor = anchorRef.current;
27
+ if (!node || !anchor)
28
+ return;
29
+ const supportsPopover = typeof node.showPopover === 'function';
30
+ // Set only where supported: an unsupported runtime would hide a
31
+ // `[popover]` element that it can never show.
32
+ if (supportsPopover) {
33
+ node.setAttribute('popover', 'manual');
34
+ if (!node.matches(':popover-open'))
35
+ node.showPopover();
36
+ }
37
+ placePopover(node, anchor);
38
+ function onPointerDown(event) {
39
+ const target = event.target;
40
+ if (!(target instanceof Node))
41
+ return;
42
+ if (node?.contains(target) || ignoreRef.current?.contains(target))
43
+ return;
44
+ dismissRef.current();
45
+ }
46
+ function onMove() {
47
+ if (node && anchor)
48
+ placePopover(node, anchor);
49
+ }
50
+ document.addEventListener('pointerdown', onPointerDown);
51
+ window.addEventListener('resize', onMove);
52
+ window.addEventListener('scroll', onMove, true);
53
+ return () => {
54
+ document.removeEventListener('pointerdown', onPointerDown);
55
+ window.removeEventListener('resize', onMove);
56
+ window.removeEventListener('scroll', onMove, true);
57
+ if (supportsPopover && node.matches(':popover-open'))
58
+ node.hidePopover();
59
+ };
60
+ }, [anchorRef, ignoreRef, popoverRef]);
61
+ }
@@ -0,0 +1,20 @@
1
+ import { type DateEntryPattern } from '../../date-entry.js';
2
+ export type DateEntry = {
3
+ text: string;
4
+ /** The date the text forms: YYYY-MM-DD, '' when blank, null when not one. */
5
+ parsed: string | null;
6
+ /** Typed text that is not a date, after the person left the field. */
7
+ invalid: boolean;
8
+ change: (text: string) => void;
9
+ check: () => void;
10
+ reset: () => void;
11
+ };
12
+ /**
13
+ * Typed entry over a controlled date-only value. A keystroke that finishes
14
+ * a date (see `isCompleteDateEntry`) or blanks the text calls `onCommit`;
15
+ * a date whose last field may still grow commits on `check` (blur or
16
+ * Enter). Text that is not (yet) a date stays on screen and leaves the value
17
+ * alone. The text also stays when the parent keeps the old value; when
18
+ * `value` changes from outside to any other date, the text follows it.
19
+ */
20
+ export declare function useDateEntry(value: string, pattern: DateEntryPattern, onCommit: (value: string) => void): DateEntry;
@@ -0,0 +1,61 @@
1
+ import { useState } from 'react';
2
+ import { formatDateEntry, isCompleteDateEntry, parseDateEntry, } from '../../date-entry.js';
3
+ /**
4
+ * Typed entry over a controlled date-only value. A keystroke that finishes
5
+ * a date (see `isCompleteDateEntry`) or blanks the text calls `onCommit`;
6
+ * a date whose last field may still grow commits on `check` (blur or
7
+ * Enter). Text that is not (yet) a date stays on screen and leaves the value
8
+ * alone. The text also stays when the parent keeps the old value; when
9
+ * `value` changes from outside to any other date, the text follows it.
10
+ */
11
+ export function useDateEntry(value, pattern, onCommit) {
12
+ const [draft, setDraft] = useState(null);
13
+ const current = draft !== null && (draft.value === value || draft.committed === value)
14
+ ? draft
15
+ : null;
16
+ // The parent adopted the commit: tie the text to it, so a later outside
17
+ // change back to the old value replaces the text.
18
+ if (current !== null && current.value !== value)
19
+ setDraft({ ...current, value, committed: null });
20
+ const text = current ? current.text : formatDateEntry(value, pattern);
21
+ const parsed = parseDateEntry(text, pattern);
22
+ function change(next) {
23
+ const nextValue = parseDateEntry(next, pattern);
24
+ const ready = nextValue === '' ||
25
+ (nextValue !== null && isCompleteDateEntry(next, pattern));
26
+ const committed = ready && nextValue !== value ? nextValue : null;
27
+ setDraft({ text: next, value, committed, checked: false });
28
+ if (committed !== null)
29
+ onCommit(committed);
30
+ }
31
+ function check() {
32
+ if (current === null)
33
+ return;
34
+ if (parsed === null) {
35
+ setDraft({ ...current, checked: true });
36
+ return;
37
+ }
38
+ if (parsed === value) {
39
+ // Show the value in the locale's padded form.
40
+ setDraft(null);
41
+ return;
42
+ }
43
+ // Commit once; a parent that keeps the old value keeps the padded text.
44
+ if (parsed !== current.committed)
45
+ onCommit(parsed);
46
+ setDraft({
47
+ text: formatDateEntry(parsed, pattern),
48
+ value,
49
+ committed: parsed,
50
+ checked: false,
51
+ });
52
+ }
53
+ return {
54
+ text,
55
+ parsed,
56
+ invalid: current !== null && current.checked && parsed === null,
57
+ change,
58
+ check,
59
+ reset: () => setDraft(null),
60
+ };
61
+ }
@@ -0,0 +1,7 @@
1
+ import { type RefObject } from 'react';
2
+ /**
3
+ * The `locale` prop when given; otherwise the `lang` of the nearest ancestor
4
+ * of `ref`, read when the field mounts, then en-US. A product that switches
5
+ * language without remounting passes `locale`.
6
+ */
7
+ export declare function useLangLocale(locale: string | undefined, ref: RefObject<HTMLElement | null>): string;
@@ -0,0 +1,17 @@
1
+ import { useLayoutEffect, useState } from 'react';
2
+ import { resolveDateLocale } from '../../date-field-locale.js';
3
+ /**
4
+ * The `locale` prop when given; otherwise the `lang` of the nearest ancestor
5
+ * of `ref`, read when the field mounts, then en-US. A product that switches
6
+ * language without remounting passes `locale`.
7
+ */
8
+ export function useLangLocale(locale, ref) {
9
+ const [lang, setLang] = useState(null);
10
+ useLayoutEffect(() => {
11
+ if (locale !== undefined)
12
+ return;
13
+ const nearest = ref.current?.closest('[lang]')?.getAttribute('lang');
14
+ setLang(nearest ?? null);
15
+ }, [locale, ref]);
16
+ return resolveDateLocale(locale, lang);
17
+ }
@@ -1,3 +1,33 @@
1
+ function markerRules() {
2
+ return `/* A token chevron replaces the user-agent triangle: it points to the inline end when closed and down when open. */
3
+ .sw-accordion-summary::-webkit-details-marker { display: none; }
4
+
5
+ .sw-accordion-marker {
6
+ border-bottom: 2px solid var(--sw-color-muted);
7
+ border-right: 2px solid var(--sw-color-muted);
8
+ box-sizing: border-box;
9
+ flex: none;
10
+ height: var(--sw-space-2);
11
+ margin-inline-end: var(--sw-space-1);
12
+ transform: rotate(-45deg);
13
+ transition: transform var(--sw-motion-default) var(--sw-motion-easing);
14
+ width: var(--sw-space-2);
15
+ }
16
+
17
+ .sw-accordion-marker:dir(rtl) { transform: rotate(135deg); }
18
+
19
+ .sw-accordion[open] > .sw-accordion-summary .sw-accordion-marker {
20
+ transform: rotate(45deg);
21
+ }
22
+
23
+ @media (prefers-reduced-motion: reduce) {
24
+ .sw-accordion-marker { transition: none; }
25
+ }
26
+
27
+ @media (forced-colors: active) {
28
+ .sw-accordion-marker { border-color: CanvasText; }
29
+ }`;
30
+ }
1
31
  export function cssAccordionClasses() {
2
32
  return `.sw-accordion {
3
33
  background: var(--sw-glass-fill);
@@ -9,25 +39,42 @@ export function cssAccordionClasses() {
9
39
  color: var(--sw-color-text);
10
40
  }
11
41
 
42
+ /* A nested disclosure sits inside a larger surface, so its corners are smaller. */
43
+ .sw-accordion-sm { border-radius: var(--sw-radius-md); }
44
+
12
45
  .sw-accordion-summary {
46
+ align-items: center;
47
+ box-sizing: border-box;
13
48
  cursor: pointer;
14
- display: list-item;
49
+ display: flex;
50
+ gap: var(--sw-space-3);
51
+ list-style: none;
15
52
  min-height: var(--sw-control-md-min-height);
16
53
  }
17
54
 
18
- .sw-accordion-summary::-webkit-details-marker {
19
- color: var(--sw-color-muted);
20
- }
55
+ /* A nested header is an sm control, still a comfortable pointer and touch target. */
56
+ .sw-accordion-sm > .sw-accordion-summary { min-height: var(--sw-control-sm-min-height); }
21
57
 
22
- .sw-accordion-summary::marker {
23
- color: var(--sw-color-muted);
58
+ .sw-accordion-heading {
59
+ display: flex;
60
+ flex: 1 1 auto;
61
+ flex-direction: column;
62
+ min-width: 0;
24
63
  }
25
64
 
26
65
  .sw-accordion-summary:focus-visible {
27
66
  outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
28
67
  outline-offset: var(--sw-focus-ring-offset);
29
- }`;
68
+ }
69
+
70
+ ${markerRules()}`;
30
71
  }
31
72
  export function accordionClassCatalog() {
32
- return ['sw-accordion', 'sw-accordion-summary'];
73
+ return [
74
+ 'sw-accordion',
75
+ 'sw-accordion-sm',
76
+ 'sw-accordion-summary',
77
+ 'sw-accordion-heading',
78
+ 'sw-accordion-marker',
79
+ ];
33
80
  }
@@ -0,0 +1,4 @@
1
+ import { type Breakpoint } from './breakpoints.js';
2
+ export declare function actionBarStickyClass(stickyBelow: Breakpoint | undefined): string;
3
+ export declare function cssActionBarClasses(): string;
4
+ export declare function actionBarClassCatalog(): string[];
@@ -0,0 +1,99 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery, breakpoints, } from './breakpoints.js';
3
+ import { zIndex } from './stacking.js';
4
+ const caption = typographyVariants.caption;
5
+ /* The bar floats a step above the bottom edge and clears the home indicator. */
6
+ function stuckToBottom(indent) {
7
+ return [
8
+ 'bottom: calc(var(--sw-space-2) + env(safe-area-inset-bottom, 0px));',
9
+ 'position: sticky;',
10
+ zIndex('bottomChrome'),
11
+ ]
12
+ .map((declaration) => `${indent}${declaration}`)
13
+ .join('\n');
14
+ }
15
+ export function actionBarStickyClass(stickyBelow) {
16
+ return stickyBelow
17
+ ? `sw-action-bar-sticky-below-${stickyBelow}`
18
+ : 'sw-action-bar-sticky';
19
+ }
20
+ function stickyRules() {
21
+ const below = breakpoints
22
+ .map((breakpoint) => `@media ${breakpointQuery('below', breakpoint)} {
23
+ .${actionBarStickyClass(breakpoint)} {
24
+ ${stuckToBottom(' ')}
25
+ }
26
+ }`)
27
+ .join('\n\n');
28
+ return `.sw-action-bar-sticky {
29
+ ${stuckToBottom(' ')}
30
+ }
31
+
32
+ ${below}`;
33
+ }
34
+ export function cssActionBarClasses() {
35
+ return `.sw-action-bar {
36
+ align-items: center;
37
+ background: var(--sw-glass-fill);
38
+ border: 1px solid var(--sw-glass-border);
39
+ border-radius: var(--sw-radius-lg);
40
+ box-shadow: inset 0 1px 0 var(--sw-glass-specular);
41
+ backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
42
+ -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
43
+ box-sizing: border-box;
44
+ color: var(--sw-color-text);
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ gap: var(--sw-space-2) var(--sw-space-4);
48
+ padding-block: var(--sw-space-3);
49
+ padding-left: max(var(--sw-space-4), env(safe-area-inset-left, 0px));
50
+ padding-right: max(var(--sw-space-4), env(safe-area-inset-right, 0px));
51
+ }
52
+
53
+ .sw-action-bar-status {
54
+ color: var(--sw-color-muted);
55
+ flex: 1 1 auto;
56
+ font-family: var(--sw-font-sans);
57
+ font-size: ${caption.fontSize}px;
58
+ line-height: ${caption.lineHeight}px;
59
+ margin: 0;
60
+ min-width: 0;
61
+ }
62
+
63
+ /* With no status the live region stays in the page, so a first status is
64
+ still announced, but it takes no room in the bar. */
65
+ .sw-action-bar-status:empty {
66
+ clip-path: inset(50%);
67
+ height: 1px;
68
+ overflow: hidden;
69
+ position: absolute;
70
+ width: 1px;
71
+ }
72
+
73
+ .sw-action-bar-actions {
74
+ align-items: center;
75
+ display: flex;
76
+ flex: 0 1 auto;
77
+ flex-wrap: wrap;
78
+ gap: var(--sw-space-2);
79
+ justify-content: flex-end;
80
+ margin-inline-start: auto;
81
+ }
82
+
83
+ @media ${breakpointQuery('below', 'md')} {
84
+ .sw-action-bar-status,
85
+ .sw-action-bar-actions { flex-basis: 100%; }
86
+ .sw-action-bar-actions > * { flex: 1 1 auto; }
87
+ }
88
+
89
+ ${stickyRules()}`;
90
+ }
91
+ export function actionBarClassCatalog() {
92
+ return [
93
+ 'sw-action-bar',
94
+ 'sw-action-bar-status',
95
+ 'sw-action-bar-actions',
96
+ 'sw-action-bar-sticky',
97
+ ...breakpoints.map((breakpoint) => actionBarStickyClass(breakpoint)),
98
+ ];
99
+ }
@@ -1,4 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { zIndex } from './stacking.js';
2
3
  const label = typographyVariants.label;
3
4
  export function cssActionMenuClasses() {
4
5
  return `.sw-action-menu {
@@ -45,7 +46,7 @@ export function cssActionMenuClasses() {
45
46
  overflow: auto;
46
47
  padding: var(--sw-space-1);
47
48
  position: fixed;
48
- z-index: 10;
49
+ ${zIndex('popup')}
49
50
  }
50
51
 
51
52
  .sw-action-menu-list[hidden] {
@@ -0,0 +1,5 @@
1
+ export type BoxBorderValue = boolean | 'dashed';
2
+ /** The generated class for a Box `border` prop, or none for `false`. */
3
+ export declare function boxBorderClass(border: BoxBorderValue | undefined): string | undefined;
4
+ export declare function cssBoxBorderClasses(): string;
5
+ export declare function boxBorderClassCatalog(): string[];
@@ -0,0 +1,24 @@
1
+ const borderStyles = ['solid', 'dashed'];
2
+ function borderClass(style) {
3
+ return style === 'solid' ? 'sw-border' : `sw-border-${style}`;
4
+ }
5
+ /** The generated class for a Box `border` prop, or none for `false`. */
6
+ export function boxBorderClass(border) {
7
+ if (!border)
8
+ return undefined;
9
+ return borderClass(border === 'dashed' ? 'dashed' : 'solid');
10
+ }
11
+ /*
12
+ * A Box `border` is a token hairline. These rules are emitted after every
13
+ * component rule so that, at equal specificity, the prop still wins over a
14
+ * Box-based component's own frame. A consumer `style` wins over both. In
15
+ * forced colors the system border color replaces the token and the style stays.
16
+ */
17
+ export function cssBoxBorderClasses() {
18
+ return borderStyles
19
+ .map((style) => `.${borderClass(style)} { border: 1px ${style} var(--sw-color-border); }`)
20
+ .join('\n');
21
+ }
22
+ export function boxBorderClassCatalog() {
23
+ return borderStyles.map(borderClass);
24
+ }
@@ -1,4 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { zIndex } from './stacking.js';
2
3
  const caption = typographyVariants.caption;
3
4
  const label = typographyVariants.label;
4
5
  export function cssChromeClasses() {
@@ -14,7 +15,7 @@ export function cssChromeClasses() {
14
15
  .sw-app-header-sticky {
15
16
  position: sticky;
16
17
  top: 0;
17
- z-index: 2;
18
+ ${zIndex('topChrome')}
18
19
  }
19
20
 
20
21
  .sw-nav {
@@ -1,5 +1,6 @@
1
1
  import { compactTypographyVariants, typographyVariants, } from '@scalewing/tokens';
2
2
  import { breakpointQuery } from './breakpoints.js';
3
+ import { cssActionBarClasses } from './css-action-bar.js';
3
4
  import { cssActionMenuClasses } from './css-action-menu.js';
4
5
  import { cssAccordionClasses } from './css-accordion.js';
5
6
  import { cssButtonClasses } from './css-button.js';
@@ -26,6 +27,8 @@ import { cssGridSpanClasses } from './css-grid-span.js';
26
27
  import { cssDenominationGridClasses } from './css-denomination-grid.js';
27
28
  import { cssFieldClasses } from './css-field.js';
28
29
  import { cssToastClasses } from './css-toast.js';
30
+ import { cssBoxBorderClasses } from './css-box-border.js';
31
+ import { cssPopupHostRules } from './stacking.js';
29
32
  /*
30
33
  * Reduce Transparency swaps every glass surface for the solid surface. It is
31
34
  * emitted after every component rule: at equal specificity the later rule wins,
@@ -35,6 +38,7 @@ function reducedTransparencyRules() {
35
38
  return `@media (prefers-reduced-transparency: reduce) {
36
39
  .sw-card-glass,
37
40
  .sw-app-header,
41
+ .sw-action-bar,
38
42
  .sw-badge-neutral,
39
43
  .sw-segmented,
40
44
  .sw-table-sticky thead th,
@@ -45,6 +49,7 @@ function reducedTransparencyRules() {
45
49
  .sw-accordion,
46
50
  .sw-select-list,
47
51
  .sw-action-menu-list,
52
+ .sw-date-field-calendar,
48
53
  .sw-toast {
49
54
  background: var(--sw-color-surface);
50
55
  backdrop-filter: none;
@@ -82,12 +87,7 @@ export function cssComponentClasses() {
82
87
  -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
83
88
  }
84
89
 
85
- /* Backdrop blur makes a glass surface its own stacking context, so an open popup inside it would paint under the next surface. */
86
- .sw-card-glass:has(.sw-select-list, .sw-action-menu-list:not([hidden]), .sw-tooltip:not([hidden])),
87
- .sw-accordion:has(.sw-select-list, .sw-action-menu-list:not([hidden]), .sw-tooltip:not([hidden])) {
88
- position: relative;
89
- z-index: 1;
90
- }
90
+ ${cssPopupHostRules()}
91
91
 
92
92
  .sw-card-outlined {
93
93
  background: var(--sw-color-surface);
@@ -145,6 +145,8 @@ ${cssSelectClasses()}
145
145
 
146
146
  ${cssActionMenuClasses()}
147
147
 
148
+ ${cssActionBarClasses()}
149
+
148
150
  ${cssSwitchClasses()}
149
151
 
150
152
  ${cssSpinnerClasses()}
@@ -173,5 +175,7 @@ ${cssToastClasses()}
173
175
 
174
176
  ${cssResponsiveClasses()}
175
177
 
178
+ ${cssBoxBorderClasses()}
179
+
176
180
  ${reducedTransparencyRules()}`;
177
181
  }
@@ -1,5 +1,6 @@
1
1
  import { trackInset, typographyVariants } from '@scalewing/tokens';
2
2
  import { scrollRegionRules } from './css-scroll-region.js';
3
+ import { zIndex } from './stacking.js';
3
4
  export const badgeTones = [
4
5
  'neutral',
5
6
  'accent',
@@ -180,7 +181,7 @@ ${scrollRegionRules('.sw-table-wrap')}
180
181
  -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
181
182
  position: sticky;
182
183
  top: 0;
183
- z-index: 1;
184
+ ${zIndex('stickyCell')}
184
185
  }
185
186
 
186
187
  .sw-table-compact th,