panelui-native 0.2.0 → 0.5.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 (106) hide show
  1. package/README.md +258 -0
  2. package/lib/module/components/accordion/index.js +312 -0
  3. package/lib/module/components/accordion/index.js.map +1 -0
  4. package/lib/module/components/alert/index.js +106 -13
  5. package/lib/module/components/alert/index.js.map +1 -1
  6. package/lib/module/components/avatar/index.js +58 -17
  7. package/lib/module/components/avatar/index.js.map +1 -1
  8. package/lib/module/components/badge/index.js +29 -8
  9. package/lib/module/components/badge/index.js.map +1 -1
  10. package/lib/module/components/button/index.js +87 -26
  11. package/lib/module/components/button/index.js.map +1 -1
  12. package/lib/module/components/checkbox/index.js +59 -18
  13. package/lib/module/components/checkbox/index.js.map +1 -1
  14. package/lib/module/components/empty-state/index.js +178 -0
  15. package/lib/module/components/empty-state/index.js.map +1 -0
  16. package/lib/module/components/frame/index.js +64 -13
  17. package/lib/module/components/frame/index.js.map +1 -1
  18. package/lib/module/components/input-group/index.js +132 -0
  19. package/lib/module/components/input-group/index.js.map +1 -0
  20. package/lib/module/components/label/index.js +117 -0
  21. package/lib/module/components/label/index.js.map +1 -0
  22. package/lib/module/components/progress/index.js +138 -0
  23. package/lib/module/components/progress/index.js.map +1 -0
  24. package/lib/module/components/steps/index.js +354 -0
  25. package/lib/module/components/steps/index.js.map +1 -0
  26. package/lib/module/components/tabs/index.js +9 -3
  27. package/lib/module/components/tabs/index.js.map +1 -1
  28. package/lib/module/components/timeline/index.js +633 -0
  29. package/lib/module/components/timeline/index.js.map +1 -0
  30. package/lib/module/components/toast/index.js +569 -0
  31. package/lib/module/components/toast/index.js.map +1 -0
  32. package/lib/module/components/toast/toast-store.js +81 -0
  33. package/lib/module/components/toast/toast-store.js.map +1 -0
  34. package/lib/module/components/typography/index.js +126 -0
  35. package/lib/module/components/typography/index.js.map +1 -0
  36. package/lib/module/icons/index.js +590 -8
  37. package/lib/module/icons/index.js.map +1 -1
  38. package/lib/module/index.js +11 -2
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/providers/panel-ui-provider.js +2 -1
  41. package/lib/module/providers/panel-ui-provider.js.map +1 -1
  42. package/lib/module/theme/use-theme.js +90 -3
  43. package/lib/module/theme/use-theme.js.map +1 -1
  44. package/lib/typescript/src/components/accordion/index.d.ts +56 -0
  45. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -0
  46. package/lib/typescript/src/components/alert/index.d.ts +56 -4
  47. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  48. package/lib/typescript/src/components/avatar/index.d.ts +8 -1
  49. package/lib/typescript/src/components/avatar/index.d.ts.map +1 -1
  50. package/lib/typescript/src/components/badge/index.d.ts +38 -0
  51. package/lib/typescript/src/components/badge/index.d.ts.map +1 -1
  52. package/lib/typescript/src/components/button/index.d.ts +57 -0
  53. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  54. package/lib/typescript/src/components/checkbox/index.d.ts +50 -0
  55. package/lib/typescript/src/components/checkbox/index.d.ts.map +1 -1
  56. package/lib/typescript/src/components/empty-state/index.d.ts +73 -0
  57. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
  58. package/lib/typescript/src/components/frame/index.d.ts +39 -5
  59. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  60. package/lib/typescript/src/components/input-group/index.d.ts +34 -0
  61. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -0
  62. package/lib/typescript/src/components/label/index.d.ts +28 -0
  63. package/lib/typescript/src/components/label/index.d.ts.map +1 -0
  64. package/lib/typescript/src/components/progress/index.d.ts +132 -0
  65. package/lib/typescript/src/components/progress/index.d.ts.map +1 -0
  66. package/lib/typescript/src/components/steps/index.d.ts +62 -0
  67. package/lib/typescript/src/components/steps/index.d.ts.map +1 -0
  68. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  69. package/lib/typescript/src/components/timeline/index.d.ts +96 -0
  70. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -0
  71. package/lib/typescript/src/components/toast/index.d.ts +176 -0
  72. package/lib/typescript/src/components/toast/index.d.ts.map +1 -0
  73. package/lib/typescript/src/components/toast/toast-store.d.ts +65 -0
  74. package/lib/typescript/src/components/toast/toast-store.d.ts.map +1 -0
  75. package/lib/typescript/src/components/typography/index.d.ts +77 -0
  76. package/lib/typescript/src/components/typography/index.d.ts.map +1 -0
  77. package/lib/typescript/src/icons/index.d.ts +60 -0
  78. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  79. package/lib/typescript/src/index.d.ts +13 -4
  80. package/lib/typescript/src/index.d.ts.map +1 -1
  81. package/lib/typescript/src/providers/panel-ui-provider.d.ts.map +1 -1
  82. package/lib/typescript/src/theme/use-theme.d.ts +64 -4
  83. package/lib/typescript/src/theme/use-theme.d.ts.map +1 -1
  84. package/package.json +22 -4
  85. package/src/components/accordion/index.tsx +344 -0
  86. package/src/components/alert/index.tsx +102 -16
  87. package/src/components/avatar/index.tsx +68 -20
  88. package/src/components/badge/index.tsx +38 -9
  89. package/src/components/button/index.tsx +102 -19
  90. package/src/components/checkbox/index.tsx +64 -12
  91. package/src/components/empty-state/index.tsx +136 -0
  92. package/src/components/frame/index.tsx +101 -31
  93. package/src/components/input-group/index.tsx +164 -0
  94. package/src/components/label/index.tsx +128 -0
  95. package/src/components/progress/index.tsx +147 -0
  96. package/src/components/steps/index.tsx +343 -0
  97. package/src/components/tabs/index.tsx +10 -2
  98. package/src/components/timeline/index.tsx +456 -0
  99. package/src/components/toast/index.tsx +568 -0
  100. package/src/components/toast/toast-store.ts +120 -0
  101. package/src/components/typography/index.tsx +121 -0
  102. package/src/icons/index.tsx +423 -7
  103. package/src/index.ts +114 -4
  104. package/src/providers/panel-ui-provider.tsx +3 -0
  105. package/src/theme/use-theme.ts +119 -4
  106. package/theme.css +330 -7
