@sovereignfs/ui 0.23.0 → 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 (109) 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 +14 -2
  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 +9 -3
  10. package/dist/Drawer.module.css +61 -0
  11. package/dist/Icon.module.css +5 -0
  12. package/dist/Menu.module.css +82 -0
  13. package/dist/NavTabs.module.css +4 -2
  14. package/dist/OverlayHeader.module.css +98 -0
  15. package/dist/Popover.module.css +31 -1
  16. package/dist/SegmentedControl.module.css +4 -2
  17. package/dist/Sheet.module.css +55 -0
  18. package/dist/SplitPane.module.css +85 -0
  19. package/dist/StatusBadge.module.css +59 -0
  20. package/dist/SystemBanner.module.css +4 -2
  21. package/dist/Tabs.module.css +4 -2
  22. package/dist/TagInput.module.css +141 -0
  23. package/dist/Toast.module.css +4 -2
  24. package/dist/Tooltip.module.css +6 -1
  25. package/dist/index.d.ts +493 -17
  26. package/dist/index.js +1390 -209
  27. package/dist/tokens/primitives.css +19 -0
  28. package/dist/tokens/semantic.css +13 -0
  29. package/package.json +1 -1
  30. package/src/__tests__/motion.test.tsx +105 -0
  31. package/src/components/Button/Button.module.css +75 -8
  32. package/src/components/Button/Button.stories.tsx +6 -0
  33. package/src/components/Button/Button.tsx +17 -1
  34. package/src/components/Button/__tests__/Button.test.tsx +19 -0
  35. package/src/components/Calendar/Calendar.module.css +126 -0
  36. package/src/components/Calendar/Calendar.tsx +213 -0
  37. package/src/components/Calendar/__tests__/Calendar.test.tsx +98 -0
  38. package/src/components/Calendar/dateUtils.ts +97 -0
  39. package/src/components/Card/Card.module.css +4 -2
  40. package/src/components/Checkbox/Checkbox.module.css +14 -2
  41. package/src/components/Checkbox/Checkbox.tsx +22 -12
  42. package/src/components/CodeTextarea/CodeTextarea.module.css +53 -0
  43. package/src/components/CodeTextarea/CodeTextarea.stories.tsx +77 -0
  44. package/src/components/CodeTextarea/CodeTextarea.tsx +36 -0
  45. package/src/components/ConfirmDialog/ConfirmDialog.module.css +109 -0
  46. package/src/components/ConfirmDialog/ConfirmDialog.tsx +124 -0
  47. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +146 -0
  48. package/src/components/DatePicker/DatePicker.module.css +57 -0
  49. package/src/components/DatePicker/DatePicker.tsx +103 -0
  50. package/src/components/DatePicker/__tests__/DatePicker.test.tsx +89 -0
  51. package/src/components/Dialog/Dialog.module.css +73 -46
  52. package/src/components/Dialog/Dialog.stories.tsx +51 -1
  53. package/src/components/Dialog/Dialog.tsx +86 -22
  54. package/src/components/Dialog/__tests__/Dialog.test.tsx +67 -3
  55. package/src/components/DragHandleRow/DragHandleRow.module.css +9 -3
  56. package/src/components/Drawer/Drawer.module.css +61 -0
  57. package/src/components/Drawer/Drawer.tsx +117 -13
  58. package/src/components/Drawer/__tests__/Drawer.test.tsx +123 -2
  59. package/src/components/Icon/Icon.module.css +5 -0
  60. package/src/components/Icon/Icon.stories.tsx +4 -3
  61. package/src/components/Icon/Icon.tsx +1 -1
  62. package/src/components/Icon/icons/calendar.tsx +23 -0
  63. package/src/components/Icon/icons/ellipsis-vertical.tsx +22 -0
  64. package/src/components/Icon/icons/index.ts +8 -4
  65. package/src/components/Icon/icons/sliders-horizontal.tsx +9 -9
  66. package/src/components/Menu/Menu.module.css +82 -0
  67. package/src/components/Menu/Menu.tsx +137 -0
  68. package/src/components/Menu/__tests__/Menu.test.tsx +197 -0
  69. package/src/components/NavTabs/NavTabs.module.css +4 -2
  70. package/src/components/OverlayHeader/OverlayHeader.module.css +98 -0
  71. package/src/components/OverlayHeader/OverlayHeader.tsx +62 -0
  72. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +51 -0
  73. package/src/components/Popover/Popover.module.css +31 -1
  74. package/src/components/Popover/Popover.stories.tsx +126 -1
  75. package/src/components/Popover/Popover.tsx +140 -7
  76. package/src/components/SegmentedControl/SegmentedControl.module.css +4 -2
  77. package/src/components/Sheet/Sheet.module.css +55 -0
  78. package/src/components/Sheet/Sheet.tsx +151 -0
  79. package/src/components/Sheet/__tests__/Sheet.test.tsx +99 -0
  80. package/src/components/SplitPane/SplitPane.module.css +85 -0
  81. package/src/components/SplitPane/SplitPane.stories.tsx +160 -0
  82. package/src/components/SplitPane/SplitPane.tsx +149 -0
  83. package/src/components/SplitPane/__tests__/SplitPane.test.tsx +51 -0
  84. package/src/components/StatusBadge/StatusBadge.module.css +59 -0
  85. package/src/components/StatusBadge/StatusBadge.stories.tsx +74 -0
  86. package/src/components/StatusBadge/StatusBadge.tsx +65 -0
  87. package/src/components/SystemBanner/SystemBanner.module.css +4 -2
  88. package/src/components/Tabs/Tabs.module.css +4 -2
  89. package/src/components/TagInput/TagInput.module.css +141 -0
  90. package/src/components/TagInput/TagInput.stories.tsx +109 -0
  91. package/src/components/TagInput/TagInput.tsx +174 -0
  92. package/src/components/TagInput/__tests__/TagInput.test.tsx +69 -0
  93. package/src/components/Toast/Toast.module.css +4 -2
  94. package/src/components/Tooltip/Tooltip.module.css +6 -1
  95. package/src/hooks/__tests__/useDoubleTap.test.tsx +117 -0
  96. package/src/hooks/__tests__/useIsMobile.test.tsx +80 -0
  97. package/src/hooks/__tests__/useLongPress.test.tsx +158 -0
  98. package/src/hooks/index.ts +4 -0
  99. package/src/hooks/useDoubleTap.ts +91 -0
  100. package/src/hooks/useIsMobile.ts +33 -0
  101. package/src/hooks/useLongPress.ts +185 -0
  102. package/src/index.ts +30 -1
  103. package/src/motion.ts +78 -0
  104. package/src/stories/DesignSystemOverview.stories.tsx +292 -4
  105. package/src/stories/InteractionHooks.stories.tsx +388 -0
  106. package/src/stories/TokenGallery.stories.tsx +85 -1
  107. package/src/tokens/primitives.css +19 -0
  108. package/src/tokens/semantic.css +13 -0
  109. package/src/components/Icon/icons/terminal.tsx +0 -19
