@scalewing/react 1.8.0 → 1.10.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 (85) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +93 -2
  3. package/dist/anchored-position.d.ts +28 -0
  4. package/dist/anchored-position.js +17 -0
  5. package/dist/calendar-button-labels.d.ts +19 -0
  6. package/dist/calendar-button-labels.js +10 -0
  7. package/dist/calendar-keys.d.ts +19 -0
  8. package/dist/calendar-keys.js +39 -0
  9. package/dist/calendar-labels.d.ts +19 -0
  10. package/dist/calendar-labels.js +41 -0
  11. package/dist/calendar-month.d.ts +38 -0
  12. package/dist/calendar-month.js +116 -0
  13. package/dist/calendar-years.d.ts +14 -0
  14. package/dist/calendar-years.js +20 -0
  15. package/dist/components/Accordion.d.ts +6 -1
  16. package/dist/components/Accordion.js +12 -3
  17. package/dist/components/ActionBar.d.ts +30 -0
  18. package/dist/components/ActionBar.js +13 -0
  19. package/dist/components/Box.d.ts +7 -3
  20. package/dist/components/Box.js +2 -2
  21. package/dist/components/CalendarButton.d.ts +43 -0
  22. package/dist/components/CalendarButton.js +47 -0
  23. package/dist/components/DateField.d.ts +15 -1
  24. package/dist/components/DateField.js +47 -18
  25. package/dist/components/Field.d.ts +5 -1
  26. package/dist/components/Field.js +16 -4
  27. package/dist/components/FieldAdornment.d.ts +20 -0
  28. package/dist/components/FieldAdornment.js +34 -0
  29. package/dist/components/Grid.d.ts +4 -1
  30. package/dist/components/Grid.js +2 -2
  31. package/dist/components/Progress.d.ts +3 -1
  32. package/dist/components/Progress.js +2 -2
  33. package/dist/components/date-field/CalendarDialog.d.ts +26 -0
  34. package/dist/components/date-field/CalendarDialog.js +93 -0
  35. package/dist/components/date-field/CalendarGrid.d.ts +23 -0
  36. package/dist/components/date-field/CalendarGrid.js +36 -0
  37. package/dist/components/date-field/CalendarHeader.d.ts +18 -0
  38. package/dist/components/date-field/CalendarHeader.js +33 -0
  39. package/dist/components/date-field/DateFieldGlyph.d.ts +9 -0
  40. package/dist/components/date-field/DateFieldGlyph.js +15 -0
  41. package/dist/components/date-field/use-anchored-popover.d.ts +8 -0
  42. package/dist/components/date-field/use-anchored-popover.js +61 -0
  43. package/dist/components/date-field/use-calendar-popup.d.ts +36 -0
  44. package/dist/components/date-field/use-calendar-popup.js +43 -0
  45. package/dist/components/date-field/use-date-entry.d.ts +20 -0
  46. package/dist/components/date-field/use-date-entry.js +61 -0
  47. package/dist/components/date-field/use-lang-locale.d.ts +7 -0
  48. package/dist/components/date-field/use-lang-locale.js +17 -0
  49. package/dist/css/css-accordion.js +55 -8
  50. package/dist/css/css-action-bar.d.ts +4 -0
  51. package/dist/css/css-action-bar.js +99 -0
  52. package/dist/css/css-action-menu.js +2 -1
  53. package/dist/css/css-box-border.d.ts +5 -0
  54. package/dist/css/css-box-border.js +24 -0
  55. package/dist/css/css-calendar-button.d.ts +2 -0
  56. package/dist/css/css-calendar-button.js +33 -0
  57. package/dist/css/css-chrome.js +2 -1
  58. package/dist/css/css-components.js +13 -6
  59. package/dist/css/css-data.js +2 -1
  60. package/dist/css/css-date-field.js +241 -11
  61. package/dist/css/css-denomination-grid.js +2 -1
  62. package/dist/css/css-document.d.ts +2 -0
  63. package/dist/css/css-document.js +2 -1
  64. package/dist/css/css-field.js +61 -1
  65. package/dist/css/css-select.js +2 -1
  66. package/dist/css/css-tooltip.js +2 -1
  67. package/dist/css/stacking.d.ts +10 -0
  68. package/dist/css/stacking.js +49 -0
  69. package/dist/css/stylesheet.js +6 -0
  70. package/dist/date-entry-validity.d.ts +10 -0
  71. package/dist/date-entry-validity.js +16 -0
  72. package/dist/date-entry.d.ts +32 -0
  73. package/dist/date-entry.js +106 -0
  74. package/dist/date-field-labels.d.ts +38 -0
  75. package/dist/date-field-labels.js +22 -0
  76. package/dist/date-field-locale.d.ts +8 -0
  77. package/dist/date-field-locale.js +27 -0
  78. package/dist/date-formatters.d.ts +13 -0
  79. package/dist/date-formatters.js +28 -0
  80. package/dist/date-only.d.ts +23 -0
  81. package/dist/date-only.js +63 -9
  82. package/dist/index.d.ts +6 -3
  83. package/dist/index.js +4 -2
  84. package/dist/styles.css +410 -21
  85. package/package.json +1 -1
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssCalendarButtonClasses(): string;
2
+ export declare function calendarButtonClassCatalog(): string[];
@@ -0,0 +1,33 @@
1
+ import { buttonSizes } from '@scalewing/tokens';
2
+ /* The comfortable touch target: the md control height, 44 px, both ways. */
3
+ const touchTarget = 'var(--sw-control-md-min-height)';
4
+ /* Square at every Button size: as wide as the size's control height. */
5
+ function squareRules() {
6
+ return buttonSizes
7
+ .map((size) => `.sw-button-${size}.sw-calendar-button { min-width: var(--sw-control-${size}-min-height); }`)
8
+ .join('\n');
9
+ }
10
+ export function cssCalendarButtonClasses() {
11
+ return `.sw-button.sw-calendar-button {
12
+ flex: none;
13
+ padding-inline: 0;
14
+ }
15
+
16
+ ${squareRules()}
17
+
18
+ .sw-calendar-button:not(.sw-button-md) .sw-date-field-glyph {
19
+ height: var(--sw-space-4);
20
+ width: var(--sw-space-4);
21
+ }
22
+
23
+ /* A coarse pointer gets the full touch target at the smaller sizes too. */
24
+ @media (pointer: coarse) {
25
+ .sw-button.sw-calendar-button {
26
+ min-height: ${touchTarget};
27
+ min-width: ${touchTarget};
28
+ }
29
+ }`;
30
+ }
31
+ export function calendarButtonClassCatalog() {
32
+ return ['sw-calendar-button'];
33
+ }
@@ -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,8 +1,10 @@
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';
7
+ import { cssCalendarButtonClasses } from './css-calendar-button.js';
6
8
  import { cssChartClasses } from './css-chart.js';
