aplance-ui 1.0.1

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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -0
  3. package/dist/api.d.ts +175 -0
  4. package/dist/api.js +1954 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/frameworks/astro/astro.config.snippet.md +23 -0
  7. package/dist/frameworks/astro/postcss.config.mjs.template +20 -0
  8. package/dist/frameworks/astro/vite-inline.snippet.md +22 -0
  9. package/dist/frameworks/electron-vite/vite.config.snippet.md +44 -0
  10. package/dist/frameworks/next/layout-import.snippet.md +19 -0
  11. package/dist/frameworks/next/next.config.snippet.md +15 -0
  12. package/dist/frameworks/next/postcss.config.js.template +16 -0
  13. package/dist/frameworks/tanstack-start/postcss.config.ts.template +17 -0
  14. package/dist/frameworks/tanstack-start/vite-inline.snippet.md +22 -0
  15. package/dist/frameworks/vite-react/postcss.config.ts.template +16 -0
  16. package/dist/frameworks/vite-react/vite-inline.snippet.md +20 -0
  17. package/dist/frameworks/vite-react/vite.config.ts.template +48 -0
  18. package/dist/index.js +2047 -0
  19. package/dist/index.js.map +1 -0
  20. package/package.json +63 -0
  21. package/registry/components/primitives/icon/icon.module.css +63 -0
  22. package/registry/components/primitives/icon/icon.tsx +53 -0
  23. package/registry/components/primitives/modal/modal-context.ts +19 -0
  24. package/registry/components/primitives/modal/modal.module.css +415 -0
  25. package/registry/components/primitives/modal/modal.tsx +550 -0
  26. package/registry/components/primitives/popover/popover-context.ts +28 -0
  27. package/registry/components/primitives/popover/popover.module.css +28 -0
  28. package/registry/components/primitives/popover/popover.tsx +465 -0
  29. package/registry/components/ui/accordion/accordion.module.css +208 -0
  30. package/registry/components/ui/accordion/accordion.tsx +327 -0
  31. package/registry/components/ui/avatar/avatar.module.css +32 -0
  32. package/registry/components/ui/avatar/avatar.tsx +37 -0
  33. package/registry/components/ui/badge/badge.module.css +103 -0
  34. package/registry/components/ui/badge/badge.tsx +29 -0
  35. package/registry/components/ui/button/button.module.css +216 -0
  36. package/registry/components/ui/button/button.tsx +133 -0
  37. package/registry/components/ui/chart/chart.module.css +168 -0
  38. package/registry/components/ui/chart/chart.tsx +1293 -0
  39. package/registry/components/ui/chart/types.ts +210 -0
  40. package/registry/components/ui/chart/utils.ts +59 -0
  41. package/registry/components/ui/copy-button/copy-button.module.css +21 -0
  42. package/registry/components/ui/copy-button/copy-button.tsx +123 -0
  43. package/registry/components/ui/countdown/countdown.module.css +5 -0
  44. package/registry/components/ui/countdown/countdown.tsx +152 -0
  45. package/registry/components/ui/data-table/checkbox-column.tsx +59 -0
  46. package/registry/components/ui/data-table/data-table.module.css +594 -0
  47. package/registry/components/ui/data-table/data-table.tsx +1109 -0
  48. package/registry/components/ui/data-table/filter-bar.tsx +33 -0
  49. package/registry/components/ui/data-table/pagination-bar.tsx +125 -0
  50. package/registry/components/ui/data-table/types.ts +594 -0
  51. package/registry/components/ui/data-table/useDataTable.tsx +573 -0
  52. package/registry/components/ui/data-table/utils.ts +445 -0
  53. package/registry/components/ui/empty-state/empty-state.module.css +26 -0
  54. package/registry/components/ui/empty-state/empty-state.tsx +32 -0
  55. package/registry/components/ui/input-fields/color-picker/color-palette.module.css +102 -0
  56. package/registry/components/ui/input-fields/color-picker/color-palette.tsx +92 -0
  57. package/registry/components/ui/input-fields/color-picker/color-picker.module.css +29 -0
  58. package/registry/components/ui/input-fields/color-picker/color-picker.tsx +168 -0
  59. package/registry/components/ui/input-fields/common/field-message/field-message.module.css +28 -0
  60. package/registry/components/ui/input-fields/common/field-message/field-message.tsx +39 -0
  61. package/registry/components/ui/input-fields/common/input-field-common.module.css +318 -0
  62. package/registry/components/ui/input-fields/common/input-field-native.module.css +267 -0
  63. package/registry/components/ui/input-fields/common/picker-popover-common.module.css +40 -0
  64. package/registry/components/ui/input-fields/common/select-button/select-button.module.css +9 -0
  65. package/registry/components/ui/input-fields/common/select-button/select-button.tsx +171 -0
  66. package/registry/components/ui/input-fields/common/types.ts +38 -0
  67. package/registry/components/ui/input-fields/custom-select/custom-select.module.css +56 -0
  68. package/registry/components/ui/input-fields/custom-select/custom-select.tsx +403 -0
  69. package/registry/components/ui/input-fields/date-picker/calendar.module.css +311 -0
  70. package/registry/components/ui/input-fields/date-picker/calendar.tsx +186 -0
  71. package/registry/components/ui/input-fields/date-picker/date-picker.module.css +57 -0
  72. package/registry/components/ui/input-fields/date-picker/date-picker.tsx +945 -0
  73. package/registry/components/ui/input-fields/date-picker/types.ts +102 -0
  74. package/registry/components/ui/input-fields/date-picker/utils.ts +236 -0
  75. package/registry/components/ui/input-fields/emoji-picker/emoji-palette.module.css +90 -0
  76. package/registry/components/ui/input-fields/emoji-picker/emoji-palette.tsx +168 -0
  77. package/registry/components/ui/input-fields/emoji-picker/emoji-picker.module.css +8 -0
  78. package/registry/components/ui/input-fields/emoji-picker/emoji-picker.tsx +102 -0
  79. package/registry/components/ui/input-fields/emoji-picker/utils.ts +505 -0
  80. package/registry/components/ui/input-fields/field-group/field-group.module.css +86 -0
  81. package/registry/components/ui/input-fields/field-group/field-group.tsx +86 -0
  82. package/registry/components/ui/input-fields/input/input.tsx +319 -0
  83. package/registry/components/ui/input-fields/media-input/kind-icons.tsx +62 -0
  84. package/registry/components/ui/input-fields/media-input/media-input.module.css +294 -0
  85. package/registry/components/ui/input-fields/media-input/media-input.tsx +327 -0
  86. package/registry/components/ui/input-fields/media-input/media-preview.tsx +101 -0
  87. package/registry/components/ui/input-fields/media-input/types.ts +129 -0
  88. package/registry/components/ui/input-fields/media-input/useMediaUpload.tsx +302 -0
  89. package/registry/components/ui/input-fields/media-input/utils.ts +325 -0
  90. package/registry/components/ui/input-fields/otp-input/otp-input.module.css +24 -0
  91. package/registry/components/ui/input-fields/otp-input/otp-input.tsx +291 -0
  92. package/registry/components/ui/input-fields/select/select.tsx +118 -0
  93. package/registry/components/ui/input-fields/switch/switch.module.css +70 -0
  94. package/registry/components/ui/input-fields/switch/switch.tsx +59 -0
  95. package/registry/components/ui/input-fields/textarea/textarea.tsx +133 -0
  96. package/registry/components/ui/input-fields/time-picker/time-picker.module.css +57 -0
  97. package/registry/components/ui/input-fields/time-picker/time-picker.tsx +437 -0
  98. package/registry/components/ui/loader/loader.module.css +156 -0
  99. package/registry/components/ui/loader/loader.tsx +102 -0
  100. package/registry/components/ui/stepper/stepper.module.css +294 -0
  101. package/registry/components/ui/stepper/stepper.tsx +197 -0
  102. package/registry/components/ui/tabs/tabs.module.css +150 -0
  103. package/registry/components/ui/tabs/tabs.tsx +134 -0
  104. package/registry/components/ui/toast/toast.module.css +159 -0
  105. package/registry/components/ui/toast/toast.tsx +104 -0
  106. package/registry/components/ui/tooltip/tooltip.module.css +82 -0
  107. package/registry/components/ui/tooltip/tooltip.tsx +72 -0
  108. package/registry/global.d.ts +4 -0
  109. package/registry/hooks/useFocusOutside.tsx +33 -0
  110. package/registry/hooks/useResizeObserver.tsx +32 -0
  111. package/registry/lib/dom.ts +25 -0
  112. package/registry/lib/string.ts +23 -0
  113. package/registry/registry.json +719 -0
  114. package/registry/styles/app.css +240 -0
  115. package/registry/styles/components.css +78 -0
  116. package/registry/styles/fonts/geist-regular.woff2 +0 -0
  117. package/registry/styles/globals.css +7 -0
  118. package/registry/styles/media.css +25 -0
  119. package/registry/styles/utilities.css +90 -0
  120. package/registry/themes/colors/blue.css +37 -0
  121. package/registry/themes/colors/green.css +37 -0
  122. package/registry/themes/colors/rose.css +37 -0
  123. package/registry/themes/colors/slate.css +37 -0
  124. package/registry/themes/colors/stone.css +37 -0
  125. package/registry/themes/colors/violet.css +37 -0
  126. package/registry/themes/colors/zinc.css +37 -0
  127. package/registry/themes/radius/pill.css +8 -0
  128. package/registry/themes/radius/rounded.css +8 -0
  129. package/registry/themes/radius/sharp.css +8 -0
  130. package/registry/themes/spacing/compact.css +17 -0
  131. package/registry/themes/spacing/spacious.css +17 -0
  132. package/registry/tsconfig.json +25 -0