@@ -5,9 +5,15 @@
5
5
  position: relative;
6
6
  }
7
7
 
8
- .row:not(:hover) .handle,
9
- .row:not(:focus-within) .handle {
10
- opacity: 0;
8
+ /* Hover-reveal only where hover genuinely exists — on touch the handle would
9
+ otherwise never become visible (tap-generated hover doesn't count as a
10
+ sustained hover state, and :not(:hover) is always true with no hover
11
+ capability at all), so it stays visible by default there. */
12
+ @media (hover: hover) {
13
+ .row:not(:hover) .handle,
14
+ .row:not(:focus-within) .handle {
15
+ opacity: 0;
16
+ }
11
17
  }
12
18
 
13
19
  .handle {
@@ -14,6 +14,14 @@
14
14
  display: flex;
15
15
  align-items: flex-end;
16
16
  background: var(--sv-color-scrim);
17
+ /* Entrance/exit fade — same two-phase-mount pattern as Dialog (see motion.ts
18
+ and Drawer.tsx). */
19
+ opacity: 0;
20
+ transition: opacity var(--sv-motion-duration-base) var(--sv-motion-ease-out);
21
+ }
22
+
23
+ .scrimOpen {
24
+ opacity: 1;
17
25
  }
18
26
 
19
27
  .panel {
@@ -28,4 +36,57 @@
28
36
  /* Pull content above the home indicator / bottom bar in standalone mode. */
29
37
  padding-bottom: env(safe-area-inset-bottom);
30
38
  outline: none;
39
+ /* Slide up from below the viewport. Always a bottom sheet, so unlike Dialog
40
+ there's no desktop-vs-mobile fork here. */
41
+ transform: translateY(100%);
42
+ transition: transform var(--sv-motion-duration-base) var(--sv-motion-ease-out);
43
+ }
44
+
45
+ .panelOpen {
46
+ transform: translateY(0);
47
+ }
48
+
49
+ /* 'half' snapHeight — a fixed height instead of the default content-sized cap
50
+ * above. Per the design-system's "half screen or less" convention for
51
+ * Drawer; content taller than this belongs in Sheet instead. */
52
+ .panelHalf {
53
+ height: 50dvh;
54
+ max-height: 50dvh;
55
+ }
56
+
57
+ /* See Dialog.module.css's identical rule for why 0.01ms rather than 0. */
58
+ @media (prefers-reduced-motion: reduce) {
59
+ .scrim,
60
+ .panel {
61
+ transition-duration: 0.01ms;
62
+ }
63
+ }
64
+
65
+ /* Grab handle — the sole drag-initiation region for swipe-down-to-dismiss
66
+ * (see Drawer.tsx's doc comment for why not the whole panel). The visual bar
67
+ * (::before) stays the small, established 36x4px size; only the hit area
68
+ * grows to a comfortable touch target. */
69
+ .handle {
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ width: 100%;
74
+ height: var(--sv-touch-target-min, 44px);
75
+ flex-shrink: 0;
76
+ cursor: grab;
77
+ /* JS-driven drag — the browser must not treat a press here as a
78
+ scroll/pan gesture that would fight it. */
79
+ touch-action: none;
80
+ }
81
+
82
+ .handle:active {
83
+ cursor: grabbing;
84
+ }
85
+
86
+ .handle::before {
87
+ content: '';
88
+ width: 36px;
89
+ height: 4px;
90
+ border-radius: var(--sv-radius-full);
91
+ background: var(--sv-color-border-strong);
31
92
  }
@@ -6,6 +6,11 @@
6
6
  /* Color follows the surrounding text via currentColor — no token needed. */
7
7
  }