7
9
  import { cssCheckboxClasses } from './css-checkbox.js';
8
10
  import { cssRadioGroupClasses } from './css-radio-group.js';
@@ -26,6 +28,8 @@ import { cssGridSpanClasses } from './css-grid-span.js';
26
28
  import { cssDenominationGridClasses } from './css-denomination-grid.js';
27
29
  import { cssFieldClasses } from './css-field.js';
28
30
  import { cssToastClasses } from './css-toast.js';
31
+ import { cssBoxBorderClasses } from './css-box-border.js';
32
+ import { cssPopupHostRules } from './stacking.js';
29
33
  /*
30
34
  * Reduce Transparency swaps every glass surface for the solid surface. It is
31
35
  * emitted after every component rule: at equal specificity the later rule wins,
@@ -35,6 +39,7 @@ function reducedTransparencyRules() {
35
39
  return `@media (prefers-reduced-transparency: reduce) {
36
40
  .sw-card-glass,
37
41
  .sw-app-header,
42
+ .sw-action-bar,
38
43
  .sw-badge-neutral,
39
44
  .sw-segmented,
40
45
  .sw-table-sticky thead th,
@@ -45,6 +50,7 @@ function reducedTransparencyRules() {
45
50
  .sw-accordion,
46
51
  .sw-select-list,
47
52
  .sw-action-menu-list,
53
+ .sw-date-field-calendar,
48
54
  .sw-toast {
49
55
  background: var(--sw-color-surface);
50
56
  backdrop-filter: none;
@@ -82,12 +88,7 @@ export function cssComponentClasses() {
82
88
  -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
83
89
  }
84
90
 
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
- }
91
+ ${cssPopupHostRules()}
91
92
 
92
93
  .sw-card-outlined {
93
94
  background: var(--sw-color-surface);
@@ -133,6 +134,8 @@ ${cssDataClasses()}
133
134
 
134
135
  ${cssDateFieldClasses()}
135
136
 
137
+ ${cssCalendarButtonClasses()}
138
+
136
139
  ${cssChartClasses()}
137
140
 
138
141
  ${cssChromeClasses()}
@@ -145,6 +148,8 @@ ${cssSelectClasses()}
145
148
 
146
149
  ${cssActionMenuClasses()}
147
150
 
151
+ ${cssActionBarClasses()}
152
+
148
153
  ${cssSwitchClasses()}
149
154
 
150
155
  ${cssSpinnerClasses()}
@@ -173,5 +178,7 @@ ${cssToastClasses()}
173
178
 
174
179
  ${cssResponsiveClasses()}
175
180
 
181
+ ${cssBoxBorderClasses()}
182
+
176
183
  ${reducedTransparencyRules()}`;
177
184
  }
@@ -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,
@@ -1,8 +1,15 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { zIndex } from './stacking.js';
2
3
  const body = typographyVariants.body;
3
4
  const label = typographyVariants.label;
4
5
  const caption = typographyVariants.caption;
5
- export function cssDateFieldClasses() {
6
+ /* The calendar button, month steps, and days are one square control wide. */
7
+ const square = 'var(--sw-control-md-min-height)';
8
+ /* Wide enough for "MM/DD/YYYY" in the system sans. */
9
+ const entryWidth = '12ch';
10
+ /* Outranks the canvas rule for text inputs so the button keeps its room. */
11
+ const input = '.sw-date-field .sw-date-field-control > .sw-date-field-input';
12
+ function fieldRules() {
6
13
  return `.sw-date-field {
7
14
  display: flex;
8
15
  flex-direction: column;
@@ -20,7 +27,15 @@ export function cssDateFieldClasses() {
20
27
  line-height: ${label.lineHeight}px;
21
28
  }
22
29
 
23
- .sw-date-field-input {
30
+ .sw-date-field-control {
31
+ align-self: flex-start;
32
+ display: flex;
33
+ max-width: 100%;
34
+ min-width: 0;
35
+ position: relative;
36
+ }
37
+
38
+ ${input} {
24
39
  background: var(--sw-glass-fill);
25
40
  border: 1px solid var(--sw-color-border);
26
41
  border-radius: var(--sw-radius-sm);
@@ -28,26 +43,78 @@ export function cssDateFieldClasses() {
28
43
  color: var(--sw-color-text);
29
44
  font-family: var(--sw-font-sans);
30
45
  font-size: ${body.fontSize}px;
46
+ font-variant-numeric: tabular-nums;
47
+ margin: 0;
48
+ max-width: 100%;
31
49
  min-height: var(--sw-control-md-min-height);
32
50
  min-width: 0;
33
- padding-inline: var(--sw-control-md-padding-inline);
34
- width: 100%;
51
+ padding-inline: var(--sw-control-md-padding-inline) ${square};
52
+ width: calc(${entryWidth} + var(--sw-control-md-padding-inline) + ${square});
53
+ }
54
+
55
+ ${input}::placeholder {
56
+ color: var(--sw-color-muted);
57
+ opacity: 1;
58
+ }
59
+
60
+ ${input}:focus {
61
+ box-shadow: none;
62
+ outline: none;
35
63
  }
36
64
 
37
- .sw-date-field-input:focus-visible {
65
+ ${input}:focus-visible {
38
66
  outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
39
67
  outline-offset: var(--sw-focus-ring-offset);
40
68
  }
41
69
 
42
- .sw-date-field-input[aria-invalid='true'] {
70
+ ${input}[aria-invalid='true'] {
43
71
  border-color: var(--sw-color-danger);
44
72
  }
45
73
 
46
- .sw-date-field-input:disabled {
74
+ ${input}:disabled,
75
+ .sw-date-field-button:disabled {
47
76
  cursor: not-allowed;
48
77
  opacity: var(--sw-disabled-opacity);
49
78
  }
50
79
 
80
+ .sw-date-field-button {
81
+ align-items: center;
82
+ appearance: none;
83
+ background: transparent;
84
+ border: 0;
85
+ border-radius: var(--sw-radius-sm);
86
+ color: var(--sw-color-muted);
87
+ cursor: pointer;
88
+ display: flex;
89
+ inset-block: 0;
90
+ inset-inline-end: 0;
91
+ justify-content: center;
92
+ margin: 0;
93
+ padding: 0;
94
+ position: absolute;
95
+ width: ${square};
96
+ }
97
+
98
+ .sw-date-field-button:hover:not(:disabled),
99
+ .sw-date-field-button[aria-expanded='true'] {
100
+ color: var(--sw-color-text);
101
+ }
102
+
103
+ .sw-date-field-glyph {
104
+ fill: none;
105
+ flex: none;
106
+ height: var(--sw-space-5);
107
+ stroke: currentColor;
108
+ stroke-linecap: round;
109
+ stroke-linejoin: round;
110
+ stroke-width: 1.75;
111
+ width: var(--sw-space-5);
112
+ }
113
+
114
+ [dir='rtl'] .sw-date-field-glyph-directional {
115
+ transform: scaleX(-1);
116
+ }
117
+
51
118
  .sw-date-field-description,
52
119
  .sw-date-field-error {
53
120
  font-family: var(--sw-font-sans);
@@ -56,22 +123,185 @@ export function cssDateFieldClasses() {
56
123
  }
57
124
 
58
125
  .sw-date-field-description { color: var(--sw-color-muted); }
59
- .sw-date-field-error { color: var(--sw-color-danger); }
126
+ .sw-date-field-error { color: var(--sw-color-danger); }`;
127
+ }
128
+ function calendarRules() {
129
+ return `.sw-date-field-calendar {
130
+ background: var(--sw-glass-fill);
131
+ border: 1px solid var(--sw-glass-border);
132
+ border-radius: var(--sw-radius-md);
133
+ box-shadow: var(--sw-elevation-md), inset 0 1px 0 var(--sw-glass-specular);
134
+ backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
135
+ -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
136
+ box-sizing: border-box;
137
+ color: var(--sw-color-text);
138
+ display: flex;
139
+ flex-direction: column;
140
+ font-family: var(--sw-font-sans);
141
+ gap: var(--sw-space-2);
142
+ height: auto;
143
+ inset: auto;
144
+ margin: 0;
145
+ max-width: calc(100vw - var(--sw-space-2) - var(--sw-space-2));
146
+ overflow: visible;
147
+ padding: var(--sw-space-3);
148
+ position: fixed;
149
+ width: max-content;
150
+ ${zIndex('popup')}
151
+ }
152
+
153
+ .sw-date-field-header {
154
+ align-items: center;
155
+ display: flex;
156
+ gap: var(--sw-space-1);
157
+ justify-content: space-between;
158
+ }
159
+
160
+ .sw-date-field-jump {
161
+ display: flex;
162
+ gap: var(--sw-space-1);
163
+ min-width: 0;
164
+ }
165
+
166
+ .sw-date-field-step {
167
+ align-items: center;
168
+ appearance: none;
169
+ background: transparent;
170
+ border: 0;
171
+ border-radius: var(--sw-radius-pill);
172
+ color: var(--sw-color-text);
173
+ cursor: pointer;
174
+ display: flex;
175
+ flex: none;
176
+ height: ${square};
177
+ justify-content: center;
178
+ margin: 0;
179
+ padding: 0;
180
+ width: ${square};
181
+ }
182
+
183
+ .sw-date-field-step[aria-disabled='true'] {
184
+ cursor: not-allowed;
185
+ opacity: var(--sw-disabled-opacity);
186
+ }
187
+
188
+ .sw-date-field-grid {
189
+ border-collapse: separate;
190
+ border-spacing: 0;
191
+ table-layout: fixed;
192
+ }
193
+
194
+ .sw-date-field-grid th {
195
+ color: var(--sw-color-muted);
196
+ font-size: ${caption.fontSize}px;
197
+ font-weight: normal;
198
+ line-height: ${caption.lineHeight}px;
199
+ padding: 0 0 var(--sw-space-1);
200
+ text-align: center;
201
+ }
60
202
 
61
- @media (prefers-reduced-transparency: reduce) {
62
- .sw-date-field-input { background: var(--sw-color-surface); }
203
+ .sw-date-field-day {
204
+ border: 1px solid transparent;
205
+ border-radius: var(--sw-radius-pill);
206
+ box-sizing: border-box;
207
+ cursor: pointer;
208
+ font-size: ${label.fontSize}px;
209
+ font-variant-numeric: tabular-nums;
210
+ height: ${square};
211
+ padding: 0;
212
+ text-align: center;
213
+ vertical-align: middle;
214
+ width: ${square};
215
+ }
216
+
217
+ .sw-date-field-day-outside {
218
+ color: var(--sw-color-muted);
219
+ }
220
+
221
+ .sw-date-field-step:hover:not([aria-disabled='true']),
222
+ .sw-date-field-day:hover:not([aria-disabled='true']) {
223
+ background: color-mix(in srgb, var(--sw-color-muted) 16%, transparent);
224
+ }
225
+
226
+ .sw-date-field-day[aria-current='date'] {
227
+ border-color: var(--sw-color-accent);
228
+ color: var(--sw-color-accent);
229
+ font-weight: ${label.fontWeight};
230
+ }
231
+
232
+ .sw-date-field-day[aria-selected='true'],
233
+ .sw-date-field-day[aria-selected='true']:hover {
234
+ background: var(--sw-color-accent);
235
+ border-color: var(--sw-color-accent);
236
+ color: var(--sw-color-onAccent);
237
+ font-weight: ${label.fontWeight};
238
+ }
239
+
240
+ .sw-date-field-day[aria-disabled='true'] {
241
+ cursor: not-allowed;
242
+ opacity: var(--sw-disabled-opacity);
243
+ }
244
+
245
+ .sw-date-field-button:focus,
246
+ .sw-date-field-step:focus,
247
+ .sw-date-field-day:focus {
248
+ outline: none;
249
+ }
250
+
251
+ .sw-date-field-button:focus-visible,
252
+ .sw-date-field-step:focus-visible,
253
+ .sw-date-field-day:focus-visible {
254
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
255
+ outline-offset: var(--sw-focus-ring-offset);
256
+ }
257
+
258
+ .sw-date-field-footer {
259
+ display: flex;
260
+ gap: var(--sw-space-1);
261
+ justify-content: flex-end;
262
+ }`;
263
+ }
264
+ function adaptiveRules() {
265
+ return `@media (prefers-reduced-transparency: reduce) {
266
+ ${input} { background: var(--sw-color-surface); }
63
267
  }
64
268
 
65
269
  @media (forced-colors: active) {
66
- .sw-date-field-input[aria-invalid='true'] { border-color: CanvasText; }
270
+ ${input}[aria-invalid='true'] { border-color: CanvasText; }
271
+ .sw-date-field-calendar { border-color: CanvasText; }
272
+ .sw-date-field-day[aria-current='date'] { border-color: CanvasText; }
273
+ .sw-date-field-day[aria-selected='true'] {
274
+ background: Highlight;
275
+ color: HighlightText;
276
+ forced-color-adjust: none;
277
+ }
67
278
  }`;
68
279
  }
