@sovereignfs/ui 0.21.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 (141) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +24 -0
  3. package/dist/Avatar.module.css +38 -0
  4. package/dist/Badge.module.css +88 -0
  5. package/dist/Button.module.css +77 -0
  6. package/dist/Card.module.css +34 -0
  7. package/dist/Dialog.module.css +188 -0
  8. package/dist/Drawer.module.css +30 -0
  9. package/dist/EmptyState.module.css +27 -0
  10. package/dist/FormField.module.css +28 -0
  11. package/dist/Icon.module.css +22 -0
  12. package/dist/Input.module.css +37 -0
  13. package/dist/NavTabs.module.css +48 -0
  14. package/dist/PageHeader.module.css +32 -0
  15. package/dist/Popover.module.css +44 -0
  16. package/dist/SegmentedControl.module.css +53 -0
  17. package/dist/Select.module.css +69 -0
  18. package/dist/Spinner.module.css +39 -0
  19. package/dist/SystemBanner.module.css +67 -0
  20. package/dist/Tabs.module.css +49 -0
  21. package/dist/Toast.module.css +130 -0
  22. package/dist/Toggle.module.css +52 -0
  23. package/dist/Tooltip.module.css +51 -0
  24. package/dist/index.d.ts +327 -0
  25. package/dist/index.js +1370 -0
  26. package/dist/tokens/primitives.css +99 -0
  27. package/dist/tokens/semantic.css +111 -0
  28. package/dist/tokens.css +5 -0
  29. package/package.json +50 -0
  30. package/src/components/Avatar/Avatar.module.css +38 -0
  31. package/src/components/Avatar/Avatar.tsx +42 -0
  32. package/src/components/Badge/Badge.module.css +88 -0
  33. package/src/components/Badge/Badge.stories.tsx +118 -0
  34. package/src/components/Badge/Badge.tsx +59 -0
  35. package/src/components/Badge/__tests__/Badge.test.tsx +64 -0
  36. package/src/components/Button/Button.module.css +77 -0
  37. package/src/components/Button/Button.stories.tsx +76 -0
  38. package/src/components/Button/Button.tsx +36 -0
  39. package/src/components/Button/__tests__/Button.test.tsx +37 -0
  40. package/src/components/Card/Card.module.css +34 -0
  41. package/src/components/Card/Card.tsx +34 -0
  42. package/src/components/Dialog/Dialog.module.css +188 -0
  43. package/src/components/Dialog/Dialog.stories.tsx +97 -0
  44. package/src/components/Dialog/Dialog.tsx +137 -0
  45. package/src/components/Dialog/__tests__/Dialog.test.tsx +73 -0
  46. package/src/components/Drawer/Drawer.module.css +30 -0
  47. package/src/components/Drawer/Drawer.stories.tsx +88 -0
  48. package/src/components/Drawer/Drawer.tsx +103 -0
  49. package/src/components/Drawer/__tests__/Drawer.test.tsx +56 -0
  50. package/src/components/EmptyState/EmptyState.module.css +27 -0
  51. package/src/components/EmptyState/EmptyState.tsx +27 -0
  52. package/src/components/FormField/FormField.module.css +28 -0
  53. package/src/components/FormField/FormField.tsx +53 -0
  54. package/src/components/Icon/Icon.module.css +22 -0
  55. package/src/components/Icon/Icon.stories.tsx +94 -0
  56. package/src/components/Icon/Icon.tsx +43 -0
  57. package/src/components/Icon/__tests__/Icon.test.tsx +40 -0
  58. package/src/components/Icon/icons/activity.tsx +20 -0
  59. package/src/components/Icon/icons/alert-triangle.tsx +22 -0
  60. package/src/components/Icon/icons/bell.tsx +21 -0
  61. package/src/components/Icon/icons/check.tsx +20 -0
  62. package/src/components/Icon/icons/chevron-down.tsx +20 -0
  63. package/src/components/Icon/icons/chevron-left.tsx +20 -0
  64. package/src/components/Icon/icons/chevron-right.tsx +20 -0
  65. package/src/components/Icon/icons/chevron-up.tsx +20 -0
  66. package/src/components/Icon/icons/eye-off.tsx +23 -0
  67. package/src/components/Icon/icons/eye.tsx +21 -0
  68. package/src/components/Icon/icons/grid-2x2.tsx +22 -0
  69. package/src/components/Icon/icons/house.tsx +21 -0
  70. package/src/components/Icon/icons/index.ts +122 -0
  71. package/src/components/Icon/icons/info.tsx +22 -0
  72. package/src/components/Icon/icons/lock.tsx +21 -0
  73. package/src/components/Icon/icons/log-out.tsx +22 -0
  74. package/src/components/Icon/icons/mail.tsx +21 -0
  75. package/src/components/Icon/icons/package.tsx +23 -0
  76. package/src/components/Icon/icons/pencil.tsx +21 -0
  77. package/src/components/Icon/icons/plus.tsx +21 -0
  78. package/src/components/Icon/icons/rotate-ccw.tsx +21 -0
  79. package/src/components/Icon/icons/search.tsx +21 -0
  80. package/src/components/Icon/icons/settings.tsx +21 -0
  81. package/src/components/Icon/icons/shield.tsx +20 -0
  82. package/src/components/Icon/icons/sliders-horizontal.tsx +28 -0
  83. package/src/components/Icon/icons/terminal.tsx +19 -0
  84. package/src/components/Icon/icons/trash-2.tsx +24 -0
  85. package/src/components/Icon/icons/user.tsx +21 -0
  86. package/src/components/Icon/icons/x.tsx +21 -0
  87. package/src/components/Input/Input.module.css +37 -0
  88. package/src/components/Input/Input.stories.tsx +86 -0
  89. package/src/components/Input/Input.tsx +14 -0
  90. package/src/components/Input/__tests__/Input.test.tsx +22 -0
  91. package/src/components/NavTabs/NavTabs.module.css +48 -0
  92. package/src/components/NavTabs/NavTabs.tsx +33 -0
  93. package/src/components/PageHeader/PageHeader.module.css +32 -0
  94. package/src/components/PageHeader/PageHeader.tsx +21 -0
  95. package/src/components/Popover/Popover.module.css +44 -0
  96. package/src/components/Popover/Popover.stories.tsx +232 -0
  97. package/src/components/Popover/Popover.tsx +75 -0
  98. package/src/components/Popover/__tests__/Popover.test.tsx +98 -0
  99. package/src/components/SegmentedControl/SegmentedControl.module.css +53 -0
  100. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +118 -0
  101. package/src/components/SegmentedControl/SegmentedControl.tsx +56 -0
  102. package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +63 -0
  103. package/src/components/Select/Select.module.css +69 -0
  104. package/src/components/Select/Select.stories.tsx +115 -0
  105. package/src/components/Select/Select.tsx +40 -0
  106. package/src/components/Select/__tests__/Select.test.tsx +59 -0
  107. package/src/components/Spinner/Spinner.module.css +39 -0
  108. package/src/components/Spinner/Spinner.tsx +23 -0
  109. package/src/components/SystemBanner/SystemBanner.module.css +67 -0
  110. package/src/components/SystemBanner/SystemBanner.stories.tsx +86 -0
  111. package/src/components/SystemBanner/SystemBanner.tsx +50 -0
  112. package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +50 -0
  113. package/src/components/Tabs/Tabs.module.css +49 -0
  114. package/src/components/Tabs/Tabs.stories.tsx +100 -0
  115. package/src/components/Tabs/Tabs.tsx +46 -0
  116. package/src/components/Tabs/__tests__/Tabs.test.tsx +45 -0
  117. package/src/components/Toast/Toast.module.css +130 -0
  118. package/src/components/Toast/Toast.stories.tsx +191 -0
  119. package/src/components/Toast/Toast.tsx +139 -0
  120. package/src/components/Toggle/Toggle.module.css +52 -0
  121. package/src/components/Toggle/Toggle.stories.tsx +95 -0
  122. package/src/components/Toggle/Toggle.tsx +39 -0
  123. package/src/components/Toggle/__tests__/Toggle.test.tsx +47 -0
  124. package/src/components/Tooltip/Tooltip.module.css +51 -0
  125. package/src/components/Tooltip/Tooltip.tsx +30 -0
  126. package/src/css-modules.d.ts +4 -0
  127. package/src/index.ts +45 -0
  128. package/src/stories/Avatar.stories.tsx +44 -0
  129. package/src/stories/Card.stories.tsx +35 -0
  130. package/src/stories/DesignSystemOverview.stories.tsx +1683 -0
  131. package/src/stories/EmptyState.stories.tsx +33 -0
  132. package/src/stories/FormField.stories.tsx +47 -0
  133. package/src/stories/MobilePatterns.stories.tsx +1285 -0
  134. package/src/stories/NavTabs.stories.tsx +35 -0
  135. package/src/stories/PageHeader.stories.tsx +31 -0
  136. package/src/stories/Spinner.stories.tsx +25 -0
  137. package/src/stories/TokenGallery.stories.tsx +355 -0
  138. package/src/stories/Tooltip.stories.tsx +59 -0
  139. package/src/tokens/primitives.css +99 -0
  140. package/src/tokens/semantic.css +111 -0
  141. package/src/tokens.css +5 -0