8
8
 
9
+ .xs {
10
+ width: var(--sv-icon-size-xs);
11
+ height: var(--sv-icon-size-xs);
12
+ }
13
+
9
14
  .sm {
10
15
  width: var(--sv-icon-size-sm);
11
16
  height: var(--sv-icon-size-sm);
@@ -0,0 +1,82 @@
1
+ /* Menu — the item list shared by both presentations (Popover panel on
2
+ * desktop, Drawer panel on mobile); the presentational chrome around it is
3
+ * entirely owned by those two components, not duplicated here. */
4
+
5
+ .list {
6
+ list-style: none;
7
+ margin: 0;
8
+ padding: var(--sv-space-1);
9
+ display: flex;
10
+ flex-direction: column;
11
+ }
12
+
13
+ .item {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: var(--sv-space-2);
17
+ width: 100%;
18
+ /* Min touch target (WCAG 2.5.5 / Apple HIG) — this menu renders as a
19
+ Drawer sheet on mobile, so every item must be comfortably tappable. */
20
+ min-height: var(--sv-touch-target-min, 44px);
21
+ padding: var(--sv-space-2) var(--sv-space-3);
22
+ border: none;
23
+ border-radius: var(--sv-radius-sm);
24
+ background: transparent;
25
+ color: var(--sv-color-text-primary);
26
+ font: inherit;
27
+ font-size: var(--sv-font-size-sm);
28
+ text-align: left;
29
+ cursor: pointer;
30
+ }
31
+
32
+ @media (hover: hover) {
33
+ .item:hover:not(:disabled) {
34
+ background: var(--sv-color-surface-sunken);
35
+ }
36
+ }
37
+
38
+ .item:focus-visible {
39
+ outline: 2px solid var(--sv-color-focus-ring);
40
+ outline-offset: -2px;
41
+ }
42
+
43
+ .item:disabled {
44
+ opacity: 0.5;
45
+ cursor: not-allowed;
46
+ }
47
+
48
+ .itemDestructive {
49
+ color: var(--sv-color-error-text);
50
+ }
51
+
52
+ @media (hover: hover) {
53
+ .itemDestructive:hover:not(:disabled) {
54
+ background: var(--sv-color-error-surface);
55
+ }
56
+ }
57
+
58
+ /* Reserved checkmark gutter for a checkable item — sized so unchecked
59
+ siblings in the same group still align with the checked one's icon. */
60
+ .check {
61
+ display: inline-flex;
62
+ flex-shrink: 0;
63
+ width: var(--sv-icon-size-sm);
64
+ height: var(--sv-icon-size-sm);
65
+ align-items: center;
66
+ justify-content: center;
67
+ }
68
+
69
+ .label {
70
+ padding: var(--sv-space-2) var(--sv-space-3) var(--sv-space-1);
71
+ font-size: var(--sv-font-size-label);
72
+ font-weight: var(--sv-font-weight-semibold);
73
+ color: var(--sv-color-text-muted);
74
+ text-transform: uppercase;
75
+ letter-spacing: 0.04em;
76
+ }
77
+
78
+ .separator {
79
+ height: 1px;
80
+ margin: var(--sv-space-1) var(--sv-space-2);
81
+ background: var(--sv-color-border);
82
+ }
@@ -17,8 +17,10 @@
17
17
  transition: color 0.1s ease;
18
18
  }