280
+ export function cssDateFieldClasses() {
281
+ return `${fieldRules()}
282
+
283
+ ${calendarRules()}
284
+
285
+ ${adaptiveRules()}`;
286
+ }
69
287
  export function dateFieldClassCatalog() {
70
288
  return [
71
289
  'sw-date-field',
72
290
  'sw-date-field-label',
291
+ 'sw-date-field-control',
73
292
  'sw-date-field-input',
293
+ 'sw-date-field-button',
294
+ 'sw-date-field-glyph',
295
+ 'sw-date-field-glyph-directional',
74
296
  'sw-date-field-description',
75
297
  'sw-date-field-error',
298
+ 'sw-date-field-calendar',
299
+ 'sw-date-field-header',
300
+ 'sw-date-field-jump',
301
+ 'sw-date-field-step',
302
+ 'sw-date-field-grid',
303
+ 'sw-date-field-day',
304
+ 'sw-date-field-day-outside',
305
+ 'sw-date-field-footer',
76
306
  ];
77
307
  }
@@ -2,6 +2,7 @@ import { typographyVariants } from '@scalewing/tokens';
2
2
  import { breakpointQuery } from './breakpoints.js';
3
3
  import { badgeTones } from './css-data.js';
4
4
  import { scrollRegionRules } from './css-scroll-region.js';
