@sovereignfs/ui 0.21.3 → 0.33.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 (130) hide show
  1. package/dist/Button.module.css +75 -8
  2. package/dist/Calendar.module.css +126 -0
  3. package/dist/Card.module.css +4 -2
  4. package/dist/Checkbox.module.css +109 -0
  5. package/dist/CodeTextarea.module.css +53 -0
  6. package/dist/ConfirmDialog.module.css +109 -0
  7. package/dist/DatePicker.module.css +57 -0
  8. package/dist/Dialog.module.css +73 -46
  9. package/dist/DragHandleRow.module.css +57 -0
  10. package/dist/Drawer.module.css +61 -0
  11. package/dist/FormField.module.css +5 -0
  12. package/dist/Icon.module.css +5 -0
  13. package/dist/Input.module.css +10 -0
  14. package/dist/Menu.module.css +82 -0
  15. package/dist/NavTabs.module.css +4 -2
  16. package/dist/OverlayHeader.module.css +98 -0
  17. package/dist/Popover.module.css +31 -1
  18. package/dist/SegmentedControl.module.css +4 -2
  19. package/dist/Select.module.css +9 -0
  20. package/dist/Sheet.module.css +55 -0
  21. package/dist/SplitPane.module.css +85 -0
  22. package/dist/StatusBadge.module.css +59 -0
  23. package/dist/SystemBanner.module.css +4 -2
  24. package/dist/Tabs.module.css +4 -2
  25. package/dist/TagInput.module.css +141 -0
  26. package/dist/Textarea.module.css +39 -0
  27. package/dist/Toast.module.css +7 -5
  28. package/dist/Toggle.module.css +1 -1
  29. package/dist/Tooltip.module.css +6 -1
  30. package/dist/index.d.ts +549 -20
  31. package/dist/index.js +1496 -193
  32. package/dist/tokens/primitives.css +19 -0
  33. package/dist/tokens/semantic.css +19 -0
  34. package/package.json +1 -1
  35. package/src/__tests__/motion.test.tsx +105 -0
  36. package/src/components/Button/Button.module.css +75 -8
  37. package/src/components/Button/Button.stories.tsx +6 -0
  38. package/src/components/Button/Button.tsx +17 -1
  39. package/src/components/Button/__tests__/Button.test.tsx +19 -0
  40. package/src/components/Calendar/Calendar.module.css +126 -0
  41. package/src/components/Calendar/Calendar.tsx +213 -0
  42. package/src/components/Calendar/__tests__/Calendar.test.tsx +98 -0
  43. package/src/components/Calendar/dateUtils.ts +97 -0
  44. package/src/components/Card/Card.module.css +4 -2
  45. package/src/components/Checkbox/Checkbox.module.css +109 -0
  46. package/src/components/Checkbox/Checkbox.tsx +78 -0
  47. package/src/components/CodeTextarea/CodeTextarea.module.css +53 -0
  48. package/src/components/CodeTextarea/CodeTextarea.stories.tsx +77 -0
  49. package/src/components/CodeTextarea/CodeTextarea.tsx +36 -0
  50. package/src/components/ConfirmDialog/ConfirmDialog.module.css +109 -0
  51. package/src/components/ConfirmDialog/ConfirmDialog.tsx +124 -0
  52. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +146 -0
  53. package/src/components/DatePicker/DatePicker.module.css +57 -0
  54. package/src/components/DatePicker/DatePicker.tsx +103 -0
  55. package/src/components/DatePicker/__tests__/DatePicker.test.tsx +89 -0
  56. package/src/components/Dialog/Dialog.module.css +73 -46
  57. package/src/components/Dialog/Dialog.stories.tsx +51 -1
  58. package/src/components/Dialog/Dialog.tsx +86 -22
  59. package/src/components/Dialog/__tests__/Dialog.test.tsx +67 -3
  60. package/src/components/DragHandleRow/DragHandleRow.module.css +57 -0
  61. package/src/components/DragHandleRow/DragHandleRow.tsx +61 -0
  62. package/src/components/Drawer/Drawer.module.css +61 -0
  63. package/src/components/Drawer/Drawer.tsx +117 -13
  64. package/src/components/Drawer/__tests__/Drawer.test.tsx +123 -2
  65. package/src/components/FormField/FormField.module.css +5 -0
  66. package/src/components/FormField/FormField.tsx +36 -11
  67. package/src/components/FormField/__tests__/FormField.test.tsx +83 -0
  68. package/src/components/Icon/Icon.module.css +5 -0
  69. package/src/components/Icon/Icon.stories.tsx +4 -3
  70. package/src/components/Icon/Icon.tsx +1 -1
  71. package/src/components/Icon/icons/calendar.tsx +23 -0
  72. package/src/components/Icon/icons/ellipsis-vertical.tsx +22 -0
  73. package/src/components/Icon/icons/index.ts +8 -4
  74. package/src/components/Icon/icons/sliders-horizontal.tsx +9 -9
  75. package/src/components/Input/Input.module.css +10 -0
  76. package/src/components/Menu/Menu.module.css +82 -0
  77. package/src/components/Menu/Menu.tsx +137 -0
  78. package/src/components/Menu/__tests__/Menu.test.tsx +197 -0
  79. package/src/components/NavTabs/NavTabs.module.css +4 -2
  80. package/src/components/OverlayHeader/OverlayHeader.module.css +98 -0
  81. package/src/components/OverlayHeader/OverlayHeader.tsx +62 -0
  82. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +51 -0
  83. package/src/components/Popover/Popover.module.css +31 -1
  84. package/src/components/Popover/Popover.stories.tsx +126 -1
  85. package/src/components/Popover/Popover.tsx +140 -7
  86. package/src/components/SegmentedControl/SegmentedControl.module.css +4 -2
  87. package/src/components/Select/Select.module.css +9 -0
  88. package/src/components/Select/Select.tsx +10 -2
  89. package/src/components/Sheet/Sheet.module.css +55 -0
  90. package/src/components/Sheet/Sheet.tsx +151 -0
  91. package/src/components/Sheet/__tests__/Sheet.test.tsx +99 -0
  92. package/src/components/SplitPane/SplitPane.module.css +85 -0
  93. package/src/components/SplitPane/SplitPane.stories.tsx +160 -0
  94. package/src/components/SplitPane/SplitPane.tsx +149 -0
  95. package/src/components/SplitPane/__tests__/SplitPane.test.tsx +51 -0
  96. package/src/components/StatusBadge/StatusBadge.module.css +59 -0
  97. package/src/components/StatusBadge/StatusBadge.stories.tsx +74 -0
  98. package/src/components/StatusBadge/StatusBadge.tsx +65 -0
  99. package/src/components/SystemBanner/SystemBanner.module.css +4 -2
  100. package/src/components/Tabs/Tabs.module.css +4 -2
  101. package/src/components/TagInput/TagInput.module.css +141 -0
  102. package/src/components/TagInput/TagInput.stories.tsx +109 -0
  103. package/src/components/TagInput/TagInput.tsx +174 -0
  104. package/src/components/TagInput/__tests__/TagInput.test.tsx +69 -0
  105. package/src/components/Textarea/Textarea.module.css +39 -0
  106. package/src/components/Textarea/Textarea.tsx +15 -0
  107. package/src/components/Textarea/__tests__/Textarea.test.tsx +29 -0
  108. package/src/components/Toast/Toast.module.css +7 -5
  109. package/src/components/Toggle/Toggle.module.css +1 -1
  110. package/src/components/Tooltip/Tooltip.module.css +6 -1
  111. package/src/hooks/__tests__/useDoubleTap.test.tsx +117 -0
  112. package/src/hooks/__tests__/useIsMobile.test.tsx +80 -0
  113. package/src/hooks/__tests__/useLongPress.test.tsx +158 -0
  114. package/src/hooks/index.ts +4 -0
  115. package/src/hooks/useDoubleTap.ts +91 -0
  116. package/src/hooks/useIsMobile.ts +33 -0
  117. package/src/hooks/useLongPress.ts +185 -0
  118. package/src/index.ts +37 -2
  119. package/src/motion.ts +78 -0
  120. package/src/stories/Checkbox.stories.tsx +45 -0
  121. package/src/stories/DesignSystemOverview.stories.tsx +354 -8
  122. package/src/stories/DragHandleRow.stories.tsx +41 -0
  123. package/src/stories/FormField.stories.tsx +8 -8
  124. package/src/stories/InteractionHooks.stories.tsx +388 -0
  125. package/src/stories/MobilePatterns.stories.tsx +4 -4
  126. package/src/stories/Textarea.stories.tsx +51 -0
  127. package/src/stories/TokenGallery.stories.tsx +87 -1
  128. package/src/tokens/primitives.css +19 -0
  129. package/src/tokens/semantic.css +19 -0
  130. package/src/components/Icon/icons/terminal.tsx +0 -19