@@ -0,0 +1,44 @@
1
+ .container {
2
+ position: relative;
3
+ display: inline-flex;
4
+ }
5
+
6
+ .panel {
7
+ position: absolute;
8
+ top: calc(100% + var(--sv-space-2));
9
+ z-index: 60;
10
+ background: var(--sv-color-surface);
11
+ border: 1px solid var(--sv-color-border);
12
+ border-radius: var(--sv-radius-2xl);
13
+ box-shadow: var(--sv-shadow-popover);
14
+ overflow: hidden;
15
+ /* Slide in from slightly above */
16
+ animation: sv-popover-in 0.12s ease;
17
+ }
18
+
19
+ @keyframes sv-popover-in {
20
+ from {
21
+ opacity: 0;
22
+ transform: translateY(-4px);
23
+ }
24
+ to {
25
+ opacity: 1;
26
+ transform: translateY(0);
27
+ }
28
+ }
29
+
30
+ @media (prefers-reduced-motion: reduce) {
31
+ .panel {
32
+ animation: none;
33
+ }
34
+ }
35
+
36
+ /* Alignment: right edge of panel aligns to right edge of trigger */
37
+ .right {
38
+ right: 0;
39
+ }
40
+
41
+ /* Alignment: left edge of panel aligns to left edge of trigger */
42
+ .left {
43
+ left: 0;
44
+ }
@@ -0,0 +1,53 @@
1
+ .track {
2
+ display: inline-flex;
3
+ background: var(--sv-color-surface-sunken);
4
+ border-radius: var(--sv-radius-full);
5
+ border: 1px solid var(--sv-color-border);
6
+ padding: 2px;
7
+ gap: 2px;
8
+ }
9
+
10
+ /* Size variants */
11
+ .md .segment {
12
+ padding: var(--sv-space-1) var(--sv-space-3);
13
+ font-size: var(--sv-font-size-sm);
14
+ }
15
+
16
+ .sm .segment {
17
+ padding: 2px var(--sv-space-2);
18
+ font-size: var(--sv-font-size-caption);
19
+ }
20
+
21
+ .segment {
22
+ border: none;
23
+ border-radius: var(--sv-radius-full);
24
+ font-family: var(--sv-font-family);
25
+ font-weight: var(--sv-font-weight-medium);
26
+ line-height: 1;
27
+ cursor: pointer;
28
+ transition:
29
+ background-color 0.12s ease,
30
+ color 0.12s ease,
31
+ box-shadow 0.12s ease;
32
+ white-space: nowrap;
33
+ }
34
+
35
+ .segment:focus-visible {
36
+ outline: 2px solid var(--sv-color-focus-ring);
37
+ outline-offset: 1px;
38
+ }
39
+
40
+ .active {
41
+ background: var(--sv-color-surface);
42
+ color: var(--sv-color-text-primary);
43
+ box-shadow: var(--sv-shadow-card);
44
+ }
45
+
46
+ .inactive {
47
+ background: transparent;
48
+ color: var(--sv-color-text-muted);
49
+ }
50
+
51
+ .inactive:hover {
52
+ color: var(--sv-color-text-primary);
53
+ }
@@ -0,0 +1,69 @@
1
+ /* Wrapper provides the positioning context for the custom chevron */
2
+ .wrapper {
3
+ position: relative;
4
+ display: block;
5
+ width: 100%;
6
+ }
7
+
8
+ .select {
9
+ display: block;
10
+ width: 100%;
11
+ /* Right-pad to clear the custom chevron */
12
+ padding: var(--sv-space-2) var(--sv-space-8) var(--sv-space-2) var(--sv-space-3);
13
+ font-family: var(--sv-font-family);
14
+ font-size: var(--sv-font-size-sm);
15
+ line-height: 1.5;
16
+ color: var(--sv-color-text-primary);
17
+ background-color: var(--sv-color-surface);
18
+ border: 1px solid var(--sv-color-border-strong);
19
+ border-radius: var(--sv-radius-md);
20
+ cursor: pointer;
21
+ /* Remove native OS chevron */
22
+ appearance: none;
23
+ transition:
24
+ border-color 0.15s ease,
25
+ box-shadow 0.15s ease;
26
+ }
27
+
28
+ /* sm — compact for table cells and inline controls */
29
+ .sm .select {
30
+ padding: 2px var(--sv-space-6) 2px var(--sv-space-2);
31
+ font-size: var(--sv-font-size-sm);
32
+ line-height: 1.4;
33
+ border-radius: var(--sv-radius-sm);
34
+ }
35
+
36
+ .select:focus-visible {
37
+ outline: none;
38
+ border-color: var(--sv-color-accent);
39
+ box-shadow: 0 0 0 2px var(--sv-color-focus-ring);
40
+ }
41
+
42
+ .select:disabled {
43
+ opacity: 0.5;
44
+ cursor: not-allowed;
45
+ }
46
+
47
+ /* Compact size variant — reduced padding for table-cell usage */
48
+ .sm .select {
49
+ padding-top: var(--sv-space-1);
50
+ padding-bottom: var(--sv-space-1);
51
+ font-size: var(--sv-font-size-sm);
52
+ }
53
+
54
+ /* SVG chevron — matches the icon stroke style used across the UI */
55
+ .chevron {
56
+ pointer-events: none;
57
+ position: absolute;
58
+ right: var(--sv-space-3);
59
+ top: 50%;
60
+ transform: translateY(-50%);
61
+ color: var(--sv-color-text-muted);
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ .sm .chevron {
66
+ right: var(--sv-space-2);
67
+ width: 10px;
68
+ height: 10px;
69
+ }
@@ -0,0 +1,39 @@
1
+ @keyframes spin {
2
+ to {
3
+ transform: rotate(360deg);
4
+ }
5
+ }
6
+
7
+ .spinner {
8
+ display: inline-block;
9
+ border-radius: 50%;
10
+ border-style: solid;
11
+ border-color: var(--sv-color-border);
12
+ border-top-color: var(--sv-color-accent);
13
+ animation: spin 0.7s linear infinite;
14
+ flex-shrink: 0;
15
+ }
16
+
17
+ @media (prefers-reduced-motion: reduce) {
18
+ .spinner {
19
+ animation-play-state: paused;
20
+ }
21
+ }
22
+
23
+ .sm {
24
+ width: var(--sv-icon-size-sm);
25
+ height: var(--sv-icon-size-sm);
26
+ border-width: 2px;
27
+ }
28
+
29
+ .md {
30
+ width: var(--sv-icon-size-md);
31
+ height: var(--sv-icon-size-md);
32
+ border-width: 2px;
33
+ }
34
+
35
+ .lg {
36
+ width: var(--sv-icon-size-lg);
37
+ height: var(--sv-icon-size-lg);
38
+ border-width: 3px;
39
+ }
@@ -0,0 +1,67 @@
1
+ .banner {
2
+ position: sticky;
3
+ top: 0;
4
+ /* Sits above sidebar rail (z-index ~10) but below dialogs/overlays (z-index 100) */
5
+ z-index: 50;
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ gap: var(--sv-space-3);
10
+ padding: var(--sv-space-2) var(--sv-space-4);
11
+ border-bottom-width: 1px;
12
+ border-bottom-style: solid;
13
+ font-family: var(--sv-font-family);
14
+ font-size: var(--sv-font-size-sm);
15
+ line-height: 1.4;
16
+ }
17
+
18
+ .content {
19
+ flex: 1;
20
+ text-align: center;
21
+ }
22
+
23
+ /* Dismiss button — inherits the variant text colour */
24
+ .dismiss {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ flex-shrink: 0;
29
+ width: 24px;
30
+ height: 24px;
31
+ padding: 0;
32
+ border: none;
33
+ border-radius: var(--sv-radius-sm);
34
+ background: transparent;
35
+ color: inherit;
36
+ cursor: pointer;
37
+ opacity: 0.7;
38
+ transition: opacity 0.12s ease;
39
+ }
40
+
41
+ .dismiss:hover {
42
+ opacity: 1;
43
+ }
44
+
45
+ .dismiss:focus-visible {
46
+ outline: 2px solid currentColor;
47
+ outline-offset: 1px;
48
+ }
49
+
50
+ /* Variants */
51
+ .info {
52
+ background: var(--sv-color-info-surface);
53
+ color: var(--sv-color-info-text);
54
+ border-bottom-color: var(--sv-color-info-border);
55
+ }
56
+
57
+ .warning {
58
+ background: var(--sv-color-warning-surface);
59
+ color: var(--sv-color-warning-text);
60
+ border-bottom-color: var(--sv-color-warning-border);
61
+ }
62
+
63
+ .error {
64
+ background: var(--sv-color-error-surface);
65
+ color: var(--sv-color-error-text);
66
+ border-bottom-color: var(--sv-color-error-border);
67
+ }
@@ -0,0 +1,49 @@
1
+ .tablist {
2
+ display: flex;
3
+ border-bottom: 1px solid var(--sv-color-border);
4
+ /* Horizontal scroll on mobile — scrollbar hidden for clean appearance */
5
+ overflow-x: auto;
6
+ scrollbar-width: none;
7
+ }
8
+
9
+ .tablist::-webkit-scrollbar {
10
+ display: none;
11
+ }
12
+
13
+ .tab {
14
+ flex-shrink: 0;
15
+ padding: var(--sv-space-2) var(--sv-space-4);
16
+ border: none;
17
+ border-bottom: 2px solid transparent;
18
+ /* Negative margin pulls the 2px border flush with the tablist border-bottom */
19
+ margin-bottom: -1px;
20
+ background: transparent;
21
+ font-family: var(--sv-font-family);
22
+ font-size: var(--sv-font-size-sm);
23
+ line-height: 1;
24
+ cursor: pointer;
25
+ transition:
26
+ color 0.12s ease,
27
+ border-color 0.12s ease;
28
+ white-space: nowrap;
29
+ }
30
+
31
+ .tab:focus-visible {
32
+ outline: 2px solid var(--sv-color-focus-ring);
33
+ outline-offset: -2px;
34
+ }
35
+
36
+ .active {
37
+ color: var(--sv-color-text-primary);
38
+ font-weight: var(--sv-font-weight-semibold);
39
+ border-bottom-color: var(--sv-color-accent);
40
+ }
41
+
42
+ .inactive {
43
+ color: var(--sv-color-text-muted);
44
+ font-weight: var(--sv-font-weight-medium);
45
+ }
46
+
47
+ .inactive:hover {
48
+ color: var(--sv-color-text-primary);
49
+ }
@@ -0,0 +1,130 @@
1
+ /* Toast — transient notification snackbar.
2
+ *
3
+ * Fixed top-right, 320px. Slides in from above. Each toast auto-dismisses
4
+ * after `duration` ms. Leading status icon reinforces category at a glance.
5
+ *
6
+ * Category variants map to the semantic status tokens (info/success/warning/
7
+ * error) so the visual language is consistent with banners and badges.
8
+ * The announcement category uses the accent colour (neutral brand highlight).
9
+ * `security` matches the error palette — draws maximum attention. */
10
+
11
+ .region {
12
+ position: fixed;
13
+ top: var(--sv-space-4);
14
+ right: var(--sv-space-4);
15
+ z-index: 200;
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: var(--sv-space-2);
19
+ width: 320px;
20
+ max-width: calc(100vw - var(--sv-space-8));
21
+ pointer-events: none;
22
+ }
23
+
24
+ .toast {
25
+ display: flex;
26
+ align-items: flex-start;
27
+ gap: var(--sv-space-3);
28
+ padding: var(--sv-space-3) var(--sv-space-4);
29
+ background: var(--sv-color-surface-raised);
30
+ color: var(--sv-color-text-primary);
31
+ border: 1px solid var(--sv-color-border);
32
+ border-radius: var(--sv-radius-md);
33
+ box-shadow: var(--sv-shadow-overlay);
34
+ pointer-events: all;
35
+ animation: toastIn 200ms ease-out;
36
+ }
37
+
38
+ .toast.exiting {
39
+ animation: toastOut 200ms ease-in forwards;
40
+ }
41
+
42
+ /* Leading status icon */
43
+ .icon {
44
+ flex: 0 0 auto;
45
+ display: flex;
46
+ align-items: center;
47
+ padding-top: 1px;
48
+ color: var(--sv-color-border-strong);
49
+ }
50
+
51
+ .toast.info .icon,
52
+ .toast.announcement .icon {
53
+ color: var(--sv-color-accent);
54
+ }
55
+
56
+ .toast.success .icon {
57
+ color: var(--sv-color-success-text);
58
+ }
59
+
60
+ .toast.warning .icon {
61
+ color: var(--sv-color-warning-text);
62
+ }
63
+
64
+ .toast.error .icon,
65
+ .toast.security .icon {
66
+ color: var(--sv-color-error-text);
67
+ }
68
+
69
+ .body {
70
+ flex: 1 1 auto;
71
+ min-width: 0;
72
+ }
73
+
74
+ .title {
75
+ font-size: var(--sv-font-size-sm);
76
+ font-weight: 600;
77
+ line-height: 1.4;
78
+ color: var(--sv-color-text-primary);
79
+ }
80
+
81
+ .message {
82
+ font-size: var(--sv-font-size-sm);
83
+ color: var(--sv-color-text-secondary);
84
+ margin-top: var(--sv-space-1);
85
+ line-height: 1.4;
86
+ }
87
+
88
+ .close {
89
+ flex: 0 0 auto;
90
+ background: none;
91
+ border: none;
92
+ padding: 0;
93
+ cursor: pointer;
94
+ color: var(--sv-color-text-tertiary);
95
+ line-height: 1;
96
+ font-size: var(--sv-font-size-base);
97
+ }
98
+
99
+ .close:hover {
100
+ color: var(--sv-color-text-primary);
101
+ }
102
+
103
+ @keyframes toastIn {
104
+ from {
105
+ opacity: 0;
106
+ transform: translateY(-8px);
107
+ }
108
+ to {
109
+ opacity: 1;
110
+ transform: translateY(0);
111
+ }
112
+ }
113
+
114
+ @keyframes toastOut {
115
+ from {
116
+ opacity: 1;
117
+ transform: translateY(0);
118
+ }
119
+ to {
120
+ opacity: 0;
121
+ transform: translateY(-8px);
122
+ }
123
+ }
124
+
125
+ @media (prefers-reduced-motion: reduce) {
126
+ .toast,
127
+ .toast.exiting {
128
+ animation: none;
129
+ }
130
+ }
@@ -0,0 +1,52 @@
1
+ .track {
2
+ /* 38×22px pill — spec dimensions */
3
+ display: inline-flex;
4
+ align-items: center;
5
+ width: 38px;
6
+ height: 22px;
7
+ border-radius: var(--sv-radius-full);
8
+ border: none;
9
+ padding: 0;
10
+ cursor: pointer;
11
+ flex-shrink: 0;
12
+ transition: background-color 0.15s ease;
13
+ position: relative;
14
+ }
15
+
16
+ .track:focus-visible {
17
+ outline: 2px solid var(--sv-color-focus-ring);
18
+ outline-offset: 2px;
19
+ }
20
+
21
+ .track:disabled {
22
+ opacity: 0.5;
23
+ cursor: not-allowed;
24
+ }
25
+
26
+ .on {
27
+ background-color: var(--sv-color-accent);
28
+ }
29
+
30
+ .off {
31
+ background-color: var(--sv-color-border-strong);
32
+ }
33
+
34
+ /* 18px thumb, 2px inset on each side */
35
+ .thumb {
36
+ position: absolute;
37
+ width: 18px;
38
+ height: 18px;
39
+ border-radius: var(--sv-radius-full);
40
+ background: var(--sv-white);
41
+ transition: transform 0.15s ease;
42
+ left: 2px;
43
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
44
+ }
45
+
46
+ .on .thumb {
47
+ transform: translateX(16px); /* 38 - 18 - 2 - 2 = 16 */
48
+ }
49
+
50
+ .off .thumb {
51
+ transform: translateX(0);
52
+ }
@@ -0,0 +1,51 @@
1
+ .wrapper {
2
+ position: relative;
3
+ display: inline-flex;
4
+ }
5
+
6
+ .tip {
7
+ position: absolute;
8
+ z-index: 50;
9
+ white-space: nowrap;
10
+ padding: var(--sv-space-1) var(--sv-space-2);
11
+ background: var(--sv-color-surface-raised);
12
+ border: 1px solid var(--sv-color-border);
13
+ border-radius: var(--sv-radius-md);
14
+ box-shadow: var(--sv-shadow-popover);
15
+ font-size: var(--sv-font-size-xs);
16
+ color: var(--sv-color-text-primary);
17
+ font-family: var(--sv-font-family);
18
+ pointer-events: none;
19
+ opacity: 0;
20
+ transition: opacity 0.1s ease;
21
+ }
22
+
23
+ .wrapper:hover .tip,
24
+ .wrapper:focus-within .tip {
25
+ opacity: 1;
26
+ }
27
+
28
+ /* Placement */
29
+ .top {
30
+ bottom: calc(100% + var(--sv-space-2));
31
+ left: 50%;
32
+ transform: translateX(-50%);
33
+ }
34
+
35
+ .bottom {
36
+ top: calc(100% + var(--sv-space-2));
37
+ left: 50%;
38
+ transform: translateX(-50%);
39
+ }
40
+
41
+ .left {
42
+ right: calc(100% + var(--sv-space-2));
43
+ top: 50%;
44
+ transform: translateY(-50%);
45
+ }
46
+
47
+ .right {
48
+ left: calc(100% + var(--sv-space-2));
49
+ top: 50%;
50
+ transform: translateY(-50%);
51
+ }