19
19
 
20
- .link:hover {
21
- color: var(--sv-color-text-primary);
20
+ @media (hover: hover) {
21
+ .link:hover {
22
+ color: var(--sv-color-text-primary);
23
+ }
22
24
  }
23
25
 
24
26
  .link:focus-visible {
@@ -0,0 +1,98 @@
1
+ /* OverlayHeader — shared fixed secondary header for Dialog's mobile mode,
2
+ * Sheet, and Drawer. Not itself pinned via position — the consumer renders it
3
+ * as a flex-shrink: 0 sibling before its own scrolling content region (see
4
+ * the component's own doc comment). */
5
+
6
+ .header {
7
+ flex-shrink: 0;
8
+ background: var(--sv-color-surface);
9
+ border-bottom: 1px solid var(--sv-color-border);
10
+ }
11
+
12
+ .row {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: var(--sv-space-2);
16
+ padding: var(--sv-space-3) var(--sv-space-4);
17
+ }
18
+
19
+ .backButton {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ width: var(--sv-touch-target-min, 44px);
24
+ height: var(--sv-touch-target-min, 44px);
25
+ flex-shrink: 0;
26
+ /* Pull the touch-target padding back so the chevron glyph still sits close
27
+ to the title, matching the visual (not hit-area) spacing of .row's own
28
+ gap — otherwise a 44px box reads as a large empty gap before the title. */
29
+ margin-left: calc(-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-md)) / 2);
30
+ margin-right: calc(
31
+ -1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-md)) / 2 + var(--sv-space-1)
32
+ );
33
+ border: none;
34
+ border-radius: var(--sv-radius-md);
35
+ background: transparent;
36
+ color: var(--sv-color-text-muted);
37
+ cursor: pointer;
38
+ }
39
+
40
+ @media (hover: hover) {
41
+ .backButton:hover {
42
+ background: var(--sv-color-surface-sunken);
43
+ color: var(--sv-color-text-primary);
44
+ }
45
+ }
46
+
47
+ .backButton:focus-visible {
48
+ outline: 2px solid var(--sv-color-focus-ring);
49
+ outline-offset: -2px;
50
+ }
51
+
52
+ .title {
53
+ flex: 1;
54
+ min-width: 0;
55
+ overflow: hidden;
56
+ text-overflow: ellipsis;
57
+ white-space: nowrap;
58
+ font-size: var(--sv-font-size-xl);
59
+ font-weight: var(--sv-font-weight-semibold);
60
+ color: var(--sv-color-text-primary);
61
+ }
62
+
63
+ .action {
64
+ flex-shrink: 0;
65
+ }
66
+
67
+ .closeButton {
68
+ display: inline-flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ width: var(--sv-touch-target-min, 44px);
72
+ height: var(--sv-touch-target-min, 44px);
73
+ flex-shrink: 0;
74
+ /* Pull the touch-target padding back so the × sits flush with the row's own
75
+ edge padding, same reasoning as .backButton above. */
76
+ margin-right: calc(-1 * (var(--sv-touch-target-min, 44px) - var(--sv-icon-size-sm)) / 2);
77
+ border: none;
78
+ border-radius: var(--sv-radius-md);
79
+ background: transparent;
80
+ color: var(--sv-color-text-muted);
81
+ cursor: pointer;
82
+ }
83
+
84
+ @media (hover: hover) {
85
+ .closeButton:hover {
86
+ background: var(--sv-color-surface-sunken);
87
+ color: var(--sv-color-text-primary);
88
+ }
89
+ }
90
+
91
+ .closeButton:focus-visible {
92
+ outline: 2px solid var(--sv-color-focus-ring);
93
+ outline-offset: -2px;
94
+ }
95
+
96
+ .secondRow {
97
+ padding: 0 var(--sv-space-2) var(--sv-space-2);
98
+ }
@@ -11,7 +11,16 @@
11
11
  border: 1px solid var(--sv-color-border);