@@ -9,6 +9,9 @@
9
9
  font-weight: var(--sv-font-weight-medium);
10
10
  line-height: 1;
11
11
  cursor: pointer;
12
+ /* Removes the ~300ms double-tap-to-zoom delay on touch — this is a tap
13
+ target, never a pinch-zoom surface. */
14
+ touch-action: manipulation;
12
15
  transition:
13
16
  background-color 0.15s ease,
14
17
  border-color 0.15s ease,
@@ -25,6 +28,15 @@
25
28
  cursor: not-allowed;
26
29
  }
27
30
 
31
+ /* Touch target: gated to (pointer: coarse) — the *primary* pointer, not
32
+ (any-pointer: coarse) — so a touchscreen laptop with mouse/trackpad as
33
+ primary keeps desktop density (see mobile design-system plan, Part 4 #5). */
34
+ @media (pointer: coarse) {
35
+ .button {
36
+ min-height: var(--sv-touch-target-min, 44px);
37
+ }
38
+ }
39
+
28
40
  /* Sizes */
29
41
  .sm {
30
42
  padding: var(--sv-space-1) var(--sv-space-3);
@@ -37,14 +49,51 @@
37
49
  font-weight: var(--sv-font-weight-semibold);
38
50
  }
39
51
 
52
+ /* Loading spinner — a self-contained token-only spinner rather than reusing
53
+ the Spinner component: Spinner's border colors are semantic-fixed
54
+ (`--sv-color-accent`), which would be invisible against a primary button's
55
+ accent background, and overriding a second CSS module's rule on the same
56
+ element is a cross-module cascade-order gamble (see ConfirmDialog's
57
+ `.destructiveConfirm` comment for the same tradeoff). `currentColor` picks
58
+ up each variant's own text color for free. */
59
+ .spinner {
60
+ display: inline-block;
61
+ width: var(--sv-icon-size-sm);
62
+ height: var(--sv-icon-size-sm);
63
+ flex-shrink: 0;
64
+ border: 2px solid currentColor;
65
+ border-top-color: transparent;
66
+ border-radius: var(--sv-radius-full);
67
+ opacity: 0.7;
68
+ animation: button-spin 0.7s linear infinite;
69
+ }
70
+
71
+ @keyframes button-spin {
72
+ to {
73
+ transform: rotate(360deg);
74
+ }
75
+ }
76
+
77
+ @media (prefers-reduced-motion: reduce) {
78
+ .spinner {
79
+ animation-play-state: paused;
80
+ }
81
+ }
82
+
40
83
  /* Variants */
