@candiesocean/craftui 0.1.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 (45) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +45 -0
  3. package/lib/module/button.js +170 -0
  4. package/lib/module/button.js.map +1 -0
  5. package/lib/module/image.js +4 -0
  6. package/lib/module/image.js.map +1 -0
  7. package/lib/module/index.js +11 -0
  8. package/lib/module/index.js.map +1 -0
  9. package/lib/module/internal/cn.js +8 -0
  10. package/lib/module/internal/cn.js.map +1 -0
  11. package/lib/module/package.json +1 -0
  12. package/lib/module/pressable.js +7 -0
  13. package/lib/module/pressable.js.map +1 -0
  14. package/lib/module/squircle.js +9 -0
  15. package/lib/module/squircle.js.map +1 -0
  16. package/lib/module/text.js +42 -0
  17. package/lib/module/text.js.map +1 -0
  18. package/lib/module/theme.js +33 -0
  19. package/lib/module/theme.js.map +1 -0
  20. package/lib/typescript/package.json +1 -0
  21. package/lib/typescript/src/button.d.ts +181 -0
  22. package/lib/typescript/src/button.d.ts.map +1 -0
  23. package/lib/typescript/src/image.d.ts +2 -0
  24. package/lib/typescript/src/image.d.ts.map +1 -0
  25. package/lib/typescript/src/index.d.ts +12 -0
  26. package/lib/typescript/src/index.d.ts.map +1 -0
  27. package/lib/typescript/src/internal/cn.d.ts +3 -0
  28. package/lib/typescript/src/internal/cn.d.ts.map +1 -0
  29. package/lib/typescript/src/pressable.d.ts +7 -0
  30. package/lib/typescript/src/pressable.d.ts.map +1 -0
  31. package/lib/typescript/src/squircle.d.ts +1529 -0
  32. package/lib/typescript/src/squircle.d.ts.map +1 -0
  33. package/lib/typescript/src/text.d.ts +43 -0
  34. package/lib/typescript/src/text.d.ts.map +1 -0
  35. package/lib/typescript/src/theme.d.ts +22 -0
  36. package/lib/typescript/src/theme.d.ts.map +1 -0
  37. package/package.json +121 -0
  38. package/src/button.tsx +202 -0
  39. package/src/image.tsx +1 -0
  40. package/src/index.tsx +25 -0
  41. package/src/internal/cn.ts +7 -0
  42. package/src/pressable.tsx +6 -0
  43. package/src/squircle.tsx +8 -0
  44. package/src/text.tsx +47 -0
  45. package/src/theme.ts +37 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"squircle.d.ts","sourceRoot":"","sources":["../../../src/squircle.tsx"],"names":[],"mappings":"AAGA,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAA4B,CAAC;AAC3C,QAAA,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAImsyB,CAAC;kBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAJ5ryB,CAAC;AAEzC,eAAe,QAAQ,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,aAAa,EAAE,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { TextProps as RNTextProps } from 'react-native';
