cnnative-ui 1.0.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.
- package/README.md +34 -0
- package/babel.config.js +6 -0
- package/jest.config.js +22 -0
- package/jest.init.js +5 -0
- package/jest.setup.js +173 -0
- package/package.json +87 -0
- package/src/__tests__/a11y/accessibility.test.tsx +33 -0
- package/src/__tests__/components/badge.test.tsx +25 -0
- package/src/__tests__/components/button.test.tsx +53 -0
- package/src/__tests__/components/card.test.tsx +28 -0
- package/src/__tests__/components/input.test.tsx +33 -0
- package/src/__tests__/hooks/use-controllable.test.ts +58 -0
- package/src/__tests__/integration.test.tsx +35 -0
- package/src/__tests__/lib/utils.test.ts +23 -0
- package/src/__tests__/mocks/handlers.ts +19 -0
- package/src/components/accordion/accordion.tsx +143 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/alert/alert.tsx +65 -0
- package/src/components/alert/index.ts +1 -0
- package/src/components/alert-dialog/alert-dialog.tsx +145 -0
- package/src/components/alert-dialog/index.ts +1 -0
- package/src/components/aspect-ratio/aspect-ratio.tsx +18 -0
- package/src/components/aspect-ratio/index.ts +1 -0
- package/src/components/avatar/avatar.tsx +93 -0
- package/src/components/avatar/index.ts +1 -0
- package/src/components/badge/badge.tsx +64 -0
- package/src/components/badge/index.ts +1 -0
- package/src/components/breadcrumb/breadcrumb.tsx +75 -0
- package/src/components/breadcrumb/index.ts +1 -0
- package/src/components/button/button.tsx +119 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/card/card.tsx +40 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/checkbox/checkbox.tsx +87 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/collapsible/collapsible.tsx +92 -0
- package/src/components/collapsible/index.ts +1 -0
- package/src/components/context-menu/context-menu.tsx +121 -0
- package/src/components/context-menu/index.ts +1 -0
- package/src/components/dialog/dialog.tsx +124 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/dropdown-menu/dropdown-menu.tsx +145 -0
- package/src/components/dropdown-menu/index.ts +1 -0
- package/src/components/form/form.tsx +84 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.tsx +115 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.tsx +13 -0
- package/src/components/navigation-menu/index.ts +1 -0
- package/src/components/navigation-menu/navigation-menu.tsx +68 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.tsx +70 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/progress/progress.tsx +66 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.tsx +90 -0
- package/src/components/scroll-area/index.ts +1 -0
- package/src/components/scroll-area/scroll-area.tsx +27 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/select.tsx +154 -0
- package/src/components/separator/index.ts +1 -0
- package/src/components/separator/separator.tsx +37 -0
- package/src/components/sheet/index.ts +1 -0
- package/src/components/sheet/sheet.tsx +128 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.tsx +84 -0
- package/src/components/slider/index.ts +1 -0
- package/src/components/slider/slider.tsx +145 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.tsx +78 -0
- package/src/components/table/index.ts +1 -0
- package/src/components/table/table.tsx +71 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs.tsx +124 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/textarea.tsx +83 -0
- package/src/components/toast/index.ts +1 -0
- package/src/components/toast/toast.tsx +124 -0
- package/src/components/toggle/index.ts +1 -0
- package/src/components/toggle/toggle.tsx +87 -0
- package/src/components/toggle-group/index.ts +1 -0
- package/src/components/toggle-group/toggle-group.tsx +87 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.tsx +103 -0
- package/src/components/typography/index.ts +1 -0
- package/src/components/typography/typography.tsx +57 -0
- package/src/context/index.ts +3 -0
- package/src/context/provider.tsx +35 -0
- package/src/context/theme-context.tsx +81 -0
- package/src/context/toast-context.tsx +63 -0
- package/src/env.d.ts +2 -0
- package/src/hooks/index.ts +15 -0
- package/src/hooks/use-biometric.ts +27 -0
- package/src/hooks/use-color-scheme.ts +10 -0
- package/src/hooks/use-controllable.ts +40 -0
- package/src/hooks/use-countdown.ts +33 -0
- package/src/hooks/use-debounce.ts +18 -0
- package/src/hooks/use-disclosure.ts +14 -0
- package/src/hooks/use-haptics.ts +47 -0
- package/src/hooks/use-keyboard.ts +35 -0
- package/src/hooks/use-media-query.ts +27 -0
- package/src/hooks/use-press-animation.ts +45 -0
- package/src/hooks/use-previous.ts +14 -0
- package/src/hooks/use-scroll-header.ts +42 -0
- package/src/hooks/use-spring.ts +18 -0
- package/src/hooks/use-theme.ts +6 -0
- package/src/hooks/use-toast.ts +6 -0
- package/src/index.ts +53 -0
- package/src/lib/create-animated.tsx +25 -0
- package/src/lib/create-component.tsx +56 -0
- package/src/lib/index.ts +4 -0
- package/src/lib/platform.ts +25 -0
- package/src/lib/types.ts +28 -0
- package/src/lib/utils.ts +35 -0
- package/src/lib/variants.ts +7 -0
- package/src/premium/ai/chat-bubble.tsx +58 -0
- package/src/premium/ai/typing-indicator.tsx +59 -0
- package/src/premium/charts/bar-chart.tsx +66 -0
- package/src/premium/charts/progress-ring.tsx +63 -0
- package/src/premium/glass/glass-bottom-sheet.tsx +50 -0
- package/src/premium/glass/glass-card.tsx +51 -0
- package/src/premium/glass/glass-header.tsx +61 -0
- package/src/premium/glass/glass-panel.tsx +32 -0
- package/src/premium/glass/glass-sidebar.tsx +56 -0
- package/src/premium/index.ts +44 -0
- package/src/premium/index2.ts +13 -0
- package/src/premium/index3.ts +1 -0
- package/src/premium/inputs/color-picker.tsx +92 -0
- package/src/premium/inputs/currency-input.tsx +50 -0
- package/src/premium/inputs/otp-input.tsx +92 -0
- package/src/premium/inputs/phone-input.tsx +58 -0
- package/src/premium/inputs/rating.tsx +51 -0
- package/src/premium/layout/carousel.tsx +57 -0
- package/src/premium/layout/floating-dock.tsx +63 -0
- package/src/premium/layout/masonry-grid.tsx +41 -0
- package/src/premium/layout/parallax-scroll.tsx +81 -0
- package/src/premium/magic/animated-number.tsx +104 -0
- package/src/premium/magic/bento-grid.tsx +55 -0
- package/src/premium/magic/border-beam.tsx +68 -0
- package/src/premium/magic/confetti.tsx +88 -0
- package/src/premium/magic/magic-card.tsx +65 -0
- package/src/premium/magic/meteors.tsx +95 -0
- package/src/premium/magic/ripple.tsx +70 -0
- package/src/premium/magic/shimmer.tsx +58 -0
- package/src/premium/magic/shiny-button.tsx +70 -0
- package/src/premium/mobile/biometric-button.tsx +82 -0
- package/src/premium/mobile/bottom-tab-bar.tsx +81 -0
- package/src/premium/mobile/fab.tsx +74 -0
- package/src/premium/mobile/haptic-pressable.tsx +53 -0
- package/src/premium/mobile/notification-badge.tsx +61 -0
- package/src/premium/mobile/pull-to-refresh.tsx +84 -0
- package/src/premium/mobile/scroll-header.tsx +57 -0
- package/src/premium/mobile/swipe-row.tsx +128 -0
- package/src/premium/mobile/swipeable-card-stack.tsx +121 -0
- package/src/premium/motion/blur-fade.tsx +51 -0
- package/src/premium/motion/fade-up.tsx +34 -0
- package/src/premium/motion/marquee.tsx +67 -0
- package/src/premium/motion/pulsating-button.tsx +95 -0
- package/src/premium/motion/slide-in.tsx +38 -0
- package/src/premium/motion/stagger-children.tsx +28 -0
- package/src/premium/motion/typing-text.tsx +55 -0
- package/src/premium/motion/word-pull-up.tsx +34 -0
- package/src/premium/onboarding/step-indicator.tsx +65 -0
- package/src/tokens/colors.ts +83 -0
- package/src/tokens/global.css +83 -0
- package/src/tokens/index.ts +10 -0
- package/src/tokens/layout.ts +121 -0
- package/src/tokens/motion.ts +94 -0
- package/src/tokens/themes/dark.ts +7 -0
- package/src/tokens/themes/default.ts +8 -0
- package/src/tokens/themes/ocean.ts +28 -0
- package/src/tokens/themes/rose.ts +29 -0
- package/src/tokens/typography.ts +127 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, type ViewProps } from 'react-native';
|
|
3
|
+
import { cn } from '../../lib/utils';
|
|
4
|
+
import { Text } from '../../components/typography';
|
|
5
|
+
import { Check } from 'lucide-react-native';
|
|
6
|
+
import Animated, { FadeIn, SlideInRight } from 'react-native-reanimated';
|
|
7
|
+
|
|
8
|
+
export interface StepIndicatorProps extends ViewProps {
|
|
9
|
+
steps: string[];
|
|
10
|
+
currentStep: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const StepIndicator = React.forwardRef<React.ElementRef<typeof View>, StepIndicatorProps>(
|
|
14
|
+
({ className, steps, currentStep, ...props }, ref) => {
|
|
15
|
+
return (
|
|
16
|
+
<View ref={ref} className={cn('flex-row items-center justify-between w-full px-4', className)} {...props}>
|
|
17
|
+
{steps.map((step, index) => {
|
|
18
|
+
const isCompleted = index < currentStep;
|
|
19
|
+
const isActive = index === currentStep;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<React.Fragment key={step}>
|
|
23
|
+
{/* Step Circle */}
|
|
24
|
+
<View className="items-center">
|
|
25
|
+
<Animated.View
|
|
26
|
+
entering={SlideInRight.delay(index * 100).springify()}
|
|
27
|
+
className={cn(
|
|
28
|
+
'h-8 w-8 rounded-full items-center justify-center border-2 transition-colors',
|
|
29
|
+
isCompleted ? 'bg-primary border-primary' : isActive ? 'border-primary bg-background' : 'border-muted bg-background'
|
|
30
|
+
)}
|
|
31
|
+
>
|
|
32
|
+
{isCompleted ? (
|
|
33
|
+
<Animated.View entering={FadeIn}>
|
|
34
|
+
<Check size={16} className="text-primary-foreground" />
|
|
35
|
+
</Animated.View>
|
|
36
|
+
) : (
|
|
37
|
+
<Text className={cn('text-xs font-bold', isActive ? 'text-primary' : 'text-muted-foreground')}>
|
|
38
|
+
{index + 1}
|
|
39
|
+
</Text>
|
|
40
|
+
)}
|
|
41
|
+
</Animated.View>
|
|
42
|
+
<Text className={cn('absolute -bottom-6 text-[10px] w-20 text-center', isActive || isCompleted ? 'text-foreground font-medium' : 'text-muted-foreground')}>
|
|
43
|
+
{step}
|
|
44
|
+
</Text>
|
|
45
|
+
</View>
|
|
46
|
+
|
|
47
|
+
{/* Connecting Line */}
|
|
48
|
+
{index < steps.length - 1 && (
|
|
49
|
+
<View className="flex-1 h-[2px] mx-2 bg-muted overflow-hidden">
|
|
50
|
+
<Animated.View
|
|
51
|
+
className="h-full bg-primary"
|
|
52
|
+
style={{
|
|
53
|
+
width: isCompleted ? '100%' : '0%',
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
</View>
|
|
57
|
+
)}
|
|
58
|
+
</React.Fragment>
|
|
59
|
+
);
|
|
60
|
+
})}
|
|
61
|
+
</View>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
StepIndicator.displayName = 'StepIndicator';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nativecn Color Tokens
|
|
3
|
+
* HSL-based design tokens compatible with shadcn/ui defaults.
|
|
4
|
+
* Extended with success, warning, info, glass, and overlay layers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** A raw HSL string e.g. "221.2 83.2% 53.3%" (no 'hsl()' wrapper). */
|
|
8
|
+
export type HslValue = string;
|
|
9
|
+
|
|
10
|
+
export interface ColorScale {
|
|
11
|
+
DEFAULT: HslValue;
|
|
12
|
+
foreground: HslValue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SemanticColors {
|
|
16
|
+
background: HslValue;
|
|
17
|
+
foreground: HslValue;
|
|
18
|
+
card: ColorScale;
|
|
19
|
+
popover: ColorScale;
|
|
20
|
+
primary: ColorScale;
|
|
21
|
+
secondary: ColorScale;
|
|
22
|
+
muted: ColorScale;
|
|
23
|
+
accent: ColorScale;
|
|
24
|
+
destructive: ColorScale;
|
|
25
|
+
success: ColorScale;
|
|
26
|
+
warning: ColorScale;
|
|
27
|
+
info: ColorScale;
|
|
28
|
+
border: HslValue;
|
|
29
|
+
input: HslValue;
|
|
30
|
+
ring: HslValue;
|
|
31
|
+
/** Glassmorphism base — semi-transparent white/black */
|
|
32
|
+
glass: string;
|
|
33
|
+
glassBorder: string;
|
|
34
|
+
overlay: string;
|
|
35
|
+
shimmer: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const lightColors: SemanticColors = {
|
|
39
|
+
background: '0 0% 100%',
|
|
40
|
+
foreground: '222.2 84% 4.9%',
|
|
41
|
+
card: { DEFAULT: '0 0% 100%', foreground: '222.2 84% 4.9%' },
|
|
42
|
+
popover: { DEFAULT: '0 0% 100%', foreground: '222.2 84% 4.9%' },
|
|
43
|
+
primary: { DEFAULT: '221.2 83.2% 53.3%', foreground: '210 40% 98%' },
|
|
44
|
+
secondary: { DEFAULT: '210 40% 96.1%', foreground: '222.2 47.4% 11.2%' },
|
|
45
|
+
muted: { DEFAULT: '210 40% 96.1%', foreground: '215.4 16.3% 46.9%' },
|
|
46
|
+
accent: { DEFAULT: '210 40% 96.1%', foreground: '222.2 47.4% 11.2%' },
|
|
47
|
+
destructive: { DEFAULT: '0 84.2% 60.2%', foreground: '210 40% 98%' },
|
|
48
|
+
success: { DEFAULT: '142.1 76.2% 36.3%', foreground: '210 40% 98%' },
|
|
49
|
+
warning: { DEFAULT: '38 92% 50%', foreground: '0 0% 100%' },
|
|
50
|
+
info: { DEFAULT: '199 89% 48%', foreground: '0 0% 100%' },
|
|
51
|
+
border: '214.3 31.8% 91.4%',
|
|
52
|
+
input: '214.3 31.8% 91.4%',
|
|
53
|
+
ring: '221.2 83.2% 53.3%',
|
|
54
|
+
glass: 'rgba(255, 255, 255, 0.12)',
|
|
55
|
+
glassBorder: 'rgba(255, 255, 255, 0.20)',
|
|
56
|
+
overlay: 'rgba(0, 0, 0, 0.60)',
|
|
57
|
+
shimmer: 'rgba(255, 255, 255, 0.60)',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const darkColors: SemanticColors = {
|
|
61
|
+
background: '222.2 84% 4.9%',
|
|
62
|
+
foreground: '210 40% 98%',
|
|
63
|
+
card: { DEFAULT: '222.2 84% 4.9%', foreground: '210 40% 98%' },
|
|
64
|
+
popover: { DEFAULT: '222.2 84% 4.9%', foreground: '210 40% 98%' },
|
|
65
|
+
primary: { DEFAULT: '217.2 91.2% 59.8%', foreground: '222.2 47.4% 11.2%' },
|
|
66
|
+
secondary: { DEFAULT: '217.2 32.6% 17.5%', foreground: '210 40% 98%' },
|
|
67
|
+
muted: { DEFAULT: '217.2 32.6% 17.5%', foreground: '215 20.2% 65.1%' },
|
|
68
|
+
accent: { DEFAULT: '217.2 32.6% 17.5%', foreground: '210 40% 98%' },
|
|
69
|
+
destructive: { DEFAULT: '0 62.8% 30.6%', foreground: '210 40% 98%' },
|
|
70
|
+
success: { DEFAULT: '142.1 70.6% 45.3%', foreground: '0 0% 100%' },
|
|
71
|
+
warning: { DEFAULT: '38 92% 55%', foreground: '0 0% 100%' },
|
|
72
|
+
info: { DEFAULT: '199 89% 52%', foreground: '0 0% 100%' },
|
|
73
|
+
border: '217.2 32.6% 17.5%',
|
|
74
|
+
input: '217.2 32.6% 17.5%',
|
|
75
|
+
ring: '224.3 76.3% 48%',
|
|
76
|
+
glass: 'rgba(0, 0, 0, 0.25)',
|
|
77
|
+
glassBorder: 'rgba(255, 255, 255, 0.10)',
|
|
78
|
+
overlay: 'rgba(0, 0, 0, 0.75)',
|
|
79
|
+
shimmer: 'rgba(255, 255, 255, 0.08)',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Convert an HSL token to a full CSS hsl() string */
|
|
83
|
+
export const hsl = (value: HslValue): string => `hsl(${value})`;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
2
|
+
@import "nativewind/theme";
|
|
3
|
+
|
|
4
|
+
@theme {
|
|
5
|
+
/* Base tokens (shadcn-compatible) */
|
|
6
|
+
--color-background: hsl(0 0% 100%);
|
|
7
|
+
--color-foreground: hsl(222.2 84% 4.9%);
|
|
8
|
+
--color-primary: hsl(221.2 83.2% 53.3%);
|
|
9
|
+
--color-primary-foreground: hsl(210 40% 98%);
|
|
10
|
+
--color-secondary: hsl(210 40% 96.1%);
|
|
11
|
+
--color-secondary-foreground: hsl(222.2 47.4% 11.2%);
|
|
12
|
+
--color-muted: hsl(210 40% 96.1%);
|
|
13
|
+
--color-muted-foreground: hsl(215.4 16.3% 46.9%);
|
|
14
|
+
--color-accent: hsl(210 40% 96.1%);
|
|
15
|
+
--color-accent-foreground: hsl(222.2 47.4% 11.2%);
|
|
16
|
+
--color-destructive: hsl(0 84.2% 60.2%);
|
|
17
|
+
--color-destructive-foreground: hsl(210 40% 98%);
|
|
18
|
+
--color-border: hsl(214.3 31.8% 91.4%);
|
|
19
|
+
--color-input: hsl(214.3 31.8% 91.4%);
|
|
20
|
+
--color-ring: hsl(221.2 83.2% 53.3%);
|
|
21
|
+
|
|
22
|
+
/* Extended tokens */
|
|
23
|
+
--color-success: hsl(142.1 76.2% 36.3%);
|
|
24
|
+
--color-success-foreground: hsl(210 40% 98%);
|
|
25
|
+
--color-warning: hsl(38 92% 50%);
|
|
26
|
+
--color-warning-foreground: hsl(0 0% 100%);
|
|
27
|
+
--color-info: hsl(199 89% 48%);
|
|
28
|
+
--color-info-foreground: hsl(0 0% 100%);
|
|
29
|
+
--color-glass: rgba(255 255 255 / 0.12);
|
|
30
|
+
--color-glass-border: rgba(255 255 255 / 0.20);
|
|
31
|
+
--color-overlay: rgba(0 0 0 / 0.60);
|
|
32
|
+
|
|
33
|
+
/* Radius */
|
|
34
|
+
--radius-none: 0;
|
|
35
|
+
--radius-sm: 4px;
|
|
36
|
+
--radius-md: 6px;
|
|
37
|
+
--radius-lg: 8px;
|
|
38
|
+
--radius-xl: 12px;
|
|
39
|
+
--radius-2xl: 16px;
|
|
40
|
+
--radius-full: 9999px;
|
|
41
|
+
|
|
42
|
+
/* Typography */
|
|
43
|
+
--font-sans: 'Inter', system-ui;
|
|
44
|
+
--font-mono: 'JetBrains Mono', monospace;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@media (prefers-color-scheme: dark) {
|
|
48
|
+
:root {
|
|
49
|
+
--color-background: hsl(222.2 84% 4.9%);
|
|
50
|
+
--color-foreground: hsl(210 40% 98%);
|
|
51
|
+
--color-primary: hsl(217.2 91.2% 59.8%);
|
|
52
|
+
--color-primary-foreground: hsl(222.2 47.4% 11.2%);
|
|
53
|
+
--color-secondary: hsl(217.2 32.6% 17.5%);
|
|
54
|
+
--color-secondary-foreground: hsl(210 40% 98%);
|
|
55
|
+
--color-muted: hsl(217.2 32.6% 17.5%);
|
|
56
|
+
--color-muted-foreground: hsl(215 20.2% 65.1%);
|
|
57
|
+
--color-accent: hsl(217.2 32.6% 17.5%);
|
|
58
|
+
--color-accent-foreground: hsl(210 40% 98%);
|
|
59
|
+
--color-destructive: hsl(0 62.8% 30.6%);
|
|
60
|
+
--color-destructive-foreground: hsl(210 40% 98%);
|
|
61
|
+
--color-border: hsl(217.2 32.6% 17.5%);
|
|
62
|
+
--color-input: hsl(217.2 32.6% 17.5%);
|
|
63
|
+
--color-ring: hsl(224.3 76.3% 48%);
|
|
64
|
+
|
|
65
|
+
--color-success: hsl(142.1 70.6% 45.3%);
|
|
66
|
+
--color-success-foreground: hsl(0 0% 100%);
|
|
67
|
+
--color-warning: hsl(38 92% 55%);
|
|
68
|
+
--color-warning-foreground: hsl(0 0% 100%);
|
|
69
|
+
--color-info: hsl(199 89% 52%);
|
|
70
|
+
--color-info-foreground: hsl(0 0% 100%);
|
|
71
|
+
--color-glass: rgba(0 0 0 / 0.25);
|
|
72
|
+
--color-glass-border: rgba(255 255 255 / 0.10);
|
|
73
|
+
--color-overlay: rgba(0 0 0 / 0.75);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Reduced-motion — disables all transitions/animations */
|
|
78
|
+
@media (prefers-reduced-motion: reduce) {
|
|
79
|
+
* {
|
|
80
|
+
transition-duration: 0ms !important;
|
|
81
|
+
animation-duration: 0ms !important;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './colors';
|
|
2
|
+
export * from './typography';
|
|
3
|
+
export * from './layout';
|
|
4
|
+
export * from './motion';
|
|
5
|
+
|
|
6
|
+
// Export themes
|
|
7
|
+
export * from './themes/default';
|
|
8
|
+
export * from './themes/dark';
|
|
9
|
+
export * from './themes/ocean';
|
|
10
|
+
export * from './themes/rose';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nativecn Spacing, Radius, and Shadow Tokens
|
|
3
|
+
* Single source of truth for all layout metrics.
|
|
4
|
+
*/
|
|
5
|
+
import type { ViewStyle } from 'react-native';
|
|
6
|
+
|
|
7
|
+
// ─── Spacing (4pt grid) ───────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export const spacing = {
|
|
10
|
+
0: 0,
|
|
11
|
+
0.5: 2,
|
|
12
|
+
1: 4,
|
|
13
|
+
1.5: 6,
|
|
14
|
+
2: 8,
|
|
15
|
+
2.5: 10,
|
|
16
|
+
3: 12,
|
|
17
|
+
3.5: 14,
|
|
18
|
+
4: 16,
|
|
19
|
+
5: 20,
|
|
20
|
+
6: 24,
|
|
21
|
+
7: 28,
|
|
22
|
+
8: 32,
|
|
23
|
+
9: 36,
|
|
24
|
+
10: 40,
|
|
25
|
+
11: 44,
|
|
26
|
+
12: 48,
|
|
27
|
+
14: 56,
|
|
28
|
+
16: 64,
|
|
29
|
+
20: 80,
|
|
30
|
+
24: 96,
|
|
31
|
+
28: 112,
|
|
32
|
+
32: 128,
|
|
33
|
+
36: 144,
|
|
34
|
+
40: 160,
|
|
35
|
+
48: 192,
|
|
36
|
+
56: 224,
|
|
37
|
+
64: 256,
|
|
38
|
+
72: 288,
|
|
39
|
+
80: 320,
|
|
40
|
+
96: 384,
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
43
|
+
export type SpacingKey = keyof typeof spacing;
|
|
44
|
+
|
|
45
|
+
// ─── Border Radius ─────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
export const radius = {
|
|
48
|
+
none: 0,
|
|
49
|
+
sm: 4,
|
|
50
|
+
md: 6,
|
|
51
|
+
lg: 8,
|
|
52
|
+
xl: 12,
|
|
53
|
+
'2xl': 16,
|
|
54
|
+
'3xl': 24,
|
|
55
|
+
full: 9999,
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
export type RadiusKey = keyof typeof radius;
|
|
59
|
+
|
|
60
|
+
// ─── Elevation / Shadows ─────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Cross-platform elevation specs.
|
|
64
|
+
* iOS uses shadowColor/offset/radius/opacity; Android uses elevation.
|
|
65
|
+
*/
|
|
66
|
+
export const shadows = {
|
|
67
|
+
none: {
|
|
68
|
+
shadowColor: 'transparent',
|
|
69
|
+
shadowOffset: { width: 0, height: 0 },
|
|
70
|
+
shadowOpacity: 0,
|
|
71
|
+
shadowRadius: 0,
|
|
72
|
+
elevation: 0,
|
|
73
|
+
},
|
|
74
|
+
sm: {
|
|
75
|
+
shadowColor: '#000',
|
|
76
|
+
shadowOffset: { width: 0, height: 1 },
|
|
77
|
+
shadowOpacity: 0.05,
|
|
78
|
+
shadowRadius: 2,
|
|
79
|
+
elevation: 1,
|
|
80
|
+
},
|
|
81
|
+
md: {
|
|
82
|
+
shadowColor: '#000',
|
|
83
|
+
shadowOffset: { width: 0, height: 2 },
|
|
84
|
+
shadowOpacity: 0.08,
|
|
85
|
+
shadowRadius: 6,
|
|
86
|
+
elevation: 3,
|
|
87
|
+
},
|
|
88
|
+
lg: {
|
|
89
|
+
shadowColor: '#000',
|
|
90
|
+
shadowOffset: { width: 0, height: 4 },
|
|
91
|
+
shadowOpacity: 0.12,
|
|
92
|
+
shadowRadius: 12,
|
|
93
|
+
elevation: 6,
|
|
94
|
+
},
|
|
95
|
+
xl: {
|
|
96
|
+
shadowColor: '#000',
|
|
97
|
+
shadowOffset: { width: 0, height: 8 },
|
|
98
|
+
shadowOpacity: 0.15,
|
|
99
|
+
shadowRadius: 20,
|
|
100
|
+
elevation: 10,
|
|
101
|
+
},
|
|
102
|
+
'2xl': {
|
|
103
|
+
shadowColor: '#000',
|
|
104
|
+
shadowOffset: { width: 0, height: 16 },
|
|
105
|
+
shadowOpacity: 0.20,
|
|
106
|
+
shadowRadius: 32,
|
|
107
|
+
elevation: 16,
|
|
108
|
+
},
|
|
109
|
+
} as const satisfies Record<string, ViewStyle>;
|
|
110
|
+
|
|
111
|
+
export type ShadowKey = keyof typeof shadows;
|
|
112
|
+
|
|
113
|
+
// ─── Border Widths ─────────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
export const borderWidths = {
|
|
116
|
+
0: 0,
|
|
117
|
+
1: 1,
|
|
118
|
+
2: 2,
|
|
119
|
+
4: 4,
|
|
120
|
+
8: 8,
|
|
121
|
+
} as const;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nativecn Motion Tokens
|
|
3
|
+
* All spring configs, durations, and easing functions in one place.
|
|
4
|
+
* Every animated component MUST reference these — never hardcode values.
|
|
5
|
+
*/
|
|
6
|
+
import type { WithSpringConfig } from 'react-native-reanimated';
|
|
7
|
+
|
|
8
|
+
// ─── Spring Presets ────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export type SpringPreset = keyof typeof springPresets;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Physics-based spring configurations.
|
|
14
|
+
* Choose the preset based on the semantic meaning of the motion:
|
|
15
|
+
* - snappy → immediate feedback (button presses, toggles)
|
|
16
|
+
* - bouncy → playful entrance (FAB expand, modal pop)
|
|
17
|
+
* - smooth → standard transitions (drawer, dialog)
|
|
18
|
+
* - gentle → subtle motion (progress, skeleton)
|
|
19
|
+
*/
|
|
20
|
+
export const springPresets = {
|
|
21
|
+
snappy: {
|
|
22
|
+
damping: 20,
|
|
23
|
+
stiffness: 400,
|
|
24
|
+
mass: 0.5,
|
|
25
|
+
overshootClamping: false,
|
|
26
|
+
},
|
|
27
|
+
bouncy: {
|
|
28
|
+
damping: 12,
|
|
29
|
+
stiffness: 200,
|
|
30
|
+
mass: 0.8,
|
|
31
|
+
overshootClamping: false,
|
|
32
|
+
},
|
|
33
|
+
smooth: {
|
|
34
|
+
damping: 28,
|
|
35
|
+
stiffness: 280,
|
|
36
|
+
mass: 1.0,
|
|
37
|
+
overshootClamping: false,
|
|
38
|
+
},
|
|
39
|
+
gentle: {
|
|
40
|
+
damping: 40,
|
|
41
|
+
stiffness: 150,
|
|
42
|
+
mass: 1.2,
|
|
43
|
+
overshootClamping: false,
|
|
44
|
+
},
|
|
45
|
+
/** Zero-duration spring for prefers-reduced-motion */
|
|
46
|
+
instant: {
|
|
47
|
+
damping: 1000,
|
|
48
|
+
stiffness: 1000,
|
|
49
|
+
mass: 0.001,
|
|
50
|
+
overshootClamping: true,
|
|
51
|
+
},
|
|
52
|
+
} as const satisfies Record<string, WithSpringConfig>;
|
|
53
|
+
|
|
54
|
+
// ─── Duration Scale ────────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
export const durations = {
|
|
57
|
+
instant: 0,
|
|
58
|
+
fast: 120,
|
|
59
|
+
normal: 200,
|
|
60
|
+
slow: 350,
|
|
61
|
+
xslow: 500,
|
|
62
|
+
} as const;
|
|
63
|
+
|
|
64
|
+
export type DurationKey = keyof typeof durations;
|
|
65
|
+
|
|
66
|
+
// ─── Easing Curves ────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
export const easings = {
|
|
69
|
+
easeOut: [0.0, 0.0, 0.2, 1.0] as [number, number, number, number],
|
|
70
|
+
easeIn: [0.4, 0.0, 1.0, 1.0] as [number, number, number, number],
|
|
71
|
+
easeInOut: [0.4, 0.0, 0.2, 1.0] as [number, number, number, number],
|
|
72
|
+
linear: [0.0, 0.0, 1.0, 1.0] as [number, number, number, number],
|
|
73
|
+
} as const;
|
|
74
|
+
|
|
75
|
+
// ─── Press Scale Values ────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
/** Standard scale-down values when a pressable is actively pressed. */
|
|
78
|
+
export const pressScales = {
|
|
79
|
+
none: 1.0,
|
|
80
|
+
subtle: 0.98,
|
|
81
|
+
normal: 0.96,
|
|
82
|
+
strong: 0.93,
|
|
83
|
+
} as const;
|
|
84
|
+
|
|
85
|
+
export type PressScaleKey = keyof typeof pressScales;
|
|
86
|
+
|
|
87
|
+
// ─── Stagger Delays ────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/** Delay increments for staggered list animations */
|
|
90
|
+
export const staggerDelays = {
|
|
91
|
+
fast: 30,
|
|
92
|
+
normal: 60,
|
|
93
|
+
slow: 100,
|
|
94
|
+
} as const;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SemanticColors } from '../colors';
|
|
2
|
+
import { lightColors } from '../colors';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ocean theme (Light)
|
|
6
|
+
* A deep blue/teal aesthetic.
|
|
7
|
+
*/
|
|
8
|
+
export const oceanThemeLight: SemanticColors = {
|
|
9
|
+
...lightColors,
|
|
10
|
+
primary: { DEFAULT: '199 89% 48%', foreground: '0 0% 100%' }, // Cyan/Ocean primary
|
|
11
|
+
ring: '199 89% 48%',
|
|
12
|
+
background: '210 20% 98%', // Very slightly blue-tinted background
|
|
13
|
+
foreground: '222 47% 11%',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Ocean theme (Dark)
|
|
18
|
+
*/
|
|
19
|
+
export const oceanThemeDark: SemanticColors = {
|
|
20
|
+
...oceanThemeLight,
|
|
21
|
+
background: '222 47% 11%', // Deep navy background
|
|
22
|
+
foreground: '210 20% 98%',
|
|
23
|
+
card: { DEFAULT: '222 47% 11%', foreground: '210 20% 98%' },
|
|
24
|
+
popover: { DEFAULT: '222 47% 11%', foreground: '210 20% 98%' },
|
|
25
|
+
muted: { DEFAULT: '217.2 32.6% 17.5%', foreground: '215 20.2% 65.1%' },
|
|
26
|
+
border: '217.2 32.6% 17.5%',
|
|
27
|
+
input: '217.2 32.6% 17.5%',
|
|
28
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SemanticColors } from '../colors';
|
|
2
|
+
import { lightColors } from '../colors';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rose theme (Light)
|
|
6
|
+
*/
|
|
7
|
+
export const roseThemeLight: SemanticColors = {
|
|
8
|
+
...lightColors,
|
|
9
|
+
primary: { DEFAULT: '346.8 77.2% 49.8%', foreground: '355.7 100% 97.3%' }, // Rose primary
|
|
10
|
+
ring: '346.8 77.2% 49.8%',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Rose theme (Dark)
|
|
15
|
+
*/
|
|
16
|
+
export const roseThemeDark: SemanticColors = {
|
|
17
|
+
...roseThemeLight,
|
|
18
|
+
background: '20 14.3% 4.1%',
|
|
19
|
+
foreground: '0 0% 95%',
|
|
20
|
+
card: { DEFAULT: '24 9.8% 10%', foreground: '0 0% 95%' },
|
|
21
|
+
popover: { DEFAULT: '20 14.3% 4.1%', foreground: '0 0% 95%' },
|
|
22
|
+
primary: { DEFAULT: '346.8 77.2% 49.8%', foreground: '355.7 100% 97.3%' },
|
|
23
|
+
secondary: { DEFAULT: '12 6.5% 15.1%', foreground: '0 0% 98%' },
|
|
24
|
+
muted: { DEFAULT: '12 6.5% 15.1%', foreground: '24 5.4% 63.9%' },
|
|
25
|
+
accent: { DEFAULT: '12 6.5% 15.1%', foreground: '0 0% 98%' },
|
|
26
|
+
destructive: { DEFAULT: '0 62.8% 30.6%', foreground: '0 85.7% 97.3%' },
|
|
27
|
+
border: '12 6.5% 15.1%',
|
|
28
|
+
input: '12 6.5% 15.1%',
|
|
29
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nativecn Typography Tokens
|
|
3
|
+
* All font sizes, weights, line heights, and font families.
|
|
4
|
+
*/
|
|
5
|
+
import type { TextStyle } from 'react-native';
|
|
6
|
+
|
|
7
|
+
export const fontFamilies = {
|
|
8
|
+
sans: 'Inter',
|
|
9
|
+
mono: 'JetBrains Mono',
|
|
10
|
+
/** Fallback: uses the system default */
|
|
11
|
+
system: undefined,
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export const fontSizes = {
|
|
15
|
+
display: 48,
|
|
16
|
+
h1: 36,
|
|
17
|
+
h2: 30,
|
|
18
|
+
h3: 24,
|
|
19
|
+
h4: 20,
|
|
20
|
+
lg: 18,
|
|
21
|
+
base: 16,
|
|
22
|
+
sm: 14,
|
|
23
|
+
xs: 12,
|
|
24
|
+
'2xs': 10,
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type FontSizeKey = keyof typeof fontSizes;
|
|
28
|
+
|
|
29
|
+
export const fontWeights = {
|
|
30
|
+
thin: '100',
|
|
31
|
+
light: '300',
|
|
32
|
+
normal: '400',
|
|
33
|
+
medium: '500',
|
|
34
|
+
semibold: '600',
|
|
35
|
+
bold: '700',
|
|
36
|
+
extrabold: '800',
|
|
37
|
+
black: '900',
|
|
38
|
+
} as const satisfies Record<string, TextStyle['fontWeight']>;
|
|
39
|
+
|
|
40
|
+
export const lineHeights = {
|
|
41
|
+
none: 1,
|
|
42
|
+
tight: 1.25,
|
|
43
|
+
snug: 1.375,
|
|
44
|
+
normal: 1.5,
|
|
45
|
+
relaxed: 1.625,
|
|
46
|
+
loose: 2,
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
export const letterSpacings = {
|
|
50
|
+
tighter: -0.8,
|
|
51
|
+
tight: -0.4,
|
|
52
|
+
normal: 0,
|
|
53
|
+
wide: 0.4,
|
|
54
|
+
wider: 0.8,
|
|
55
|
+
widest: 1.6,
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
/** Pre-composed text style sets for semantic typography components */
|
|
59
|
+
export const textStyles = {
|
|
60
|
+
display: {
|
|
61
|
+
fontSize: fontSizes.display,
|
|
62
|
+
fontWeight: fontWeights.bold,
|
|
63
|
+
lineHeight: fontSizes.display * lineHeights.tight,
|
|
64
|
+
letterSpacing: letterSpacings.tight,
|
|
65
|
+
},
|
|
66
|
+
h1: {
|
|
67
|
+
fontSize: fontSizes.h1,
|
|
68
|
+
fontWeight: fontWeights.bold,
|
|
69
|
+
lineHeight: fontSizes.h1 * lineHeights.tight,
|
|
70
|
+
letterSpacing: letterSpacings.tight,
|
|
71
|
+
},
|
|
72
|
+
h2: {
|
|
73
|
+
fontSize: fontSizes.h2,
|
|
74
|
+
fontWeight: fontWeights.semibold,
|
|
75
|
+
lineHeight: fontSizes.h2 * lineHeights.snug,
|
|
76
|
+
letterSpacing: letterSpacings.tight,
|
|
77
|
+
},
|
|
78
|
+
h3: {
|
|
79
|
+
fontSize: fontSizes.h3,
|
|
80
|
+
fontWeight: fontWeights.semibold,
|
|
81
|
+
lineHeight: fontSizes.h3 * lineHeights.snug,
|
|
82
|
+
letterSpacing: letterSpacings.normal,
|
|
83
|
+
},
|
|
84
|
+
h4: {
|
|
85
|
+
fontSize: fontSizes.h4,
|
|
86
|
+
fontWeight: fontWeights.semibold,
|
|
87
|
+
lineHeight: fontSizes.h4 * lineHeights.normal,
|
|
88
|
+
letterSpacing: letterSpacings.normal,
|
|
89
|
+
},
|
|
90
|
+
bodyLg: {
|
|
91
|
+
fontSize: fontSizes.lg,
|
|
92
|
+
fontWeight: fontWeights.normal,
|
|
93
|
+
lineHeight: fontSizes.lg * lineHeights.relaxed,
|
|
94
|
+
letterSpacing: letterSpacings.normal,
|
|
95
|
+
},
|
|
96
|
+
body: {
|
|
97
|
+
fontSize: fontSizes.base,
|
|
98
|
+
fontWeight: fontWeights.normal,
|
|
99
|
+
lineHeight: fontSizes.base * lineHeights.normal,
|
|
100
|
+
letterSpacing: letterSpacings.normal,
|
|
101
|
+
},
|
|
102
|
+
bodySm: {
|
|
103
|
+
fontSize: fontSizes.sm,
|
|
104
|
+
fontWeight: fontWeights.normal,
|
|
105
|
+
lineHeight: fontSizes.sm * lineHeights.normal,
|
|
106
|
+
letterSpacing: letterSpacings.normal,
|
|
107
|
+
},
|
|
108
|
+
caption: {
|
|
109
|
+
fontSize: fontSizes.xs,
|
|
110
|
+
fontWeight: fontWeights.normal,
|
|
111
|
+
lineHeight: fontSizes.xs * lineHeights.normal,
|
|
112
|
+
letterSpacing: letterSpacings.wide,
|
|
113
|
+
},
|
|
114
|
+
code: {
|
|
115
|
+
fontSize: fontSizes.sm,
|
|
116
|
+
fontWeight: fontWeights.normal,
|
|
117
|
+
fontFamily: fontFamilies.mono,
|
|
118
|
+
lineHeight: fontSizes.sm * lineHeights.relaxed,
|
|
119
|
+
letterSpacing: letterSpacings.normal,
|
|
120
|
+
},
|
|
121
|
+
label: {
|
|
122
|
+
fontSize: fontSizes.sm,
|
|
123
|
+
fontWeight: fontWeights.medium,
|
|
124
|
+
lineHeight: fontSizes.sm * lineHeights.normal,
|
|
125
|
+
letterSpacing: letterSpacings.normal,
|
|
126
|
+
},
|
|
127
|
+
} as const satisfies Record<string, TextStyle>;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"paths": {
|
|
6
|
+
"~/*": ["./src/*"]
|
|
7
|
+
},
|
|
8
|
+
"types": ["nativewind/types", "jest"],
|
|
9
|
+
"exactOptionalPropertyTypes": false,
|
|
10
|
+
"rootDir": ".",
|
|
11
|
+
"outDir": "dist"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist", "coverage"]
|
|
15
|
+
}
|