41
84
  .primary {
42
85
  background-color: var(--sv-color-accent);
43
86
  color: var(--sv-color-text-on-accent);
44
87
  }
45
88
 
46
- .primary:hover:not(:disabled) {
47
- background-color: var(--sv-color-accent-hover);
89
+ @media (hover: hover) {
90
+ .primary:hover:not(:disabled) {
91
+ background-color: var(--sv-color-accent-hover);
92
+ }
93
+ }
94
+
95
+ .primary:active:not(:disabled) {
96
+ background-color: color-mix(in srgb, var(--sv-color-accent) 80%, black);
48
97
  }
49
98
 
50
99
  .secondary {
@@ -53,8 +102,14 @@
53
102
  border-color: var(--sv-color-border-strong);
54
103
  }
55
104
 
56
- .secondary:hover:not(:disabled) {
57
- background-color: var(--sv-color-surface-sunken);
105
+ @media (hover: hover) {
106
+ .secondary:hover:not(:disabled) {
107
+ background-color: var(--sv-color-surface-sunken);
108
+ }
109
+ }
110
+
111
+ .secondary:active:not(:disabled) {
112
+ background-color: color-mix(in srgb, var(--sv-color-surface-sunken) 85%, black);
58
113
  }
59
114
 
60
115
  .ghost {
@@ -62,8 +117,14 @@
62
117
  color: var(--sv-color-text-primary);
63
118
  }
64
119
 
65
- .ghost:hover:not(:disabled) {
66
- background-color: var(--sv-color-surface-sunken);
120
+ @media (hover: hover) {
121
+ .ghost:hover:not(:disabled) {
122
+ background-color: var(--sv-color-surface-sunken);
123
+ }
124
+ }
125
+
126
+ .ghost:active:not(:disabled) {
127
+ background-color: color-mix(in srgb, var(--sv-color-surface-sunken) 85%, black);
67
128
  }
68
129
 
69
130
  .destructive {
@@ -72,6 +133,12 @@
72
133
  border-color: var(--sv-color-error-border);
73
134
  }
74
135
 
75
- .destructive:hover:not(:disabled) {
76
- background-color: var(--sv-color-error-surface);
136
+ @media (hover: hover) {
137
+ .destructive:hover:not(:disabled) {
138
+ background-color: var(--sv-color-error-surface);
139
+ }
140
+ }
141
+
142
+ .destructive:active:not(:disabled) {
143
+ background-color: color-mix(in srgb, var(--sv-color-error-surface) 85%, black);
77
144
  }
@@ -0,0 +1,126 @@
1
+ /* Calendar — month grid. Tokens only. Grid columns are equal-width via
2
+ * `1fr` repeated 7 times rather than a fixed px size, so the component
3
+ * scales cleanly inside both a Popover panel (desktop) and a full-width
4
+ * Drawer sheet (mobile). */
5
+
6
+ .calendar {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: var(--sv-space-3);
10
+ font-family: var(--sv-font-family);
11
+ }
12
+
13
+ .header {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: space-between;
17
+ gap: var(--sv-space-2);
18
+ }
19
+
20
+ .navButton {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ width: var(--sv-touch-target-min, 44px);
25
+ height: var(--sv-touch-target-min, 44px);
26
+ flex-shrink: 0;
27
+ border: none;
28
+ border-radius: var(--sv-radius-md);
29
+ background: transparent;
30
+ color: var(--sv-color-text-muted);
31
+ cursor: pointer;
32
+ }
33
+
34
+ @media (hover: hover) {
35
+ .navButton:hover {
36
+ background: var(--sv-color-surface-sunken);
37
+ color: var(--sv-color-text-primary);
38
+ }
39
+ }
40
+
41
+ .navButton:focus-visible {
42
+ outline: 2px solid var(--sv-color-focus-ring);
43
+ outline-offset: -2px;
44
+ }
45
+
46
+ .monthLabel {
47
+ font-size: var(--sv-font-size-sm);
48
+ font-weight: var(--sv-font-weight-semibold);
49
+ color: var(--sv-color-text-primary);
50
+ }
51
+
52
+ .grid {
53
+ display: flex;
54
+ flex-direction: column;
55
+ gap: var(--sv-space-1);
56
+ }
57
+
58
+ .weekdayRow,
59
+ .week {
60
+ display: grid;
61
+ grid-template-columns: repeat(7, 1fr);
62
+ }
63
+
64
+ .weekday {
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ height: var(--sv-space-6);
69
+ font-size: var(--sv-font-size-xs);
70
+ font-weight: var(--sv-font-weight-medium);
71
+ color: var(--sv-color-text-subtle);
72
+ text-transform: uppercase;
73
+ }
74
+
75
+ .day {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ width: 100%;
80
+ /* Square-ish cell that also clears the touch-target minimum — height
81
+ drives the grid's row size; width fills its 1fr column. */
82
+ min-height: var(--sv-touch-target-min, 44px);
83
+ border: none;
84
+ border-radius: var(--sv-radius-md);
85
+ background: transparent;
86
+ color: var(--sv-color-text-primary);
87
+ font-size: var(--sv-font-size-sm);
88
+ cursor: pointer;
89
+ }
90
+
91
+ @media (hover: hover) {
92
+ .day:hover:not(:disabled) {
93
+ background: var(--sv-color-surface-sunken);
94
+ }
95
+ }
96
+
97
+ .day:focus-visible {
98
+ outline: 2px solid var(--sv-color-focus-ring);
99
+ outline-offset: -2px;
100
+ }
101
+
102
+ .day:disabled {
103
+ color: var(--sv-color-text-subtle);
104
+ cursor: not-allowed;
105
+ }
106
+
107
+ .dayOutside {
108
+ color: var(--sv-color-text-subtle);
109
+ }
110
+
111
+ .dayToday {
112
+ font-weight: var(--sv-font-weight-semibold);
113
+ box-shadow: inset 0 0 0 1px var(--sv-color-border-strong);
114
+ }
115
+
116
+ .daySelected,
117
+ .daySelected:disabled {
118
+ background: var(--sv-color-accent);
119
+ color: var(--sv-color-text-on-accent);
120
+ }
121
+
122
+ @media (hover: hover) {
123
+ .daySelected:hover:not(:disabled) {
124
+ background: var(--sv-color-accent-hover);
125
+ }
126
+ }
@@ -24,8 +24,10 @@
24
24
  transition: border-color 0.15s ease;
25
25
  }
26
26
 
27
- .interactive:hover {
28
- border-color: var(--sv-color-border-strong);
27
+ @media (hover: hover) {
28
+ .interactive:hover {
29
+ border-color: var(--sv-color-border-strong);
30
+ }
29
31
  }
30
32
 
31
33
  .interactive:focus-visible {
@@ -0,0 +1,109 @@
1
+ .root {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: var(--sv-space-2);
5
+ cursor: pointer;
6
+ }
7
+
8
+ /* The visual box */
9
+ .box {
10
+ --checkbox-box-size: 18px;
11
+ position: relative;
12
+ flex-shrink: 0;
13
+ width: var(--checkbox-box-size);
14
+ height: var(--checkbox-box-size);
15
+ border-radius: var(--sv-radius-sm);
16
+ border: 1.5px solid var(--sv-color-border-strong);
17
+ background: var(--sv-color-surface);
18
+ transition:
19
+ background-color 0.15s ease,
20
+ border-color 0.15s ease;
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ }
25
+
26
+ .checked {
27
+ background-color: var(--sv-color-accent);
28
+ border-color: var(--sv-color-accent);
29
+ color: var(--sv-white);
30
+ }
31
+
32
+ /* Hide the native input visually but keep it accessible */
33
+ .input {
34
+ position: absolute;
35
+ inset: 0;
36
+ opacity: 0;
37
+ margin: 0;
38
+ width: 100%;
39
+ height: 100%;
40
+ cursor: pointer;
41
+ }
42
+
43
+ .input:disabled {
44
+ cursor: not-allowed;
45
+ }
46
+
47
+ /* Expand the tappable region to the platform's minimum touch target without
48
+ growing the visible box — consumers (e.g. task rows) align the box to the
49
+ pixel against header icons, so its visual footprint and layout must stay
50
+ identical. Gated to (pointer: coarse), the *primary* pointer, so a
51
+ touchscreen laptop with mouse/trackpad as primary keeps desktop density. */
52
+ @media (pointer: coarse) {
53
+ .input {
54
+ inset: calc((var(--checkbox-box-size) - var(--sv-touch-target-min, 44px)) / 2);
55
+ }
56
+ }
57
+
58
+ .input:focus-visible + .tick,
59
+ .input:focus-visible {
60
+ outline: none;
61
+ }
62
+
63
+ .box:has(.input:focus-visible) {
64
+ outline: 2px solid var(--sv-color-focus-ring);
65
+ outline-offset: 2px;
66
+ }
67
+
68
+ .tick {
69
+ width: 10px;
70
+ height: 8px;
71
+ pointer-events: none;
72
+ }
73
+
74
+ /* Label */
75
+ .label {
76
+ font-size: var(--sv-font-size-sm);
77
+ color: var(--sv-color-text-primary);
78
+ cursor: pointer;
79
+ user-select: none;
80
+ transition: color 0.15s ease;
81
+ /* Strike-through animates via clip-path on a pseudo-element */
82
+ position: relative;
83
+ }
84
+
85
+ .label::after {
86
+ content: '';
87
+ position: absolute;
88
+ left: 0;
89
+ top: 50%;
90
+ width: 100%;
91
+ height: 1.5px;
92
+ background: var(--sv-color-text-muted);
93
+ transform: scaleX(0);
94
+ transform-origin: left center;
95
+ transition: transform 0.2s ease;
96
+ }
97
+
98
+ .struck {
99
+ color: var(--sv-color-text-muted);
100
+ }
101
+
102
+ .struck::after {
103
+ transform: scaleX(1);
104
+ }
105
+
106
+ .disabled {
107
+ opacity: 0.5;
108
+ cursor: not-allowed;
109
+ }
@@ -0,0 +1,53 @@
1
+ .textarea {
2
+ display: block;
3
+ width: 100%;
4
+ min-height: 180px;
5
+ max-height: min(70vh, 720px);
6
+ padding: var(--sv-space-3);
7
+ font-family: var(--sv-font-family-mono);
8
+ font-size: var(--sv-font-size-sm);
9
+ line-height: 1.6;
10
+ color: var(--sv-color-text-primary);
11
+ tab-size: 2;
12
+ white-space: pre;
13
+ overflow: auto;
14
+ resize: vertical;
15
+ background: var(--sv-color-surface);
16
+ border: 1px solid var(--sv-color-border-strong);
17
+ border-radius: var(--sv-radius-md);
18
+ transition:
19
+ border-color 0.15s ease,
20
+ box-shadow 0.15s ease;
21
+ }
22
+
23
+ .textarea::placeholder {
24
+ color: var(--sv-color-text-muted);
25
+ }
26
+
27
+ .textarea:focus-visible {
28
+ outline: none;
29
+ border-color: var(--sv-color-accent);
30
+ box-shadow: 0 0 0 2px var(--sv-color-focus-ring);
31
+ }
32
+
33
+ .textarea:read-only {
34
+ color: var(--sv-color-text-muted);
35
+ background: var(--sv-color-surface-sunken);
36
+ border-color: var(--sv-color-border);
37
+ cursor: default;
38
+ }
39
+
40
+ .textarea:disabled {
41
+ opacity: 0.5;
42
+ cursor: not-allowed;
43
+ resize: none;
44
+ }
45
+
46
+ .invalid {
47
+ border-color: var(--sv-color-error-border);
48
+ }
49
+
50
+ .invalid:focus-visible {
51
+ border-color: var(--sv-color-error-border);
52
+ box-shadow: 0 0 0 2px var(--sv-color-error-border);
53
+ }
@@ -0,0 +1,109 @@
1
+ /* ConfirmDialog — built on the native <dialog> element (see the component's
2
+ * own doc comment for why: content-sized, top-layer stacking, ::backdrop for
3
+ * free). Tokens only. */
4
+
5
+ .dialog {
6
+ border: none;
7
+ border-radius: var(--sv-radius-lg);
8
+ background: var(--sv-color-surface);
9
+ box-shadow: var(--sv-shadow-overlay);
10
+ padding: 0;
11
+ width: 22rem;
12
+ max-width: calc(100vw - var(--sv-space-8));
13
+ }
14
+
15
+ .dialog::backdrop {
16
+ background: var(--sv-color-scrim);
17
+ }
18
+
19
+ .body {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: var(--sv-space-4);
23
+ padding: var(--sv-space-6);
24
+ }
25
+
26
+ .title {
27
+ margin: 0;
28
+ font-size: var(--sv-font-size-lg);
29
+ font-weight: var(--sv-font-weight-semibold);
30
+ color: var(--sv-color-text-primary);
31
+ }
32
+
33
+ .message {
34
+ margin: 0;
35
+ font-size: var(--sv-font-size-sm);
36
+ color: var(--sv-color-text-muted);
37
+ }
38
+
39
+ .error {
40
+ margin: 0;
41
+ font-size: var(--sv-font-size-xs);
42
+ color: var(--sv-color-error-text);
43
+ padding: var(--sv-space-2) var(--sv-space-3);
44
+ border: 1px solid var(--sv-color-error-border);
45
+ border-radius: var(--sv-radius-sm);
46
+ background-color: var(--sv-color-error-surface);
47
+ }
48
+
49
+ .actions {
50
+ display: flex;
51
+ gap: var(--sv-space-3);
52
+ justify-content: flex-end;
53
+ }
54
+
55
+ /* Matches Button's own .button + .md structural rules exactly (padding,
56
+ * radius, font weight, focus-visible ring, disabled state, touch target,
57
+ * hover guard, active/pressed state) so it reads as the same control family
58
+ * — only the color scheme differs. Not built as an override of Button's own
59
+ * CSS module: cross-module class-order in the cascade isn't guaranteed, and
60
+ * an inline-style override would defeat Button's own :hover rule (inline
61
+ * styles always win over an external stylesheet, hover or not). A plain
62
+ * button with its own tokens sidesteps both problems. */
63
+ .destructiveConfirm {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ gap: var(--sv-space-2);
68
+ padding: 13px var(--sv-space-4);
69
+ border: 1px solid transparent;
70
+ border-radius: var(--sv-radius-md);
71
+ font-family: var(--sv-font-family);
72
+ font-size: var(--sv-font-size-sm);
73
+ font-weight: var(--sv-font-weight-semibold);
74
+ line-height: 1;
75
+ cursor: pointer;
76
+ touch-action: manipulation;
77
+ background-color: var(--sv-color-error-solid);
78
+ color: var(--sv-color-text-on-error);
79
+ transition: background-color 0.15s ease;
80
+ }
81
+
82
+ @media (pointer: coarse) {
83
+ .destructiveConfirm {
84
+ min-height: var(--sv-touch-target-min, 44px);
85
+ }
86
+ }
87
+
88
+ /* Derived via color-mix from the semantic token, not a raw --sv-red-800
89
+ primitive (the original .dangerButton pattern this replaces did reference
90
+ the primitive directly — fixed here, not carried forward). */
91
+ @media (hover: hover) {
92
+ .destructiveConfirm:hover:not(:disabled) {
93
+ background-color: color-mix(in srgb, var(--sv-color-error-solid) 85%, black);
94
+ }
95
+ }
96
+
97
+ .destructiveConfirm:active:not(:disabled) {
98
+ background-color: color-mix(in srgb, var(--sv-color-error-solid) 80%, black);
99
+ }
100
+
101
+ .destructiveConfirm:focus-visible {
102
+ outline: 2px solid var(--sv-color-focus-ring);
103
+ outline-offset: 2px;
104
+ }
105
+
106
+ .destructiveConfirm:disabled {
107
+ opacity: 0.5;
108
+ cursor: not-allowed;
109
+ }
@@ -0,0 +1,57 @@
1
+ /* DatePicker — trigger styled to match Input's height/border/radius/focus
2
+ * ring for visual consistency as a form field. */
3
+
4
+ .trigger {
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: space-between;
8
+ gap: var(--sv-space-2);
9
+ width: 100%;
10
+ padding: var(--sv-space-2) var(--sv-space-3);
11
+ font-family: var(--sv-font-family);
12
+ font-size: var(--sv-font-size-sm);
13
+ line-height: 1.5;
14
+ color: var(--sv-color-text-primary);
15
+ background-color: var(--sv-color-surface);
16
+ border: 1px solid var(--sv-color-border-strong);
17
+ border-radius: var(--sv-radius-md);
18
+ cursor: pointer;
19
+ transition:
20
+ border-color 0.15s ease,
21
+ box-shadow 0.15s ease;
22
+ }
23
+
24
+ /* Same iOS auto-zoom fix as Input — see its own module.css for why. */
25
+ @media (pointer: coarse) {
26
+ .trigger {
27
+ font-size: var(--sv-font-size-md);
28
+ }
29
+ }
30
+
31
+ .trigger:focus-visible {
32
+ outline: none;
33
+ border-color: var(--sv-color-accent);
34
+ box-shadow: 0 0 0 2px var(--sv-color-focus-ring);
35
+ }
36
+
37
+ .trigger:disabled {
38
+ opacity: 0.5;
39
+ cursor: not-allowed;
40
+ }
41
+
42
+ .triggerValue {
43
+ color: var(--sv-color-text-primary);
44
+ }
45
+
46
+ .triggerPlaceholder {
47
+ color: var(--sv-color-text-muted);
48
+ }
49
+
50
+ .popoverBody {
51
+ padding: var(--sv-space-3);
52
+ }
53
+
54
+ .drawerBody {
55
+ padding: var(--sv-space-4);
56
+ padding-bottom: max(var(--sv-space-4), env(safe-area-inset-bottom));
57
+ }