5
+ import { zIndex } from './stacking.js';
5
6
  const caption = typographyVariants.caption;
6
7
  const data = typographyVariants.data;
7
8
  const label = typographyVariants.label;
@@ -81,7 +82,7 @@ ${scrollRegionRules('.sw-denomination-scroll')}
81
82
  -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
82
83
  inset-inline-start: 0;
83
84
  position: sticky;
84
- z-index: 1;
85
+ ${zIndex('stickyCell')}
85
86
  }
86
87
 
87
88
  /*
@@ -1 +1,3 @@
1
+ /** The frame every native text control draws: fill, hairline border, radius. */
2
+ export declare const controlSurface = "background-color: var(--sw-glass-fill);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-radius-sm);\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n padding-inline: var(--sw-control-md-padding-inline);";
1
3
  export declare function cssDocumentCanvas(): string;
@@ -10,7 +10,8 @@ const typedInputs = [
10
10
  'input:not([type])',
11
11
  'textarea',
12
12
  ].join(', ');
13
- const controlSurface = `background-color: var(--sw-glass-fill);
13
+ /** The frame every native text control draws: fill, hairline border, radius. */
14
+ export const controlSurface = `background-color: var(--sw-glass-fill);
14
15
  border: 1px solid var(--sw-color-border);
15
16
  border-radius: var(--sw-radius-sm);
16
17
  color: inherit;