@@ -0,0 +1,216 @@
1
+ /* contains all possible styles of button component
2
+ with selectors for different variants and sizes and states
3
+ */
4
+
5
+ .base {
6
+ --button-fg: var(--color-foreground);
7
+ --button-placeholder: var(--button-fg);
8
+ --button-icon: var(--button-fg);
9
+
10
+ width: fit-content;
11
+ font-family: var(--font-family-1);
12
+ font-size: var(--font-size-sm);
13
+ font-weight: 500;
14
+ padding: var(--spacing-3) var(--spacing-6);
15
+ min-height: var(--spacing-11);
16
+ border-radius: var(--radius-md);
17
+ display: flex;
18
+ min-width: fit-content;
19
+ align-items: center;
20
+ justify-content: space-between;
21
+ gap: var(--spacing-4);
22
+ transition:
23
+ background-color var(--transition-base),
24
+ border-color var(--transition-base),
25
+ opacity var(--transition-base);
26
+ cursor: pointer;
27
+ white-space: nowrap;
28
+ outline: none;
29
+ border: none;
30
+
31
+ /* necessary */
32
+ line-height: 1.125;
33
+
34
+ svg {
35
+ cursor: pointer;
36
+ }
37
+
38
+ &.sm:not(.border_b):not(.box) {
39
+ padding: var(--spacing-2) var(--spacing-4);
40
+ min-height: var(--spacing-8);
41
+ font-size: var(--font-size-xs);
42
+ }
43
+
44
+ /* Box layout - perfect square with centered content */
45
+ &.box {
46
+ width: var(--spacing-11);
47
+ min-height: unset;
48
+ justify-content: center;
49
+ height: var(--spacing-11);
50
+ padding: var(--spacing-2);
51
+
52
+ &.sm {
53
+ width: var(--spacing-10);
54
+ height: var(--spacing-10);
55
+ font-size: var(--font-size-xs);
56
+ border-radius: var(--radius-md);
57
+ }
58
+ }
59
+
60
+ &.disabled,
61
+ &:disabled,
62
+ &[data-disabled="true"] {
63
+ pointer-events: none !important;
64
+ opacity: 0.5 !important;
65
+ cursor: not-allowed;
66
+ }
67
+ }
68
+
69
+ /*
70
+ CSS custom property contract: these vars publish the variant's colors so
71
+ consumers down the DOM tree can read them via inheritance - no JS, no prop
72
+ drilling, no hardcoded token duplication.
73
+
74
+ :is() covers both the direct variant (.primary) and the tone alias (.tone_primary).
75
+ .tone_* is applied by input-field components (Input, Select, Textarea, OTP, MediaInput,
76
+ SelectButton) via sButton[`tone_${variant}`]. It goes on both the field wrapper div
77
+ (so --button-* vars inherit into field_wrapper_variant for icons/placeholder coloring)
78
+ and on the input element itself alongside .base + the variant class for full button paint.
79
+
80
+ Consumers:
81
+ - input-field-common (.field_wrapper_variant) → maps to --field-content/placeholder/icon-color
82
+ - picker-popover-common (.picker_value_placeholder) → reads --button-placeholder directly
83
+ - media-input (.clear_all) → overrides --button-fg locally to muted
84
+ All consumers use var(--button-*, fallback) so they work standalone too.
85
+ */
86
+ :is(.primary, .tone_primary) {
87
+ --button-fg: var(--color-primary-foreground);
88
+ --button-placeholder: var(--color-primary-foreground);
89
+ --button-icon: var(--color-primary-foreground);
90
+ }
91
+
92
+ .primary {
93
+ background-color: var(--color-primary);
94
+ color: var(--button-fg);
95
+ box-shadow: var(--shadow-sm);
96
+
97
+ svg {
98
+ color: var(--button-icon);
99
+ }
100
+
101
+ &:hover:not(.disabled),
102
+ &:active:not(.disabled),
103
+ &.input_focus:focus,
104
+ &.active {
105
+ background-color: var(--color-primary-muted);
106
+ }
107
+ }
108
+
109
+ /* Same pattern as :is(.primary, .tone_primary) - vars shared between the direct
110
+ variant class and the tone alias used by composing components. */
111
+ :is(.secondary, .tone_secondary) {
112
+ --button-fg: var(--color-secondary-foreground);
113
+ --button-placeholder: var(--color-secondary-foreground);
114
+ --button-icon: var(--color-secondary-foreground);
115
+ }
116
+
117
+ .secondary {
118
+ background-color: var(--color-secondary);
119
+ color: var(--button-fg);
120
+ box-shadow: var(--shadow-sm);
121
+
122
+ svg {
123
+ color: var(--button-icon);
124
+ }
125
+
126
+ &:hover:not(.disabled),
127
+ &:active:not(.disabled),
128
+ &.input_focus:focus,
129
+ &.active {
130
+ background-color: var(--color-secondary-muted);
131
+ }
132
+ }
133
+
134
+ /* Same pattern - note .bordered uses --color-muted-foreground for placeholder
135
+ (subdued hint text) while fg/icon stay on full --color-foreground. */
136
+ :is(.bordered, .tone_bordered) {
137
+ --button-fg: var(--color-foreground);
138
+ --button-placeholder: var(--color-muted-foreground);
139
+ --button-icon: var(--color-foreground);
140
+ }
141
+
142
+ .bordered {
143
+ background-color: var(--color-background);
144
+ border: 1px solid var(--color-border);
145
+ color: var(--button-fg);
146
+ box-shadow: var(--shadow-sm);
147
+
148
+ svg {
149
+ color: var(--button-icon);
150
+ }
151
+
152
+ &:hover:not(.disabled),
153
+ &:active:not(.disabled),
154
+ &.input_focus:focus,
155
+ &.active {
156
+ background-color: var(--color-muted);
157
+ }
158
+ }
159
+
160
+ /* Same pattern - border_b (dotted underline style) shares the bordered color
161
+ palette, so tone alias gets the same muted-placeholder treatment. */
162
+ :is(.border_b, .tone_border_b) {
163
+ --button-fg: var(--color-foreground);
164
+ --button-placeholder: var(--color-muted-foreground);
165
+ --button-icon: var(--color-foreground);
166
+ }
167
+
168
+ .border_b {
169
+ padding: 0;
170
+ min-height: unset;
171
+ border-bottom: 2px dotted var(--color-border);
172
+ border-radius: 0;
173
+ color: var(--button-fg);
174
+ box-shadow: none;
175
+
176
+ svg {
177
+ color: var(--button-icon);
178
+ }
179
+
180
+ &.sm {
181
+ font-size: var(--font-size-xs);
182
+ }
183
+
184
+ &:hover:not(.disabled),
185
+ &.active:not(.disabled),
186
+ &.input_focus:focus,
187
+ &.active {
188
+ border-bottom-color: var(--color-foreground);
189
+ }
190
+ }
191
+
192
+ /* Same pattern - ghost is transparent background; tone alias lets composing
193
+ components inherit these vars without applying the full ghost layout rules. */
194
+ :is(.ghost, .tone_ghost) {
195
+ --button-fg: var(--color-foreground);
196
+ --button-placeholder: var(--color-muted-foreground);
197
+ --button-icon: var(--color-foreground);
198
+ }
199
+
200
+ .ghost {
201
+ width: fit-content;
202
+ height: fit-content;
203
+ color: var(--button-fg);
204
+ background-color: transparent;
205
+
206
+ svg {
207
+ color: var(--button-icon);
208
+ }
209
+
210
+ &:active:not(.disabled),
211
+ &:hover:not(.disabled),
212
+ &.input_focus:focus,
213
+ &.active {
214
+ background-color: var(--color-muted);
215
+ }
216
+ }
@@ -0,0 +1,133 @@
1
+ import type {
2
+ ElementType,
3
+ ComponentProps,
4
+ CSSProperties,
5
+ MouseEvent,
6
+ } from "react";
7
+ import clsx from "clsx";
8
+ import s from "./button.module.css";
9
+
10
+ export type ButtonVariant =
11
+ | "primary"
12
+ | "secondary"
13
+ | "bordered"
14
+ | "border_b"
15
+ | "ghost";
16
+ export type ButtonSize = "default" | "sm";
17
+ export type ButtonLayout = "default" | "box";
18
+ export type AsProp = "button" | "div" | "span" | "link";
19
+
20
+ type BaseProps = {
21
+ variant?: ButtonVariant;
22
+ size?: ButtonSize;
23
+ layout?: ButtonLayout;
24
+ className?: string;
25
+ style?: CSSProperties;
26
+ isActive?: boolean;
27
+ disabled?: boolean;
28
+ children?: React.ReactNode;
29
+ as?: AsProp;
30
+ };
31
+
32
+ // In case of different routers can update props
33
+ type LinkModeProps = BaseProps & {
34
+ as: "link";
35
+ href?: string;
36
+ to?: string;
37
+ target?: string;
38
+ rel?: string;
39
+ linkComponent?: ElementType;
40
+ linkProps?: Record<string, unknown>;
41
+ } & Omit<ComponentProps<"a">, "href" | "className" | "style" | "children">;
42
+
43
+ type ElementModeProps<T extends "button" | "div" | "span"> = BaseProps & {
44
+ as?: T;
45
+ href?: never;
46
+ } & ComponentProps<T>;
47
+
48
+ type ButtonProps =
49
+ | LinkModeProps
50
+ | ElementModeProps<"button">
51
+ | ElementModeProps<"div">
52
+ | ElementModeProps<"span">;
53
+
54
+ export const Button = ({
55
+ variant,
56
+ size = "default",
57
+ layout = "default",
58
+ className,
59
+ style,
60
+ isActive = false,
61
+ disabled = false,
62
+ children = "Button",
63
+ as = "button",
64
+ ...rest
65
+ }: ButtonProps) => {
66
+ const classes = clsx(
67
+ s.base,
68
+ variant && s[variant],
69
+ size === "sm" && s.sm,
70
+ layout === "box" && s.box,
71
+ isActive && s.active,
72
+ disabled && s.disabled,
73
+ className,
74
+ );
75
+
76
+ const handleClick = disabled
77
+ ? (e: MouseEvent) => e.preventDefault()
78
+ : undefined;
79
+
80
+ if (as === "link") {
81
+ const {
82
+ href,
83
+ to,
84
+ target,
85
+ rel,
86
+ linkComponent: LinkComponent,
87
+ linkProps,
88
+ ...linkRest
89
+ } = rest as LinkModeProps;
90
+ const LinkElement = LinkComponent ?? "a";
91
+ const resolvedHref = href ?? to ?? "#";
92
+ const safeRel = target === "_blank" ? (rel ?? "noopener noreferrer") : rel;
93
+
94
+ const customLinkProps: Record<string, unknown> = {
95
+ href: resolvedHref,
96
+ ...(to && { to }),
97
+ target,
98
+ rel: safeRel,
99
+ ...(linkProps ?? {}),
100
+ };
101
+
102
+ return (
103
+ <LinkElement
104
+ className={classes}
105
+ style={style}
106
+ aria-disabled={disabled}
107
+ onClick={handleClick}
108
+ {...(LinkComponent
109
+ ? customLinkProps
110
+ : { href: resolvedHref, target, rel: safeRel })}
111
+ {...linkRest}
112
+ >
113
+ {children}
114
+ </LinkElement>
115
+ );
116
+ }
117
+
118
+ const ButtonComponent = as as ElementType;
119
+
120
+ return (
121
+ <ButtonComponent
122
+ className={classes}
123
+ style={style}
124
+
125
+ aria-disabled={disabled}
126
+ {...(as === "button" && { disabled })}
127
+ onClick={handleClick}
128
+ {...rest}
129
+ >
130
+ {children}
131
+ </ButtonComponent>
132
+ );
133
+ };
@@ -0,0 +1,168 @@
1
+ /* ── Container card ───────────────────────────────────────────── */
2
+ .card {
3
+ width: 100%;
4
+ background-color: var(--color-background);
5
+ border: var(--border-1) solid var(--color-border);
6
+ border-radius: var(--radius-lg);
7
+ padding: var(--spacing-7);
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: var(--spacing-8);
11
+ }
12
+
13
+ .header {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: var(--spacing-2);
17
+ }
18
+
19
+ .title {
20
+ margin: 0;
21
+ font-size: var(--font-size-sm);
22
+ font-weight: var(--font-weight-semibold);
23
+ color: var(--color-foreground);
24
+ }
25
+
26
+ .description {
27
+ margin: 0;
28
+ font-size: var(--font-size-xs);
29
+ color: var(--color-muted-foreground);
30
+ }
31
+
32
+ .canvas {
33
+ width: 100%;
34
+ position: relative;
35
+ }
36
+
37
+ .plot {
38
+ width: 100%;
39
+ min-width: 0;
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: var(--spacing-5);
43
+ }
44
+
45
+ .plot :global(.recharts-cartesian-axis-tick-value) {
46
+ font-variant-numeric: tabular-nums;
47
+ }
48
+
49
+ /* ── Loading & empty states ──────────────────────────────────── */
50
+ /* Shared layout - same dimensions/centering so swapping between them
51
+ doesn't shift the surrounding card height. */
52
+ .loading_state,
53
+ .empty_state {
54
+ width: 100%;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ border-radius: var(--radius-md);
59
+ }
60
+
61
+ /* Empty state adds the muted text + dashed boundary; the loader
62
+ variant intentionally has neither so the spinner stands cleanly. */
63
+ .empty_state {
64
+ font-size: var(--font-size-sm);
65
+ color: var(--color-muted-foreground);
66
+ border: var(--border-1) dashed var(--color-border);
67
+ }
68
+
69
+ /* ── Tooltip ─────────────────────────────────────────────────── */
70
+ /* Match the page surface so series colors (designed against the page bg)
71
+ stay readable inside the tooltip. Border defines the floating edge
72
+ without relying on shadow alone. */
73
+ .tooltip {
74
+ min-width: 10rem;
75
+ background-color: var(--color-background);
76
+ color: var(--color-foreground);
77
+ border: var(--border-1) solid var(--color-border);
78
+ border-radius: var(--radius-md);
79
+ padding: var(--spacing-4) var(--spacing-5);
80
+ box-shadow: var(--shadow-md);
81
+ }
82
+
83
+ .tooltip_label {
84
+ font-size: var(--font-size-xs);
85
+ font-weight: var(--font-weight-semibold);
86
+ margin-bottom: var(--spacing-2);
87
+ }
88
+
89
+ .tooltip_row {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: var(--spacing-3);
93
+ font-size: var(--font-size-xs);
94
+
95
+ &:not(:last-child) {
96
+ margin-bottom: var(--spacing-2);
97
+ }
98
+ }
99
+
100
+ .tooltip_dot {
101
+ width: 8px;
102
+ height: 8px;
103
+ border-radius: var(--radius-full);
104
+ flex-shrink: 0;
105
+ }
106
+
107
+ .tooltip_value {
108
+ margin-left: auto;
109
+ font-weight: var(--font-weight-semibold);
110
+ padding-left: var(--spacing-3);
111
+ font-variant-numeric: tabular-nums;
112
+ }
113
+
114
+ /* ── Legend (shared across all chart types) ──────────────────── */
115
+ .legend {
116
+ display: flex;
117
+ flex-wrap: wrap;
118
+ gap: var(--spacing-3) var(--spacing-6);
119
+ justify-content: center;
120
+ }
121
+
122
+ .legend_item {
123
+ display: inline-flex;
124
+ align-items: center;
125
+ gap: var(--spacing-2);
126
+ font-size: var(--font-size-xs);
127
+ font-weight: var(--font-weight-medium);
128
+ color: var(--color-muted-foreground);
129
+ white-space: nowrap;
130
+ }
131
+
132
+ .legend_dot {
133
+ width: 8px;
134
+ height: 8px;
135
+ border-radius: var(--radius-full);
136
+ flex-shrink: 0;
137
+ }
138
+
139
+ .center_overlay {
140
+ position: absolute;
141
+ top: 50%;
142
+ left: 50%;
143
+ transform: translate(-50%, -50%);
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ text-align: center;
148
+ pointer-events: none;
149
+ max-width: calc(100% - var(--spacing-8));
150
+ max-height: calc(100% - var(--spacing-8));
151
+ }
152
+
153
+ @media (width <= 600px) {
154
+ .card {
155
+ padding: var(--spacing-5);
156
+ gap: var(--spacing-5);
157
+ }
158
+
159
+ .tooltip {
160
+ min-width: 8rem;
161
+ padding: var(--spacing-3) var(--spacing-4);
162
+ }
163
+
164
+ .legend {
165
+ justify-content: flex-start;
166
+ gap: var(--spacing-3) var(--spacing-5);
167
+ }
168
+ }