package/src/index.ts CHANGED
@@ -2,7 +2,17 @@
2
2
  export { PanelUIProvider, type PanelUIProviderProps } from './providers/panel-ui-provider';
3
3
 
4
4
  // Theme
5
- export { useTheme, type ThemeName } from './theme/use-theme';
5
+ export {
6
+ useTheme,
7
+ useThemeMode,
8
+ PANEL_THEMES,
9
+ PANEL_THEME_NAMES,
10
+ PANEL_EXTRA_THEMES,
11
+ type ThemeName,
12
+ type ThemeMode,
13
+ type PanelTheme,
14
+ type PanelThemeFamily,
15
+ } from './theme/use-theme';
6
16
 
7
17
  // Primitives
8
18
  export { Portal, PortalHost, PortalProvider } from './primitives/portal';
@@ -13,8 +23,26 @@ export {
13
23
  } from './primitives/animated-pressable';
14
24
 
15
25
  // Components
16
- export { Alert, type AlertProps } from './components/alert';
17
- export { Avatar, type AvatarProps } from './components/avatar';
26
+ export {
27
+ Accordion,
28
+ type AccordionProps,
29
+ type AccordionItemProps,
30
+ type AccordionTriggerProps,
31
+ type AccordionIndicatorProps,
32
+ type AccordionContentProps,
33
+ type AccordionVariant,
34
+ type AccordionSelectionMode,
35
+ } from './components/accordion';
36
+ export {
37
+ Alert,
38
+ type AlertProps,
39
+ type AlertIndicatorProps,
40
+ } from './components/alert';
41
+ export {
42
+ Avatar,
43
+ type AvatarProps,
44
+ type AvatarBadgeProps,
45
+ } from './components/avatar';
18
46
  export { Badge, type BadgeProps } from './components/badge';