2
+ import type { VariantProps } from 'tailwind-variants';
3
+ export declare const textStyles: import("tailwind-variants").TVReturnType<{
4
+ variant: {
5
+ display: string;
6
+ title: string;
7
+ subtitle: string;
8
+ body: string;
9
+ 'body-secondary': string;
10
+ caption: string;
11
+ 'caption-secondary': string;
12
+ button: string;
13
+ };
14
+ }, undefined, "text-foreground", {
15
+ variant: {
16
+ display: string;
17
+ title: string;
18
+ subtitle: string;
19
+ body: string;
20
+ 'body-secondary': string;
21
+ caption: string;
22
+ 'caption-secondary': string;
23
+ button: string;
24
+ };
25
+ }, undefined, import("tailwind-variants").TVReturnType<{
26
+ variant: {
27
+ display: string;
28
+ title: string;
29
+ subtitle: string;
30
+ body: string;
31
+ 'body-secondary': string;
32
+ caption: string;
33
+ 'caption-secondary': string;
34
+ button: string;
35
+ };
36
+ }, undefined, "text-foreground", unknown, unknown, undefined>>;
37
+ export type TextVariant = NonNullable<VariantProps<typeof textStyles>['variant']>;
38
+ export interface TextProps extends RNTextProps, VariantProps<typeof textStyles> {
39
+ className?: string;
40
+ }
41
+ declare function Text({ variant, children, className, ...props }: TextProps): import("react/jsx-runtime").JSX.Element;
42
+ export default Text;
43
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/text.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,cAAc,CAAC;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOtD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8DAiBrB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,WAAW,CACnC,YAAY,CAAC,OAAO,UAAU,CAAC,CAAC,SAAS,CAAC,CAC3C,CAAC;AAEF,MAAM,WAAW,SACf,SAAQ,WAAW,EACjB,YAAY,CAAC,OAAO,UAAU,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,iBAAS,IAAI,CAAC,EAAE,OAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,2CAM3E;AAED,eAAe,IAAI,CAAC"}
@@ -0,0 +1,22 @@
1
+ export declare const themeColorVariables: {
2
+ readonly background: "--color-background";
3
+ readonly backgroundElement: "--color-background-element";
4
+ readonly foreground: "--color-foreground";
5
+ readonly text: "--color-text";
6
+ readonly textSecondary: "--color-text-secondary";
7
+ readonly primary: "--color-primary";
8
+ readonly primaryForeground: "--color-primary-foreground";
9
+ readonly muted: "--color-muted";
10
+ readonly mutedForeground: "--color-muted-foreground";
11
+ readonly card: "--color-card";
12
+ readonly cardForeground: "--color-card-foreground";
13
+ readonly border: "--color-border";
14
+ readonly destructive: "--color-destructive";
15
+ readonly success: "--color-success";
16
+ readonly warning: "--color-warning";
17
+ };
18
+ export type ThemeColorName = keyof typeof themeColorVariables;
19
+ export declare const requiredThemeVariables: ("--color-background" | "--color-background-element" | "--color-foreground" | "--color-text" | "--color-text-secondary" | "--color-primary" | "--color-primary-foreground" | "--color-muted" | "--color-muted-foreground" | "--color-card" | "--color-card-foreground" | "--color-border" | "--color-destructive" | "--color-success" | "--color-warning")[];
20
+ export declare function useThemeColor(colorName: ThemeColorName): string;
21
+ export declare function useThemeColors(): Record<ThemeColorName, string>;
22
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/theme.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;CAgBtB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,mBAAmB,CAAC;AAE9D,eAAO,MAAM,sBAAsB,8VAAqC,CAAC;AAEzE,wBAAgB,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,MAAM,CAE/D;AAED,wBAAgB,cAAc,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAQ/D"}
package/package.json ADDED
@@ -0,0 +1,121 @@
1
+ {
2
+ "name": "@candiesocean/craftui",
3
+ "version": "0.1.0",
4
+ "description": "Minimal React Native UI primitives for Expo apps using Uniwind, Pressto, and rn-squircle",
5
+ "main": "./lib/module/index.js",
6
+ "react-native": "./src/index.tsx",
7
+ "types": "./lib/typescript/src/index.d.ts",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "react-native": "./src/index.tsx",
12
+ "source": "./src/index.tsx",
13
+ "types": "./lib/typescript/src/index.d.ts",
14
+ "default": "./lib/module/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "src",
20
+ "lib",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "example": "yarn workspace craftui-example",
26
+ "build": "bob build",
27
+ "clean": "del-cli lib",
28
+ "prepare": "bob build",
29
+ "pack:check": "npm pack --dry-run",
30
+ "typecheck": "tsc",
31
+ "lint": "eslint \"**/*.{js,ts,tsx}\""
32
+ },
33
+ "keywords": [
34
+ "react-native",
35
+ "expo",
36
+ "ui",
37
+ "design-system",
38
+ "uniwind",
39
+ "rn-squircle",
40
+ "pressto"
41
+ ],
42
+ "author": "candiesocean <candiesoocean@gmail.com>",
43
+ "license": "MIT",
44
+ "publishConfig": {
45
+ "registry": "https://registry.npmjs.org/"
46
+ },
47
+ "devDependencies": {
48
+ "@babel/core": "^7.26.10",
49
+ "@eslint/compat": "^1.3.2",
50
+ "@eslint/eslintrc": "^3.3.1",
51
+ "@eslint/js": "^9.35.0",
52
+ "@react-native/babel-preset": "0.83.0",
53
+ "@react-native/eslint-config": "0.83.0",
54
+ "@types/react": "^19.1.12",
55
+ "del-cli": "^6.0.0",
56
+ "eslint": "^9.35.0",
57
+ "eslint-config-prettier": "^10.1.8",
58
+ "eslint-plugin-prettier": "^5.5.4",
59
+ "expo": "^55.0.0",
60
+ "expo-image": "~55.0.5",
61
+ "pressto": "^0.6.1",
62
+ "prettier": "^2.8.8",
63
+ "react": "19.2.0",
64
+ "react-native": "0.83.2",
65
+ "react-native-builder-bob": "^0.40.18",
66
+ "react-native-gesture-handler": "~2.30.0",
67
+ "react-native-reanimated": "~4.2.1",
68
+ "react-native-worklets": "0.7.2",
69
+ "rn-squircle": "^1.2.4",
70
+ "tailwindcss": "^4.0.0",
71
+ "turbo": "^2.5.6",
72
+ "typescript": "^5.9.2",
73
+ "uniwind": "^1.3.1"
74
+ },
75
+ "dependencies": {
76
+ "clsx": "^2.1.1",
77
+ "tailwind-merge": "^3.4.0",
78
+ "tailwind-variants": "^3.2.2"
79
+ },
80
+ "peerDependencies": {
81
+ "expo": ">=55.0.0",
82
+ "expo-image": ">=55.0.0",
83
+ "pressto": ">=0.6.0",
84
+ "react": ">=19.0.0",
85
+ "react-native": ">=0.81.0",
86
+ "react-native-gesture-handler": ">=2.30.0",
87
+ "react-native-reanimated": ">=4.2.0",
88
+ "react-native-worklets": ">=0.7.0",
89
+ "rn-squircle": ">=1.2.0",
90
+ "uniwind": ">=1.3.0"
91
+ },
92
+ "workspaces": [
93
+ "example"
94
+ ],
95
+ "packageManager": "yarn@4.11.0",
96
+ "react-native-builder-bob": {
97
+ "source": "src",
98
+ "output": "lib",
99
+ "targets": [
100
+ [
101
+ "module",
102
+ {
103
+ "esm": true
104
+ }
105
+ ],
106
+ [
107
+ "typescript",
108
+ {
109
+ "project": "tsconfig.build.json"
110
+ }
111
+ ]
112
+ ]
113
+ },
114
+ "prettier": {
115
+ "quoteProps": "consistent",
116
+ "singleQuote": true,
117
+ "tabWidth": 2,
118
+ "trailingComma": "es5",
119
+ "useTabs": false
120
+ }
121
+ }
package/src/button.tsx ADDED
@@ -0,0 +1,202 @@
1
+ import type { CustomPressableProps } from 'pressto';
2
+ import type { ReactNode } from 'react';
3
+ import { ActivityIndicator, View } from 'react-native';
4
+ import type { VariantProps } from 'tailwind-variants';
5
+ import { tv } from 'tailwind-variants';
6
+ import { withUniwind } from 'uniwind';
7
+ import { cn } from './internal/cn';
8
+ import Pressable from './pressable';
9
+ import Squircle from './squircle';
10
+ import Text from './text';
11
+ import { useThemeColor } from './theme';
12
+
13
+ const StyledView = withUniwind(View);
14
+
15
+ export const buttonStyles = tv({
16
+ slots: {
17
+ container: 'items-center justify-center flex-row gap-2',
18
+ label: 'font-semibold',
19
+ },
20
+ variants: {
21
+ variant: {
22
+ 'primary': {
23
+ container: 'bg-primary',
24
+ label: 'text-primary-foreground',
25
+ },
26
+ 'secondary': {
27
+ container: 'bg-muted',
28
+ label: 'text-foreground',
29
+ },
30
+ 'outline': {
31
+ container: 'bg-transparent border-2 border-border',
32
+ label: 'text-foreground',
33
+ },
34
+ 'ghost': {
35
+ container: 'bg-transparent',
36
+ label: 'text-primary',
37
+ },
38
+ 'destructive': {
39
+ container: 'bg-destructive',
40
+ label: 'text-primary-foreground',
41
+ },
42
+ 'destructive-light': {
43
+ container: 'bg-destructive/10',
44
+ label: 'text-destructive',
45
+ },
46
+ },
47
+ size: {
48
+ sm: {
49
+ container: 'px-4 py-2',
50
+ label: 'text-sm',
51
+ },
52
+ default: {
53
+ container: 'px-6 py-4',
54
+ label: 'text-base',
55
+ },
56
+ lg: {
57
+ container: 'px-8 py-5',
58
+ label: 'text-lg',
59
+ },
60
+ icon: {
61
+ container: 'p-2',
62
+ label: '',
63
+ },
64
+ },
65
+ disabled: {
66
+ true: {
67
+ container: 'opacity-50',
68
+ },
69
+ },
70
+ },
71
+ defaultVariants: {
72
+ variant: 'primary',
73
+ size: 'default',
74
+ disabled: false,
75
+ },
76
+ });
77
+
78
+ type ButtonVariants = VariantProps<typeof buttonStyles>;
79
+
80
+ export type ButtonVariant = NonNullable<ButtonVariants['variant']>;
81
+ export type ButtonSize = NonNullable<ButtonVariants['size']>;
82
+
83
+ export interface ButtonProps
84
+ extends Omit<CustomPressableProps, 'children' | 'onPress'>,
85
+ ButtonVariants {
86
+ children?: ReactNode;
87
+ label?: string;
88
+ loading?: boolean;
89
+ leadingAccessory?: ReactNode;
90
+ trailingAccessory?: ReactNode;
91
+ className?: string;
92
+ labelClassName?: string;
93
+ onPress?: () => void | Promise<void>;
94
+ }
95
+
96
+ export function useButtonForegroundColor(
97
+ variant: ButtonVariant = 'primary'
98
+ ): string {
99
+ const primaryForeground = useThemeColor('primaryForeground');
100
+ const foreground = useThemeColor('foreground');
101
+ const primary = useThemeColor('primary');
102
+ const destructive = useThemeColor('destructive');
103
+
104
+ if (variant === 'secondary' || variant === 'outline') {
105
+ return foreground;
106
+ }
107
+
108
+ if (variant === 'ghost') {
109
+ return primary;
110
+ }
111
+
112
+ if (variant === 'destructive-light') {
113
+ return destructive;
114
+ }
115
+
116
+ return primaryForeground;
117
+ }
118
+
119
+ export function getButtonAccessorySize(size: ButtonSize = 'default'): number {
120
+ if (size === 'sm') {
121
+ return 16;
122
+ }
123
+
124
+ if (size === 'lg') {
125
+ return 24;
126
+ }
127
+
128
+ return 20;
129
+ }
130
+
131
+ function Button({
132
+ children,
133
+ label,
134
+ variant = 'primary',
135
+ size = 'default',
136
+ disabled = false,
137
+ loading = false,
138
+ leadingAccessory,
139
+ trailingAccessory,
140
+ className,
141
+ labelClassName,
142
+ onPress,
143
+ ...props
144
+ }: ButtonProps) {
145
+ const styles = buttonStyles({ variant, size, disabled: disabled || loading });
146
+ const foregroundColor = useButtonForegroundColor(variant);
147
+
148
+ const renderContent = () => {
149
+ if (children) {
150
+ return (
151
+ <StyledView className={cn({ 'opacity-0': loading })}>
152
+ {children}
153
+ </StyledView>
154
+ );
155
+ }
156
+
157
+ return (
158
+ <>
159
+ {leadingAccessory && (
160
+ <StyledView className={cn({ 'opacity-0': loading })}>
161
+ {leadingAccessory}
162
+ </StyledView>
163
+ )}
164
+ {label && (
165
+ <Text
166
+ className={cn(styles.label(), labelClassName, {
167
+ 'opacity-0': loading,
168
+ })}
169
+ >
170
+ {label}
171
+ </Text>
172
+ )}
173
+ {trailingAccessory && (
174
+ <StyledView className={cn({ 'opacity-0': loading })}>
175
+ {trailingAccessory}
176
+ </StyledView>
177
+ )}
178
+ </>
179
+ );
180
+ };
181
+
182
+ return (
183
+ <Pressable
184
+ onPress={onPress}
185
+ enabled={!disabled && !loading}
186
+ accessibilityRole="button"
187
+ accessibilityState={{ disabled: disabled || loading }}
188
+ {...props}
189
+ >
190
+ <Squircle borderRadius={12} className={cn(styles.container(), className)}>
191
+ {renderContent()}
192
+ {loading && (
193
+ <StyledView className="absolute inset-0 items-center justify-center">
194
+ <ActivityIndicator size="small" color={foregroundColor} />
195
+ </StyledView>
196
+ )}
197
+ </Squircle>
198
+ </Pressable>
199
+ );
200
+ }
201
+
202
+ export default Button;
package/src/image.tsx ADDED
@@ -0,0 +1 @@
1
+ export { Image as default } from './squircle';
package/src/index.tsx ADDED
@@ -0,0 +1,25 @@
1
+ export { default as Button } from './button';
2
+ export type { ButtonProps, ButtonSize, ButtonVariant } from './button';
3
+ export {
4
+ buttonStyles,
5
+ getButtonAccessorySize,
6
+ useButtonForegroundColor,
7
+ } from './button';
8
+
9
+ export { default as Image } from './image';
10
+
11
+ export { default as Pressable } from './pressable';
12
+
13
+ export { default as Squircle, Image as SquircleImage } from './squircle';
14
+
15
+ export { default as Text } from './text';
16
+ export type { TextProps, TextVariant } from './text';
17
+ export { textStyles } from './text';
18
+
19
+ export type { ThemeColorName } from './theme';
20
+ export {
21
+ requiredThemeVariables,
22
+ themeColorVariables,
23
+ useThemeColor,
24
+ useThemeColors,
25
+ } from './theme';
@@ -0,0 +1,7 @@
1
+ import type { ClassValue } from 'clsx';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs));
7
+ }
@@ -0,0 +1,6 @@
1
+ import { PressableScale } from 'pressto';
2
+ import { withUniwind } from 'uniwind';
3
+
4
+ const Pressable = withUniwind(PressableScale);
5
+
6
+ export default Pressable;
@@ -0,0 +1,8 @@
1
+ import { SquircleImage, SquircleView } from 'rn-squircle';
2
+ import { withUniwind } from 'uniwind';
3
+
4
+ const Squircle = withUniwind(SquircleView);
5
+ const Image = withUniwind(SquircleImage);
6
+
7
+ export default Squircle;
8
+ export { Image, Image as SquircleImage };
package/src/text.tsx ADDED
@@ -0,0 +1,47 @@
1
+ import type { TextProps as RNTextProps } from 'react-native';
2
+ import { Text as RNText } from 'react-native';
3
+ import type { VariantProps } from 'tailwind-variants';
4
+ import { tv } from 'tailwind-variants';
5
+ import { withUniwind } from 'uniwind';
6
+ import { cn } from './internal/cn';
7
+
8
+ const StyledText = withUniwind(RNText);
9
+
10
+ export const textStyles = tv({
11
+ base: 'text-foreground',
12
+ variants: {
13
+ variant: {
14
+ 'display': 'text-3xl font-bold',
15
+ 'title': 'text-2xl font-bold',
16
+ 'subtitle': 'text-xl font-semibold',
17
+ 'body': 'text-base',
18
+ 'body-secondary': 'text-base text-text-secondary',
19
+ 'caption': 'text-sm',
20
+ 'caption-secondary': 'text-sm text-text-secondary',
21
+ 'button': 'text-base font-semibold text-primary-foreground',
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: 'body',
26
+ },
27
+ });
28
+
29
+ export type TextVariant = NonNullable<
30
+ VariantProps<typeof textStyles>['variant']
31
+ >;
32
+
33
+ export interface TextProps
34
+ extends RNTextProps,
35
+ VariantProps<typeof textStyles> {
36
+ className?: string;
37
+ }
38
+
39
+ function Text({ variant = 'body', children, className, ...props }: TextProps) {
40
+ return (
41
+ <StyledText className={cn(textStyles({ variant }), className)} {...props}>
42
+ {children}
43
+ </StyledText>
44
+ );
45
+ }
46
+
47
+ export default Text;
package/src/theme.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { useCSSVariable } from 'uniwind';
2
+
3
+ export const themeColorVariables = {
4
+ background: '--color-background',
5
+ backgroundElement: '--color-background-element',
6
+ foreground: '--color-foreground',
7
+ text: '--color-text',
8
+ textSecondary: '--color-text-secondary',
9
+ primary: '--color-primary',
10
+ primaryForeground: '--color-primary-foreground',
11
+ muted: '--color-muted',
12
+ mutedForeground: '--color-muted-foreground',
13
+ card: '--color-card',
14
+ cardForeground: '--color-card-foreground',
15
+ border: '--color-border',
16
+ destructive: '--color-destructive',
17
+ success: '--color-success',
18
+ warning: '--color-warning',
19
+ } as const;
20
+
21
+ export type ThemeColorName = keyof typeof themeColorVariables;
22
+
23
+ export const requiredThemeVariables = Object.values(themeColorVariables);
24
+
25
+ export function useThemeColor(colorName: ThemeColorName): string {
26
+ return useCSSVariable(themeColorVariables[colorName]) as string;
27
+ }
28
+
29
+ export function useThemeColors(): Record<ThemeColorName, string> {
30
+ const values = useCSSVariable(requiredThemeVariables) as string[];
31
+ const colorNames = Object.keys(themeColorVariables) as ThemeColorName[];
32
+
33
+ return colorNames.reduce((acc, name, index) => {
34
+ acc[name] = values[index] ?? '';
35
+ return acc;
36
+ }, {} as Record<ThemeColorName, string>);
37
+ }