12
12
  border-radius: var(--sv-radius-2xl);
13
13
  box-shadow: var(--sv-shadow-popover);
14
- overflow: hidden;
14
+ /* auto, not hidden: Popover.tsx applies an inline max-height only when the
15
+ panel is taller than the room actually available on whichever side it
16
+ opens (e.g. a tall calendar on a short mobile viewport, where neither
17
+ above nor below has enough space for the whole thing) — auto lets that
18
+ capped case scroll internally instead of silently clipping content with
19
+ no way to reach it. When no max-height is applied (the common case, panel
20
+ already fits), auto behaves identically to hidden — nothing to scroll,
21
+ content still clips at the rounded corners exactly as before. */
22
+ overflow-x: hidden;
23
+ overflow-y: auto;
15
24
  /* Slide in from slightly above */
16
25
  animation: sv-popover-in 0.12s ease;
17
26
  }
@@ -42,3 +51,24 @@
42
51
  .left {
43
52
  left: 0;
44
53
  }
54
+
55
+ /* Collision flip: opens above the trigger instead of below, when the panel
56
+ wouldn't fit within the viewport underneath it (see Popover.tsx's
57
+ layout-effect measurement). Same slide-in treatment, mirrored to slide up
58
+ from slightly below instead of down from slightly above. */
59
+ .upward {
60
+ top: auto;
61
+ bottom: calc(100% + var(--sv-space-2));
62
+ animation-name: sv-popover-in-upward;
63
+ }
64
+
65
+ @keyframes sv-popover-in-upward {
66
+ from {
67
+ opacity: 0;
68
+ transform: translateY(4px);
69
+ }
70
+ to {
71
+ opacity: 1;
72
+ transform: translateY(0);
73
+ }
74
+ }
@@ -48,6 +48,8 @@
48
48
  color: var(--sv-color-text-muted);
49
49
  }
50
50
 