19
47
  export {
20
48
  BottomSheet,
@@ -43,7 +71,20 @@ export {
43
71
  } from './components/tabs';
44
72
  export { Card, type CardProps } from './components/card';
45
73
  export { Checkbox, type CheckboxProps } from './components/checkbox';
74
+ export {
75
+ EmptyState,
76
+ type EmptyStateProps,
77
+ type EmptyStateMediaProps,
78
+ } from './components/empty-state';
46
79
  export { Input, type InputProps } from './components/input';
80
+ export {
81
+ InputGroup,
82
+ type InputGroupProps,
83
+ type InputGroupInputProps,
84
+ type InputGroupDecoratorProps,
85
+ } from './components/input-group';
86
+ export { Label, type LabelProps, type LabelTextProps } from './components/label';
87
+ export { Progress, type ProgressProps } from './components/progress';
47
88
  export {
48
89
  RadioGroup,
49
90
  type RadioGroupProps,
@@ -51,10 +92,79 @@ export {
51
92
  } from './components/radio-group';
52
93
  export { Skeleton, type SkeletonProps } from './components/skeleton';
53
94
  export { Spinner, type SpinnerProps } from './components/spinner';
95
+ export {
96
+ Steps,
97
+ type StepsProps,
98
+ type StepsItemProps,
99
+ type StepsTriggerProps,
100
+ type StepsIndicatorProps,
101
+ type StepsSeparatorProps,
102
+ type StepState,
103
+ type StepsOrientation,
104
+ } from './components/steps';
54
105
  export { Switch, type SwitchProps } from './components/switch';
106
+ export {
107
+ Timeline,
108
+ type TimelineProps,
109
+ type TimelineItemProps,
110
+ type TimelineIndicatorProps,
111
+ type TimelineStatProps,
112
+ type TimelineVariant,
113
+ type TimelineTone,
114
+ } from './components/timeline';
115
+ export {
116
+ Toast,
117
+ ToastViewport,
118
+ toast,
119
+ useToast,
120
+ type ToastProps,
121
+ type ToastOptions,
122
+ type ToastItem,
123
+ type ToastVariant,
124
+ type ToastPlacement,
125
+ type ToastHandle,
126
+ type ToastIndicatorProps,
127
+ type ToastCloseProps,
128
+ } from './components/toast';
129
+ export {
130
+ Typography,
131
+ type TypographyProps,
132
+ type TypographyType,
133
+ type TypographyHeadingProps,
134
+ type TypographyParagraphProps,
135
+ type TypographyCodeProps,
136
+ } from './components/typography';
55
137
 
56
138
  // Icons
57
- export { CheckIcon, ChevronDownIcon, XIcon, type IconProps } from './icons';
139
+ export {
140
+ AlertTriangleIcon,
141
+ AppleIcon,
142
+ ArrowUpRightIcon,
143
+ BellIcon,
144
+ CardIcon,
145
+ CheckCircleIcon,
146
+ CheckIcon,
147
+ ChevronDownIcon,
148
+ ChevronLeftIcon,
149
+ ChevronRightIcon,
150
+ FacebookIcon,
151
+ GoogleIcon,
152
+ IconColorProvider,
153
+ InfoIcon,
154
+ MoonIcon,
155
+ PackageIcon,
156
+ PlusSquareIcon,
157
+ ReceiptIcon,
158
+ SearchIcon,
159
+ SendIcon,
160
+ ShareNodesIcon,
161
+ ShieldAlertIcon,
162
+ ShieldCheckIcon,
163
+ SunIcon,
164
+ XIcon,
165
+ useIconColor,
166
+ type IconProps,
167
+ } from './icons';
58
168
 
59
169
  // Utils
60
170
  export { cn } from './utils/cn';
@@ -2,6 +2,7 @@ import type { ReactNode } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
4
4
  import { PortalHost, PortalProvider } from '../primitives/portal';
5
+ import { ToastViewport } from '../components/toast';
5
6
  import { cn } from '../utils/cn';
6
7
 
7
8
  export interface PanelUIProviderProps {
@@ -40,6 +41,8 @@ export function PanelUIProvider({
40
41
  <View className={cn('flex-1', background && 'bg-background', className)}>
41
42
  <PortalProvider>
42
43
  {children}
44
+ {/* Sits before PortalHost so it can portal into it. */}
45
+ <ToastViewport />
43
46
  <PortalHost />
44
47
  </PortalProvider>
45
48
  </View>
@@ -1,6 +1,87 @@
1
1
  import { Uniwind, useUniwind } from 'uniwind';
2
2
 
3
- export type ThemeName = 'light' | 'dark' | 'system';
3
+ /** Themes shipped in `panelui-native/theme.css`. */
4
+ export type PanelTheme =
5
+ | 'light'
6
+ | 'dark'
7
+ | 'moon'
8
+ | 'moon-dark'
9
+ | 'grass'
10
+ | 'grass-dark';
11
+
12
+ export type ThemeName = PanelTheme | 'system';
13
+
14
+ /** Light/dark mode within a theme family. */
15
+ export type ThemeMode = 'light' | 'dark';
16
+
17
+ /** A theme family — one look, in its light and dark form. */
18
+ export interface PanelThemeFamily {
19
+ /** Stable id, e.g. `'moon'`. */
20
+ id: string;
21
+ /** Short display name, for a theme picker. */
22
+ name: string;
23
+ light: PanelTheme;
24
+ dark: PanelTheme;
25
+ /** Representative colour for a swatch, as `[light, dark]`. */
26
+ swatch: [string, string];
27
+ }
28
+
29
+ /**
30
+ * The theme families PanelUI ships. Each sets its own palette *and* its own
31
+ * radius scale, so switching family changes the shape of the UI as well as
32
+ * its colour — Panel is the Coss default, Moon is sharp and monochrome,
33
+ * Grass is soft and green.
34
+ *
35
+ * Everything beyond `panel` must be registered in your metro config, or
36
+ * `setTheme` throws "it was not registered":
37
+ *
38
+ * ```js
39
+ * withUniwindConfig(config, {
40
+ * cssEntryFile: './global.css',
41
+ * extraThemes: ['moon', 'moon-dark', 'grass', 'grass-dark'],
42
+ * });
43
+ * ```
44
+ */
45
+ export const PANEL_THEMES: readonly PanelThemeFamily[] = [
46
+ {
47
+ id: 'panel',
48
+ name: 'Panel',
49
+ light: 'light',
50
+ dark: 'dark',
51
+ swatch: ['#262626', '#f5f5f5'],
52
+ },
53
+ {
54
+ id: 'moon',
55
+ name: 'Moon',
56
+ light: 'moon',
57
+ dark: 'moon-dark',
58
+ swatch: ['#0070f3', '#0070f3'],
59
+ },
60
+ {
61
+ id: 'grass',
62
+ name: 'Grass',
63
+ light: 'grass',
64
+ dark: 'grass-dark',
65
+ swatch: ['#24b47e', '#3ecf8e'],
66
+ },
67
+ ];
68
+
69
+ /** Every concrete theme name, in registration order. */
70
+ export const PANEL_THEME_NAMES: readonly PanelTheme[] = PANEL_THEMES.flatMap(
71
+ (family) => [family.light, family.dark]
72
+ );
73
+
74
+ /** The names to pass as `extraThemes` in your metro config. */
75
+ export const PANEL_EXTRA_THEMES: readonly string[] = PANEL_THEME_NAMES.filter(
76
+ (name) => name !== 'light' && name !== 'dark'
77
+ );
78
+
79
+ function findFamily(theme: string): PanelThemeFamily {
80
+ return (
81
+ PANEL_THEMES.find((family) => family.light === theme || family.dark === theme) ??
82
+ PANEL_THEMES[0]!
83
+ );
84
+ }
4
85
 
5
86
  /**
6
87
  * Reactive access to the current Uniwind theme plus a setter.
@@ -10,9 +91,43 @@ export function useTheme() {
10
91
  const { theme } = useUniwind();
11
92
 
12
93
  return {
13
- /** Currently resolved theme ('light' or 'dark'). */
14
- theme,
15
- /** Switch theme: 'light', 'dark', or 'system' (follows device). */
94
+ /** The active theme name, e.g. `'dark'` or `'moon-dark'`. */
95
+ theme: theme as PanelTheme,
96
+ /** Switch theme by name, or `'system'` to follow the device. */
16
97
  setTheme: (name: ThemeName) => Uniwind.setTheme(name),
17
98
  };
18
99
  }
100
+
101
+ /**
102
+ * Theme family and light/dark mode as two separate axes — what a picker like
103
+ * "choose a brand, then toggle the sun" actually needs.
104
+ *
105
+ * Named themes are not adaptive in Uniwind, so each brand ships as a
106
+ * light/dark pair. Toggling the mode keeps the current family, so a user in
107
+ * Moon dark lands in Moon light rather than the default theme.
108
+ *
109
+ * ```tsx
110
+ * const { family, mode, setFamily, toggleMode } = useThemeMode();
111
+ * ```
112
+ */
113
+ export function useThemeMode() {
114
+ const { theme } = useUniwind();
115
+ const family = findFamily(theme);
116
+ const mode: ThemeMode = theme === family.dark ? 'dark' : 'light';
117
+
118
+ return {
119
+ /** The active theme family. */
120
+ family,
121
+ /** `'light'` or `'dark'` within that family. */
122
+ mode,
123
+ /** Switch brand, staying in the current light/dark mode. */
124
+ setFamily: (id: string) => {
125
+ const next = PANEL_THEMES.find((candidate) => candidate.id === id);
126
+ if (next) Uniwind.setTheme(next[mode]);
127
+ },
128
+ /** Set the mode, staying in the current family. */
129
+ setMode: (next: ThemeMode) => Uniwind.setTheme(family[next]),
130
+ /** Flip light ↔ dark within the current family. */
131
+ toggleMode: () => Uniwind.setTheme(mode === 'dark' ? family.light : family.dark),
132
+ };
133
+ }
package/theme.css CHANGED
@@ -43,13 +43,35 @@
43
43
  --color-surface: unset;
44
44
  --color-skeleton: unset;
45
45
 
46
- /* Coss radius scale: --radius: 0.625rem (10px) */
47
- --radius-xs: 4px;
48
- --radius-sm: 6px;
49
- --radius-md: 8px;
50
- --radius-lg: 10px;
51
- --radius-xl: 14px;
52
- --radius-2xl: 16px;
46
+ /*
47
+ * Tinted status fills. These exist as real tokens rather than `bg-info/10
48
+ * dark:bg-info/[0.06]` because `dark:` only resolves in the light/dark
49
+ * themes — inside a named theme (moon, grass) Uniwind compiles it to a
50
+ * plain class selector that never matches, so the tint would vanish.
51
+ * `-soft` is the Alert fill, `-subtle` the lighter Badge fill.
52
+ */
53
+ --color-info-soft: unset;
54
+ --color-info-subtle: unset;
55
+ --color-success-soft: unset;
56
+ --color-success-subtle: unset;
57
+ --color-warning-soft: unset;
58
+ --color-warning-subtle: unset;
59
+ --color-destructive-soft: unset;
60
+ --color-destructive-subtle: unset;
61
+
62
+ /*
63
+ * Radius scale. Themed rather than static: a theme is a shape as well as a
64
+ * palette, so each family sets its own corner rounding. --radius-3xl is
65
+ * declared here because rounded-3xl / rounded-t-3xl are both in use and
66
+ * would otherwise silently fall through to Tailwind's default.
67
+ */
68
+ --radius-xs: unset;
69
+ --radius-sm: unset;
70
+ --radius-md: unset;
71
+ --radius-lg: unset;
72
+ --radius-xl: unset;
73
+ --radius-2xl: unset;
74
+ --radius-3xl: unset;
53
75
  }
54
76
 
55
77
  @layer theme {
@@ -101,6 +123,25 @@
101
123
  /* grouping surface (Frame) and skeleton fill */
102
124
  --color-surface: #f7f7f7;
103
125
  --color-skeleton: rgba(0, 0, 0, 0.1);
126
+
127
+ /* status fills: -soft = Alert (10%), -subtle = Badge (8%) */
128
+ --color-info-soft: rgba(59, 130, 246, 0.1);
129
+ --color-info-subtle: rgba(59, 130, 246, 0.08);
130
+ --color-success-soft: rgba(16, 185, 129, 0.1);
131
+ --color-success-subtle: rgba(16, 185, 129, 0.08);
132
+ --color-warning-soft: rgba(245, 158, 11, 0.1);
133
+ --color-warning-subtle: rgba(245, 158, 11, 0.08);
134
+ --color-destructive-soft: rgba(239, 68, 68, 0.1);
135
+ --color-destructive-subtle: rgba(239, 68, 68, 0.08);
136
+
137
+ /* Panel shape scale */
138
+ --radius-xs: 4px;
139
+ --radius-sm: 6px;
140
+ --radius-md: 8px;
141
+ --radius-lg: 10px;
142
+ --radius-xl: 14px;
143
+ --radius-2xl: 16px;
144
+ --radius-3xl: 24px;
104
145
  }
105
146
 
106
147
  @variant dark {
@@ -153,6 +194,288 @@
153
194
  /* grouping surface (Frame) and skeleton fill */
154
195
  --color-surface: #1c1c1c;
155
196
  --color-skeleton: rgba(255, 255, 255, 0.13);
197
+
198
+ /* status fills: -soft = Alert (6%), -subtle = Badge (16%) */
199
+ --color-info-soft: rgba(59, 130, 246, 0.06);
200
+ --color-info-subtle: rgba(59, 130, 246, 0.16);
201
+ --color-success-soft: rgba(16, 185, 129, 0.06);
202
+ --color-success-subtle: rgba(16, 185, 129, 0.16);
203
+ --color-warning-soft: rgba(245, 158, 11, 0.06);
204
+ --color-warning-subtle: rgba(245, 158, 11, 0.16);
205
+ --color-destructive-soft: rgba(241, 87, 87, 0.06);
206
+ --color-destructive-subtle: rgba(241, 87, 87, 0.16);
207
+
208
+ /* Panel shape scale */
209
+ --radius-xs: 4px;
210
+ --radius-sm: 6px;
211
+ --radius-md: 8px;
212
+ --radius-lg: 10px;
213
+ --radius-xl: 14px;
214
+ --radius-2xl: 16px;
215
+ --radius-3xl: 24px;
216
+ }
217
+
218
+ /*
219
+ * Named themes.
220
+ *
221
+ * Uniwind only gives `light` and `dark` prefers-color-scheme handling;
222
+ * every other theme compiles to a plain class selector. That means a
223
+ * named theme cannot adapt on its own, so each brand ships as a
224
+ * light/dark pair and the app switches between them explicitly (see
225
+ * `useThemeMode()`).
226
+ *
227
+ * Register them in your metro config or setTheme() will throw:
228
+ * withUniwindConfig(config, {
229
+ * extraThemes: ['moon', 'moon-dark', 'grass', 'grass-dark'],
230
+ * })
231
+ *
232
+ * Every theme must define every token — Uniwind fails the build with
233
+ * "All themes must have the same variables" otherwise.
234
+ */
235
+
236
+ /* Moon — monochrome and high-contrast: pure black/white, electric blue accent,
237
+ hairline borders, tight corners. */
238
+ @variant moon {
239
+ --color-background: #ffffff;
240
+ --color-foreground: #171717;
241
+
242
+ --color-card: #ffffff;
243
+ --color-card-foreground: #171717;
244
+
245
+ --color-popover: #ffffff;
246
+ --color-popover-foreground: #171717;
247
+
248
+ --color-primary: #171717;
249
+ --color-primary-foreground: #ffffff;
250
+
251
+ --color-secondary: rgba(0, 0, 0, 0.05);
252
+ --color-secondary-foreground: #171717;
253
+
254
+ --color-muted: rgba(0, 0, 0, 0.05);
255
+ --color-muted-foreground: #666666;
256
+
257
+ --color-accent: rgba(0, 0, 0, 0.05);
258
+ --color-accent-foreground: #171717;
259
+
260
+ --color-destructive: #e5484d;
261
+ --color-destructive-foreground: #c62b30;
262
+
263
+ --color-info: #0070f3;
264
+ --color-info-foreground: #0060d1;
265
+
266
+ --color-success: #45a557;
267
+ --color-success-foreground: #297c3b;
268
+
269
+ --color-warning: #f5a623;
270
+ --color-warning-foreground: #a35200;
271
+
272
+ --color-border: rgba(0, 0, 0, 0.09);
273
+ --color-input: rgba(0, 0, 0, 0.12);
274
+ --color-ring: #171717;
275
+
276
+ --color-surface: #fafafa;
277
+ --color-skeleton: rgba(0, 0, 0, 0.08);
278
+
279
+ --color-info-soft: rgba(0, 112, 243, 0.08);
280
+ --color-info-subtle: rgba(0, 112, 243, 0.06);
281
+ --color-success-soft: rgba(69, 165, 87, 0.1);
282
+ --color-success-subtle: rgba(69, 165, 87, 0.08);
283
+ --color-warning-soft: rgba(245, 166, 35, 0.1);
284
+ --color-warning-subtle: rgba(245, 166, 35, 0.08);
285
+ --color-destructive-soft: rgba(229, 72, 77, 0.1);
286
+ --color-destructive-subtle: rgba(229, 72, 77, 0.08);
287
+
288
+ /* Moon shape scale */
289
+ --radius-xs: 2px;
290
+ --radius-sm: 4px;
291
+ --radius-md: 5px;
292
+ --radius-lg: 6px;
293
+ --radius-xl: 8px;
294
+ --radius-2xl: 10px;
295
+ --radius-3xl: 14px;
296
+ }
297
+
298
+ @variant moon-dark {
299
+ --color-background: #000000;
300
+ --color-foreground: #ededed;
301
+
302
+ --color-card: #0a0a0a;
303
+ --color-card-foreground: #ededed;
304
+
305
+ --color-popover: #101010;
306
+ --color-popover-foreground: #ededed;
307
+
308
+ --color-primary: #ededed;
309
+ --color-primary-foreground: #0a0a0a;
310
+
311
+ --color-secondary: rgba(255, 255, 255, 0.09);
312
+ --color-secondary-foreground: #ededed;
313
+
314
+ --color-muted: rgba(255, 255, 255, 0.09);
315
+ --color-muted-foreground: #a1a1a1;
316
+
317
+ --color-accent: rgba(255, 255, 255, 0.09);
318
+ --color-accent-foreground: #ededed;
319
+
320
+ --color-destructive: #e5484d;
321
+ --color-destructive-foreground: #ff6369;
322
+
323
+ --color-info: #0070f3;
324
+ --color-info-foreground: #52a8ff;
325
+
326
+ --color-success: #45a557;
327
+ --color-success-foreground: #62c073;
328
+
329
+ --color-warning: #f5a623;
330
+ --color-warning-foreground: #f1a10d;
331
+
332
+ --color-border: rgba(255, 255, 255, 0.09);
333
+ --color-input: rgba(255, 255, 255, 0.14);
334
+ --color-ring: #ededed;
335
+
336
+ --color-surface: #0f0f0f;
337
+ --color-skeleton: rgba(255, 255, 255, 0.11);
338
+
339
+ --color-info-soft: rgba(0, 112, 243, 0.12);
340
+ --color-info-subtle: rgba(0, 112, 243, 0.18);
341
+ --color-success-soft: rgba(69, 165, 87, 0.1);
342
+ --color-success-subtle: rgba(69, 165, 87, 0.16);
343
+ --color-warning-soft: rgba(245, 166, 35, 0.1);
344
+ --color-warning-subtle: rgba(245, 166, 35, 0.16);
345
+ --color-destructive-soft: rgba(229, 72, 77, 0.1);
346
+ --color-destructive-subtle: rgba(229, 72, 77, 0.16);
347
+
348
+ /* Moon shape scale */
349
+ --radius-xs: 2px;
350
+ --radius-sm: 4px;
351
+ --radius-md: 5px;
352
+ --radius-lg: 6px;
353
+ --radius-xl: 8px;
354
+ --radius-2xl: 10px;
355
+ --radius-3xl: 14px;
356
+ }
357
+
358
+ /* Grass — green accent on warm-neutral greys, with soft generous corners. */
359
+ @variant grass {
360
+ --color-background: #ffffff;
361
+ --color-foreground: #171717;
362
+
363
+ --color-card: #ffffff;
364
+ --color-card-foreground: #171717;
365
+
366
+ --color-popover: #ffffff;
367
+ --color-popover-foreground: #171717;
368
+
369
+ /* Brand green is too light for white text; light mode uses the
370
+ darker ramp step so the contrast ratio holds. */
371
+ --color-primary: #24b47e;
372
+ --color-primary-foreground: #ffffff;
373
+
374
+ --color-secondary: rgba(0, 0, 0, 0.05);
375
+ --color-secondary-foreground: #171717;
376
+
377
+ --color-muted: rgba(0, 0, 0, 0.05);
378
+ --color-muted-foreground: #707070;
379
+
380
+ --color-accent: rgba(62, 207, 142, 0.1);
381
+ --color-accent-foreground: #16794c;
382
+
383
+ --color-destructive: #ef4444;
384
+ --color-destructive-foreground: #b91c1c;
385
+
386
+ --color-info: #3b82f6;
387
+ --color-info-foreground: #1d4ed8;
388
+
389
+ --color-success: #24b47e;
390
+ --color-success-foreground: #16794c;
391
+
392
+ --color-warning: #f59e0b;
393
+ --color-warning-foreground: #b45309;
394
+
395
+ --color-border: rgba(0, 0, 0, 0.08);
396
+ --color-input: rgba(0, 0, 0, 0.1);
397
+ --color-ring: #3ecf8e;
398
+
399
+ --color-surface: #f8f9fa;
400
+ --color-skeleton: rgba(0, 0, 0, 0.08);
401
+
402
+ --color-info-soft: rgba(59, 130, 246, 0.1);
403
+ --color-info-subtle: rgba(59, 130, 246, 0.08);
404
+ --color-success-soft: rgba(36, 180, 126, 0.1);
405
+ --color-success-subtle: rgba(36, 180, 126, 0.08);
406
+ --color-warning-soft: rgba(245, 158, 11, 0.1);
407
+ --color-warning-subtle: rgba(245, 158, 11, 0.08);
408
+ --color-destructive-soft: rgba(239, 68, 68, 0.1);
409
+ --color-destructive-subtle: rgba(239, 68, 68, 0.08);
410
+
411
+ /* Grass shape scale */
412
+ --radius-xs: 6px;
413
+ --radius-sm: 8px;
414
+ --radius-md: 12px;
415
+ --radius-lg: 14px;
416
+ --radius-xl: 18px;
417
+ --radius-2xl: 22px;
418
+ --radius-3xl: 28px;
419
+ }
420
+
421
+ @variant grass-dark {
422
+ --color-background: #1c1c1c;
423
+ --color-foreground: #ededed;
424
+
425
+ --color-card: #1f1f1f;
426
+ --color-card-foreground: #ededed;
427
+
428
+ --color-popover: #232323;
429
+ --color-popover-foreground: #ededed;
430
+
431
+ --color-primary: #3ecf8e;
432
+ --color-primary-foreground: #052e1b;
433
+
434
+ --color-secondary: rgba(255, 255, 255, 0.07);
435
+ --color-secondary-foreground: #ededed;
436
+
437
+ --color-muted: rgba(255, 255, 255, 0.07);
438
+ --color-muted-foreground: #8f8f8f;
439
+
440
+ --color-accent: rgba(62, 207, 142, 0.12);
441
+ --color-accent-foreground: #3ecf8e;
442
+
443
+ --color-destructive: #f15757;
444
+ --color-destructive-foreground: #f87171;
445
+
446
+ --color-info: #3b82f6;
447
+ --color-info-foreground: #60a5fa;
448
+
449
+ --color-success: #3ecf8e;
450
+ --color-success-foreground: #4ade80;
451
+
452
+ --color-warning: #f59e0b;
453
+ --color-warning-foreground: #fbbf24;
454
+
455
+ --color-border: rgba(255, 255, 255, 0.07);
456
+ --color-input: rgba(255, 255, 255, 0.1);
457
+ --color-ring: #3ecf8e;
458
+
459
+ --color-surface: #242424;
460
+ --color-skeleton: rgba(255, 255, 255, 0.11);
461
+
462
+ --color-info-soft: rgba(59, 130, 246, 0.08);
463
+ --color-info-subtle: rgba(59, 130, 246, 0.16);
464
+ --color-success-soft: rgba(62, 207, 142, 0.08);
465
+ --color-success-subtle: rgba(62, 207, 142, 0.16);
466
+ --color-warning-soft: rgba(245, 158, 11, 0.08);
467
+ --color-warning-subtle: rgba(245, 158, 11, 0.16);
468
+ --color-destructive-soft: rgba(241, 87, 87, 0.08);
469
+ --color-destructive-subtle: rgba(241, 87, 87, 0.16);
470
+
471
+ /* Grass shape scale */
472
+ --radius-xs: 6px;
473
+ --radius-sm: 8px;
474
+ --radius-md: 12px;
475
+ --radius-lg: 14px;
476
+ --radius-xl: 18px;
477
+ --radius-2xl: 22px;
478
+ --radius-3xl: 28px;
156
479
  }
157
480
  }
158
481
  }