51
- .inactive:hover {
52
- color: var(--sv-color-text-primary);
51
+ @media (hover: hover) {
52
+ .inactive:hover {
53
+ color: var(--sv-color-text-primary);
54
+ }
53
55
  }
@@ -0,0 +1,55 @@
1
+ /* Sheet — fills the same rect as a plugin's content area (between the shell's
2
+ * sticky header and footer nav — see --sv-shell-header-height /
3
+ * --sv-shell-footer-height, set by the shell), sliding in from an edge
4
+ * instead of Dialog's centered box or Drawer's partial-height sheet-over-
5
+ * scrim. z-index matches Dialog/Drawer's scrim so it layers consistently
6
+ * with any other overlay. No scrim of its own — see the component doc
7
+ * comment for why. */
8
+
9
+ .panel {
10
+ position: fixed;
11
+ left: 0;
12
+ right: 0;
13
+ top: var(--sv-shell-header-height, 0);
14
+ bottom: var(--sv-shell-footer-height, 0);
15
+ z-index: 100;
16
+ display: flex;
17
+ flex-direction: column;
18
+ background: var(--sv-color-surface);
19
+ outline: none;
20
+ transform: translateY(100%);
21
+ transition: transform var(--sv-motion-duration-base) var(--sv-motion-ease-out);
22
+ }
23
+
24
+ .panelOpen {
25
+ transform: translateY(0);
26
+ }
27
+
28
+ /* Opt-in top variant (slideFrom="top") — a short options/actions menu opened
29
+ * from a header button reads more naturally sliding down from that button's
30
+ * edge than traveling up from off-screen bottom. :not(.panelOpen) is what
31
+ * lets this coexist with .panelOpen above despite equal-looking single-class
32
+ * specificity elsewhere in this file: its (0,2,0) beats .panelOpen's (0,1,0)
33
+ * while closed/entering/closing, and yields to it (transform: translateY(0),
34
+ * same value either direction) once open. */
35
+ .panelFromTop:not(.panelOpen) {
36
+ transform: translateY(-100%);
37
+ }
38
+
39
+ /* See Dialog.module.css's identical rule for why 0.01ms rather than 0. */
40
+ @media (prefers-reduced-motion: reduce) {
41
+ .panel {
42
+ transition-duration: 0.01ms;
43
+ }
44
+ }
45
+
46
+ /* Always the scroll container, with or without OverlayHeader present (see
47
+ * Sheet.tsx). No opinionated padding here — matches Drawer's convention of
48
+ * leaving horizontal/top padding and safe-area-bottom clearance to the
49
+ * content itself, since a Sheet's content shape varies far more than a
50
+ * fixed-size Dialog's. */
51
+ .content {
52
+ flex: 1 1 auto;
53
+ overflow-y: auto;
54
+ overscroll-behavior: contain;
55
+ }
@@ -0,0 +1,85 @@
1
+ .splitPane {
2
+ display: grid;
3
+ grid-template-columns:
4
+ minmax(0, var(--split-pane-primary-size, 50%)) 12px
5
+ minmax(0, calc(100% - var(--split-pane-primary-size, 50%)));
6
+ width: 100%;
7
+ min-height: 320px;
8
+ border: 1px solid var(--sv-color-border);
9
+ border-radius: var(--sv-radius-lg);
10
+ overflow: hidden;
11
+ background: var(--sv-color-surface);
12
+ }
13
+
14
+ .splitPane:not(:has(.handle)) {
15
+ grid-template-columns:
16
+ minmax(0, var(--split-pane-primary-size, 50%))
17
+ minmax(0, calc(100% - var(--split-pane-primary-size, 50%)));
18
+ }
19
+
20
+ .pane {
21
+ min-width: 0;
22
+ min-height: 0;
23
+ overflow: auto;
24
+ background: var(--sv-color-surface);
25
+ }
26
+
27
+ .pane + .pane {
28
+ border-left: 1px solid var(--sv-color-border);
29
+ }
30
+
31
+ .handle {
32
+ position: relative;
33
+ width: 12px;
34
+ min-width: 12px;
35
+ padding: 0;
36
+ border: 0;
37
+ border-inline: 1px solid var(--sv-color-border);
38
+ cursor: col-resize;
39
+ background: var(--sv-color-surface-sunken);
40
+ }
41
+
42
+ .handle::before {
43
+ position: absolute;
44
+ inset-block: 50%;
45
+ inset-inline: 50%;
46
+ width: 2px;
47
+ height: 40px;
48
+ content: '';
49
+ background: var(--sv-color-border-strong);
50
+ border-radius: var(--sv-radius-full);
51
+ transform: translate(-50%, -50%);
52
+ }
53
+
54
+ @media (hover: hover) {
55
+ .handle:hover {
56
+ background: var(--sv-color-accent-subtle);
57
+ }
58
+ }
59
+
60
+ .handle:focus-visible {
61
+ background: var(--sv-color-accent-subtle);
62
+ outline: 2px solid var(--sv-color-focus-ring);
63
+ outline-offset: -2px;
64
+ }
65
+
66
+ @media (max-width: 720px) {
67
+ .splitPane,
68
+ .splitPane:not(:has(.handle)) {
69
+ grid-template-columns: 1fr;
70
+ min-height: 0;
71
+ }
72
+
73
+ .handle {
74
+ display: none;
75
+ }
76
+
77
+ .pane {
78
+ min-height: 220px;
79
+ }
80
+
81
+ .pane + .pane {
82
+ border-top: 1px solid var(--sv-color-border);
83
+ border-left: 0;
84
+ }
85
+ }
@@ -0,0 +1,59 @@
1
+ .badge {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ max-width: 100%;
5
+ min-height: 24px;
6
+ gap: var(--sv-space-1);
7
+ padding: 2px var(--sv-space-2);
8
+ border: 1px solid var(--sv-color-border);
9
+ border-radius: var(--sv-radius-full);
10
+ font-family: var(--sv-font-family);
11
+ font-size: var(--sv-font-size-xs);
12
+ font-weight: var(--sv-font-weight-semibold);
13
+ line-height: 1.2;
14
+ white-space: nowrap;
15
+ }
16
+
17
+ .dot {
18
+ width: 7px;
19
+ height: 7px;
20
+ flex: 0 0 auto;
21
+ border-radius: var(--sv-radius-full);
22
+ background: currentColor;
23
+ }
24
+
25
+ .label {
26
+ min-width: 0;
27
+ overflow: hidden;
28
+ text-overflow: ellipsis;
29
+ }
30
+
31
+ .neutral {
32
+ color: var(--sv-color-text-muted);
33
+ background: var(--sv-color-surface-sunken);
34
+ border-color: var(--sv-color-border);
35
+ }
36
+
37
+ .info {
38
+ color: var(--sv-color-info-text);
39
+ background: var(--sv-color-info-surface);
40
+ border-color: var(--sv-color-info-border);
41
+ }
42
+
43
+ .success {
44
+ color: var(--sv-color-success-text);
45
+ background: var(--sv-color-success-surface);
46
+ border-color: var(--sv-color-success-border);
47
+ }
48
+
49
+ .warning {
50
+ color: var(--sv-color-warning-text);
51
+ background: var(--sv-color-warning-surface);
52
+ border-color: var(--sv-color-warning-border);
53
+ }
54
+
55
+ .error {
56
+ color: var(--sv-color-error-text);
57
+ background: var(--sv-color-error-surface);
58
+ border-color: var(--sv-color-error-border);
59
+ }
@@ -38,8 +38,10 @@
38
38
  transition: opacity 0.12s ease;
39
39
  }
40
40
 
41
- .dismiss:hover {
42
- opacity: 1;
41
+ @media (hover: hover) {
42
+ .dismiss:hover {
43
+ opacity: 1;
44
+ }
43
45
  }
44
46
 
45
47
  .dismiss:focus-visible {
@@ -44,6 +44,8 @@
44
44
  font-weight: var(--sv-font-weight-medium);
45
45
  }
46
46
 
47
- .inactive:hover {
48
- color: var(--sv-color-text-primary);
47
+ @media (hover: hover) {
48
+ .inactive:hover {
49
+ color: var(--sv-color-text-primary);
50
+ }
49
51
  }