@ponti-studios/ui 1.0.1 → 1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ponti-studios/ui",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "description": "Shared UI component library for Ponti Studios",
6
6
  "license": "MIT",
@@ -133,13 +133,15 @@
133
133
  "style-dictionary": "5.5.0",
134
134
  "tailwindcss": "^4.3.3",
135
135
  "typescript": "6.0.3",
136
+ "uniwind": "^1.10.1",
136
137
  "vite": "^8.1.5",
137
138
  "vitest": "^4.1.10"
138
139
  },
139
140
  "peerDependencies": {
140
141
  "react": "^18.2.0 || ^19.0.0",
141
142
  "react-dom": "^18.2.0 || ^19.0.0",
142
- "react-native": ">=0.74"
143
+ "react-native": ">=0.74",
144
+ "uniwind": ">=1.10.0"
143
145
  },
144
146
  "peerDependenciesMeta": {
145
147
  "react-dom": {
@@ -147,6 +149,9 @@
147
149
  },
148
150
  "react-native": {
149
151
  "optional": true
152
+ },
153
+ "uniwind": {
154
+ "optional": true
150
155
  }
151
156
  },
152
157
  "packageManager": "pnpm@11.10.0"
@@ -49,18 +49,28 @@ const SheetOverlay = React.forwardRef<
49
49
  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Backdrop>
50
50
  >(({ className, ...props }, ref) => (
51
51
  <SheetPrimitive.Backdrop
52
- className={cn(
53
- "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/80 transition-opacity",
54
- className,
55
- )}
52
+ data-slot="sheet-overlay"
53
+ className={cn("fixed inset-0 z-50 bg-black/80", className)}
56
54
  {...props}
57
55
  ref={ref}
58
56
  />
59
57
  ));
60
58
  SheetOverlay.displayName = "SheetOverlay";
61
59
 
62
- const sheetClassName =
63
- "bg-background text-foreground data-open:animate-in data-closed:animate-out fixed inset-x-0 bottom-0 z-50 flex max-h-[90dvh] min-h-[30dvh] flex-col gap-6 rounded-t-xl border border-b-0 border-l-0 border-r-0 p-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:inset-y-0 md:right-0 md:bottom-auto md:left-auto md:h-full md:w-3/4 md:max-w-sm md:min-h-0 md:rounded-t-none md:rounded-l-xl md:border-t-0 md:border-r-0 md:border-b-0 md:border-l md:p-6 md:pb-6";
60
+ const sheetClassName = [
61
+ // Appearance
62
+ "bg-background text-foreground rounded-xl border shadow-lg",
63
+ // Layout
64
+ "flex flex-col gap-6 p-4",
65
+ // Position: floats off every edge, anchored to the bottom on mobile
66
+ "fixed z-50 inset-x-2 bottom-[max(0.5rem,env(safe-area-inset-bottom))]",
67
+ // Size: mobile
68
+ "max-h-[calc(100dvh-2rem)] min-h-[30dvh]",
69
+ // Position: anchored to the right edge on desktop
70
+ "md:inset-y-2 md:right-2 md:left-auto md:bottom-auto",
71
+ // Size: desktop
72
+ "md:w-3/4 md:max-w-sm md:min-h-0",
73
+ ];
64
74
 
65
75
  interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Popup> {}
66
76
 
@@ -70,7 +80,12 @@ const SheetContent = React.forwardRef<
70
80
  >(({ className, children, ...props }, ref) => (
71
81
  <SheetPortal>
72
82
  <SheetOverlay />
73
- <SheetPrimitive.Popup ref={ref} className={cn(sheetClassName, className)} {...props}>
83
+ <SheetPrimitive.Popup
84
+ ref={ref}
85
+ data-slot="sheet-content"
86
+ className={cn(sheetClassName, className)}
87
+ {...props}
88
+ >
74
89
  <SheetPrimitive.Close className="text-muted-foreground hover:bg-muted hover:text-muted-foreground focus-visible:outline-ring absolute top-3 right-3 inline-flex size-9 items-center justify-center rounded-md border border-transparent transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none md:top-4 md:right-4">
75
90
  <X className="size-4.5" />
76
91
  <span className="sr-only">Close</span>
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import { Text, View, type TextProps, type ViewProps } from "react-native";
3
+
4
+ import { cn } from "../lib/utils";
5
+
6
+ function Card({ className, ...props }: ViewProps) {
7
+ return <View className={cn("bg-card gap-4 rounded border border-border p-4", className)} {...props} />;
8
+ }
9
+
10
+ function CardHeader({ className, ...props }: ViewProps) {
11
+ return <View className={cn("gap-1", className)} {...props} />;
12
+ }
13
+
14
+ function CardTitle({ className, ...props }: TextProps) {
15
+ return <Text className={cn("text-card-foreground text-xl", className)} {...props} />;
16
+ }
17
+
18
+ function CardDescription({ className, ...props }: TextProps) {
19
+ return <Text className={cn("text-muted-foreground text-xs", className)} {...props} />;
20
+ }
21
+
22
+ function CardAction({ className, ...props }: ViewProps) {
23
+ return <View className={cn("self-start", className)} {...props} />;
24
+ }
25
+
26
+ function CardContent({ className, ...props }: ViewProps) {
27
+ return <View className={className} {...props} />;
28
+ }
29
+
30
+ function CardFooter({ className, ...props }: ViewProps) {
31
+ return <View className={cn("flex-row items-center", className)} {...props} />;
32
+ }
33
+
34
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
@@ -0,0 +1,41 @@
1
+ import type { ReactNode } from "react";
2
+ import { Pressable } from "react-native";
3
+ import { useCSSVariable } from "uniwind";
4
+
5
+ interface IconButtonProps {
6
+ accessibilityLabel?: string;
7
+ children: ReactNode;
8
+ disabled?: boolean;
9
+ onPress?: () => void;
10
+ testID?: string;
11
+ variant?: "bordered" | "plain";
12
+ }
13
+
14
+ export function IconButton({
15
+ accessibilityLabel,
16
+ children,
17
+ disabled = false,
18
+ onPress,
19
+ testID,
20
+ variant = "bordered",
21
+ }: IconButtonProps) {
22
+ const [border] = useCSSVariable(["--color-border"]) as string[];
23
+
24
+ return (
25
+ <Pressable
26
+ accessibilityLabel={accessibilityLabel}
27
+ accessibilityRole="button"
28
+ disabled={disabled}
29
+ onPress={onPress}
30
+ testID={testID}
31
+ className={`w-8 h-8 rounded-full border items-center justify-center ${variant === "plain" ? "border-0" : ""}`}
32
+ style={({ pressed }) => [
33
+ { borderColor: variant === "plain" ? "transparent" : border },
34
+ pressed && { opacity: 0.7 },
35
+ disabled && { opacity: 0.4 },
36
+ ]}
37
+ >
38
+ {children}
39
+ </Pressable>
40
+ );
41
+ }
@@ -9,3 +9,20 @@
9
9
  export { createMakeStyles, type NamedStyles } from "./make-styles";
10
10
  export { nativeShadows, type NativeShadowLayer } from "./shadows";
11
11
  export { useColorMode, useThemeColors } from "./use-theme-colors";
12
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./card";
13
+ export { TextField } from "./text-field";
14
+ export { IconButton } from "./icon-button";
15
+
16
+ export {
17
+ typography as nativeTypography,
18
+ spacing as nativeSpacing,
19
+ screenMargin as nativeScreenMargin,
20
+ componentSize as nativeComponentSize,
21
+ borderRadius as nativeBorderRadius,
22
+ streamItem as nativeStreamItem,
23
+ fontFamily as nativeFontFamily,
24
+ type TypographyVariant,
25
+ type SpacingToken,
26
+ type ComponentSizeToken,
27
+ type BorderRadiusToken,
28
+ } from "../styles/tokens/normalize-native";
@@ -0,0 +1,34 @@
1
+ import { forwardRef, useState } from "react";
2
+ import { TextInput, type TextInputProps } from "react-native";
3
+ import { useCSSVariable } from "uniwind";
4
+
5
+ export const TextField = forwardRef<TextInput, TextInputProps>(function TextField(
6
+ { onBlur, onFocus, style, ...props },
7
+ ref,
8
+ ) {
9
+ const [ring, borderInput, foreground, mutedForeground] = useCSSVariable([
10
+ "--color-ring",
11
+ "--color-input",
12
+ "--color-foreground",
13
+ "--color-muted-foreground",
14
+ ]) as string[];
15
+ const [focused, setFocused] = useState(false);
16
+
17
+ return (
18
+ <TextInput
19
+ {...props}
20
+ ref={ref}
21
+ onBlur={(event) => {
22
+ setFocused(false);
23
+ onBlur?.(event);
24
+ }}
25
+ onFocus={(event) => {
26
+ setFocused(true);
27
+ onFocus?.(event);
28
+ }}
29
+ placeholderTextColor={props.placeholderTextColor ?? mutedForeground}
30
+ className="rounded-md border min-h-11 px-4 py-3 text-base"
31
+ style={[{ borderColor: focused ? ring : borderInput, color: foreground }, style]}
32
+ />
33
+ );
34
+ });
@@ -0,0 +1 @@
1
+ /// <reference types="uniwind/types" />
@@ -50,6 +50,46 @@
50
50
  transform: scale(0.95);
51
51
  }
52
52
  }
53
+ @keyframes sheet-slide-in-bottom {
54
+ from {
55
+ transform: translateY(100%);
56
+ opacity: 0;
57
+ }
58
+ to {
59
+ transform: translateY(0);
60
+ opacity: 1;
61
+ }
62
+ }
63
+ @keyframes sheet-slide-out-bottom {
64
+ from {
65
+ transform: translateY(0);
66
+ opacity: 1;
67
+ }
68
+ to {
69
+ transform: translateY(100%);
70
+ opacity: 0;
71
+ }
72
+ }
73
+ @keyframes sheet-slide-in-right {
74
+ from {
75
+ transform: translateX(100%);
76
+ opacity: 0;
77
+ }
78
+ to {
79
+ transform: translateX(0);
80
+ opacity: 1;
81
+ }
82
+ }
83
+ @keyframes sheet-slide-out-right {
84
+ from {
85
+ transform: translateX(0);
86
+ opacity: 1;
87
+ }
88
+ to {
89
+ transform: translateX(100%);
90
+ opacity: 0;
91
+ }
92
+ }
53
93
 
54
94
  .data-open\:animate-in[data-open],
55
95
  .data-open\:animate-in[data-state="open"] {
@@ -64,6 +104,38 @@
64
104
  zoom-out 120ms ease-in;
65
105
  }
66
106
 
107
+ [data-slot="sheet-overlay"][data-open],
108
+ [data-slot="sheet-overlay"][data-state="open"] {
109
+ animation: fade-in 400ms var(--ease-out);
110
+ }
111
+ [data-slot="sheet-overlay"][data-closed],
112
+ [data-slot="sheet-overlay"][data-state="closed"] {
113
+ /* Must stay in lockstep with [data-slot="sheet-content"]'s close duration below (both 200ms) —
114
+ Base UI gates the whole portal's unmount on this element's own close-animation duration, not
115
+ the content panel's, so a mismatch here yanks the sliding panel off mid-animation. */
116
+ animation: fade-out 200ms var(--ease-in);
117
+ }
118
+
119
+ [data-slot="sheet-content"][data-open],
120
+ [data-slot="sheet-content"][data-state="open"] {
121
+ animation: sheet-slide-in-bottom 300ms var(--ease-drawer);
122
+ }
123
+ [data-slot="sheet-content"][data-closed],
124
+ [data-slot="sheet-content"][data-state="closed"] {
125
+ animation: sheet-slide-out-bottom 200ms var(--ease-drawer);
126
+ }
127
+
128
+ @media (min-width: 768px) {
129
+ [data-slot="sheet-content"][data-open],
130
+ [data-slot="sheet-content"][data-state="open"] {
131
+ animation: sheet-slide-in-right 300ms var(--ease-drawer);
132
+ }
133
+ [data-slot="sheet-content"][data-closed],
134
+ [data-slot="sheet-content"][data-state="closed"] {
135
+ animation: sheet-slide-out-right 200ms var(--ease-drawer);
136
+ }
137
+ }
138
+
67
139
  @media (prefers-reduced-motion: reduce) {
68
140
  .animate-in,
69
141
  .animate-out,
@@ -72,3 +144,22 @@
72
144
  animation: none !important;
73
145
  }
74
146
  }
147
+
148
+ @media (prefers-reduced-motion: reduce) {
149
+ [data-slot="sheet-overlay"][data-open],
150
+ [data-slot="sheet-overlay"][data-state="open"] {
151
+ animation: fade-in 120ms var(--ease-out) !important;
152
+ }
153
+ [data-slot="sheet-overlay"][data-closed],
154
+ [data-slot="sheet-overlay"][data-state="closed"] {
155
+ animation: fade-out 120ms var(--ease-in) !important;
156
+ }
157
+ [data-slot="sheet-content"][data-open],
158
+ [data-slot="sheet-content"][data-state="open"] {
159
+ animation: fade-in var(--duration-150) var(--ease-out) !important;
160
+ }
161
+ [data-slot="sheet-content"][data-closed],
162
+ [data-slot="sheet-content"][data-state="closed"] {
163
+ animation: fade-out 120ms var(--ease-in) !important;
164
+ }
165
+ }
@@ -133,6 +133,7 @@
133
133
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
134
134
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
135
135
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
136
+ --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
136
137
  --duration-75: 75ms;
137
138
  --duration-100: 100ms;
138
139
  --duration-150: 150ms;
@@ -431,6 +431,7 @@ export default {
431
431
  in: [0.4, 0, 1, 1],
432
432
  out: [0, 0, 0.2, 1],
433
433
  "in-out": [0.4, 0, 0.2, 1],
434
+ drawer: [0.32, 0.72, 0, 1],
434
435
  },
435
436
  duration: {
436
437
  75: "75ms",
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Do not edit directly, this file was auto-generated.
3
+ */
4
+
5
+ export default {
6
+ text: {
7
+ "large-title": {
8
+ fontSize: {
9
+ value: 34,
10
+ unit: "px",
11
+ },
12
+ lineHeight: {
13
+ value: 41,
14
+ unit: "px",
15
+ },
16
+ fontWeight: 700,
17
+ letterSpacing: {
18
+ value: -0.6,
19
+ unit: "px",
20
+ },
21
+ },
22
+ title1: {
23
+ fontSize: {
24
+ value: 28,
25
+ unit: "px",
26
+ },
27
+ lineHeight: {
28
+ value: 34,
29
+ unit: "px",
30
+ },
31
+ fontWeight: 700,
32
+ letterSpacing: {
33
+ value: -0.4,
34
+ unit: "px",
35
+ },
36
+ },
37
+ title2: {
38
+ fontSize: {
39
+ value: 22,
40
+ unit: "px",
41
+ },
42
+ lineHeight: {
43
+ value: 28,
44
+ unit: "px",
45
+ },
46
+ fontWeight: 600,
47
+ letterSpacing: {
48
+ value: -0.2,
49
+ unit: "px",
50
+ },
51
+ },
52
+ headline: {
53
+ fontSize: {
54
+ value: 17,
55
+ unit: "px",
56
+ },
57
+ lineHeight: {
58
+ value: 22,
59
+ unit: "px",
60
+ },
61
+ fontWeight: 600,
62
+ letterSpacing: {
63
+ value: -0.1,
64
+ unit: "px",
65
+ },
66
+ },
67
+ body: {
68
+ fontSize: {
69
+ value: 17,
70
+ unit: "px",
71
+ },
72
+ lineHeight: {
73
+ value: 24,
74
+ unit: "px",
75
+ },
76
+ fontWeight: 400,
77
+ letterSpacing: {
78
+ value: 0,
79
+ unit: "px",
80
+ },
81
+ },
82
+ callout: {
83
+ fontSize: {
84
+ value: 16,
85
+ unit: "px",
86
+ },
87
+ lineHeight: {
88
+ value: 22,
89
+ unit: "px",
90
+ },
91
+ fontWeight: 400,
92
+ letterSpacing: {
93
+ value: -0.1,
94
+ unit: "px",
95
+ },
96
+ },
97
+ subhead: {
98
+ fontSize: {
99
+ value: 15,
100
+ unit: "px",
101
+ },
102
+ lineHeight: {
103
+ value: 20,
104
+ unit: "px",
105
+ },
106
+ fontWeight: 400,
107
+ letterSpacing: {
108
+ value: 0,
109
+ unit: "px",
110
+ },
111
+ },
112
+ footnote: {
113
+ fontSize: {
114
+ value: 13,
115
+ unit: "px",
116
+ },
117
+ lineHeight: {
118
+ value: 18,
119
+ unit: "px",
120
+ },
121
+ fontWeight: 400,
122
+ letterSpacing: {
123
+ value: 0,
124
+ unit: "px",
125
+ },
126
+ },
127
+ caption1: {
128
+ fontSize: {
129
+ value: 12,
130
+ unit: "px",
131
+ },
132
+ lineHeight: {
133
+ value: 16,
134
+ unit: "px",
135
+ },
136
+ fontWeight: 400,
137
+ letterSpacing: {
138
+ value: 0,
139
+ unit: "px",
140
+ },
141
+ },
142
+ caption2: {
143
+ fontSize: {
144
+ value: 11,
145
+ unit: "px",
146
+ },
147
+ lineHeight: {
148
+ value: 14,
149
+ unit: "px",
150
+ },
151
+ fontWeight: 500,
152
+ letterSpacing: {
153
+ value: 0.2,
154
+ unit: "px",
155
+ },
156
+ },
157
+ display: {
158
+ fontSize: {
159
+ value: 40,
160
+ unit: "px",
161
+ },
162
+ lineHeight: {
163
+ value: 44,
164
+ unit: "px",
165
+ },
166
+ fontWeight: 700,
167
+ letterSpacing: {
168
+ value: -1.2,
169
+ unit: "px",
170
+ },
171
+ },
172
+ overline: {
173
+ fontSize: {
174
+ value: 11,
175
+ unit: "px",
176
+ },
177
+ lineHeight: {
178
+ value: 16,
179
+ unit: "px",
180
+ },
181
+ fontWeight: 500,
182
+ letterSpacing: {
183
+ value: 0.8,
184
+ unit: "px",
185
+ },
186
+ textTransform: "uppercase",
187
+ },
188
+ mono: {
189
+ fontFamily: "Menlo",
190
+ fontSize: {
191
+ value: 12,
192
+ unit: "px",
193
+ },
194
+ lineHeight: {
195
+ value: 16,
196
+ unit: "px",
197
+ },
198
+ fontWeight: 400,
199
+ letterSpacing: {
200
+ value: 0,
201
+ unit: "px",
202
+ },
203
+ },
204
+ },
205
+ space: {
206
+ sm: "0.5rem",
207
+ md: "0.75rem",
208
+ lg: "1rem",
209
+ xl: "1.5rem",
210
+ },
211
+ "screen-margin": "1rem",
212
+ "component-size": {
213
+ sm: "1rem",
214
+ md: "1.5rem",
215
+ lg: "2rem",
216
+ xl: "2.75rem",
217
+ icon: "1.25rem",
218
+ },
219
+ "border-radius": {
220
+ sm: "0.25rem",
221
+ md: "0.375rem",
222
+ lg: "0.5rem",
223
+ icon: "0.375rem",
224
+ },
225
+ "stream-item": {
226
+ "border-radius": "0.375rem",
227
+ "border-width": "1px",
228
+ gap: "0.5rem",
229
+ "icon-size": "1.5rem",
230
+ "min-height": "50px",
231
+ "padding-horizontal": "0.5rem",
232
+ "padding-vertical": "0.25rem",
233
+ },
234
+ "font-family": {
235
+ primary: "System",
236
+ mono: "Menlo",
237
+ },
238
+ };
@@ -0,0 +1,86 @@
1
+ import nativeGenerated from "./generated/native.generated";
2
+
3
+ const toPixels = (value: string) => {
4
+ if (value.endsWith("rem")) return Number.parseFloat(value) * 16;
5
+ if (value.endsWith("px")) return Number.parseFloat(value);
6
+ throw new Error(`Unsupported dimension: ${value}`);
7
+ };
8
+
9
+ const unwrap = (v: { value: number; unit?: string } | number) =>
10
+ typeof v === "number" ? v : v.value;
11
+
12
+ type DimensionValue = number | { value: number; unit?: string };
13
+
14
+ const makeFlat = (raw: Record<string, unknown>) => ({
15
+ fontFamily: raw.fontFamily as string | undefined,
16
+ fontSize: unwrap(raw.fontSize as DimensionValue),
17
+ fontWeight: raw.fontWeight as number,
18
+ lineHeight: unwrap(raw.lineHeight as DimensionValue),
19
+ letterSpacing: unwrap(raw.letterSpacing as DimensionValue),
20
+ ...(raw.textTransform ? { textTransform: raw.textTransform as "uppercase" | "lowercase" | "capitalize" | "none" } : {}),
21
+ });
22
+
23
+ const generated = nativeGenerated as unknown as {
24
+ text: Record<string, Record<string, unknown>>;
25
+ space: Record<string, string>;
26
+ "screen-margin": string;
27
+ "component-size": Record<string, string>;
28
+ "border-radius": Record<string, string>;
29
+ "stream-item": Record<string, string>;
30
+ "font-family": { primary: string; mono: string };
31
+ };
32
+
33
+ const primaryFont = generated["font-family"].primary;
34
+
35
+ const typographyEntries = Object.entries(generated.text).map(([key, raw]) => {
36
+ const flat = makeFlat(raw);
37
+ if (!flat.fontFamily) flat.fontFamily = primaryFont;
38
+ return [key, flat] as const;
39
+ });
40
+
41
+ export const typography = Object.fromEntries(typographyEntries) as Record<
42
+ string,
43
+ {
44
+ fontFamily: string;
45
+ fontSize: number;
46
+ fontWeight: number;
47
+ lineHeight: number;
48
+ letterSpacing: number;
49
+ textTransform?: "uppercase" | "lowercase" | "capitalize" | "none";
50
+ }
51
+ >;
52
+
53
+ export type TypographyVariant = keyof typeof typography;
54
+
55
+ export const spacing = Object.fromEntries(
56
+ Object.entries(generated.space).map(([key, value]) => [key, toPixels(value)]),
57
+ ) as Record<keyof typeof generated.space, number>;
58
+
59
+ export type SpacingToken = keyof typeof spacing;
60
+
61
+ export const screenMargin = toPixels(generated["screen-margin"]);
62
+
63
+ export const componentSize = Object.fromEntries(
64
+ Object.entries(generated["component-size"]).map(([key, value]) => [key, toPixels(value)]),
65
+ ) as Record<keyof typeof generated["component-size"], number>;
66
+
67
+ export type ComponentSizeToken = keyof typeof componentSize;
68
+
69
+ export const borderRadius = Object.fromEntries(
70
+ Object.entries(generated["border-radius"]).map(([key, value]) => [key, toPixels(value)]),
71
+ ) as Record<keyof typeof generated["border-radius"], number>;
72
+
73
+ export type BorderRadiusToken = keyof typeof borderRadius;
74
+
75
+ const rawStreamItem = generated["stream-item"];
76
+ export const streamItem = {
77
+ borderRadius: toPixels(rawStreamItem["border-radius"]),
78
+ borderWidth: toPixels(rawStreamItem["border-width"]),
79
+ gap: toPixels(rawStreamItem.gap),
80
+ iconSize: toPixels(rawStreamItem["icon-size"]),
81
+ minHeight: toPixels(rawStreamItem["min-height"]),
82
+ paddingHorizontal: toPixels(rawStreamItem["padding-horizontal"]),
83
+ paddingVertical: toPixels(rawStreamItem["padding-vertical"]),
84
+ };
85
+
86
+ export const fontFamily = generated["font-family"];
@@ -318,7 +318,8 @@
318
318
  "$type": "cubicBezier",
319
319
  "in": { "$value": [0.4, 0, 1, 1] },
320
320
  "out": { "$value": [0, 0, 0.2, 1] },
321
- "in-out": { "$value": [0.4, 0, 0.2, 1] }
321
+ "in-out": { "$value": [0.4, 0, 0.2, 1] },
322
+ "drawer": { "$value": [0.32, 0.72, 0, 1] }
322
323
  },
323
324
  "duration": {
324
325
  "$type": "duration",
@@ -0,0 +1,169 @@
1
+ {
2
+ "$extensions": {
3
+ "org.ponti.description": "Semantic and component tokens for React Native consumers. Values reference the shared foundations so mobile stays in lockstep with web. Consumed via @ponti-studios/ui/native."
4
+ },
5
+ "space": {
6
+ "$type": "dimension",
7
+ "sm": { "$value": "{spacing-scale.2}" },
8
+ "md": { "$value": "{spacing-scale.3}" },
9
+ "lg": { "$value": "{spacing-scale.4}" },
10
+ "xl": { "$value": "{spacing-scale.6}" }
11
+ },
12
+ "screen-margin": {
13
+ "$type": "dimension",
14
+ "$value": "{spacing-scale.4}"
15
+ },
16
+ "component-size": {
17
+ "$type": "dimension",
18
+ "sm": { "$value": "{spacing-scale.4}" },
19
+ "md": { "$value": "{spacing-scale.6}" },
20
+ "lg": { "$value": "{spacing-scale.8}" },
21
+ "xl": { "$value": "{spacing-scale.11}" },
22
+ "icon": { "$value": "{spacing-scale.5}" }
23
+ },
24
+ "border-radius": {
25
+ "$type": "dimension",
26
+ "sm": { "$value": "{radius.sm}" },
27
+ "md": { "$value": "{radius.md}" },
28
+ "lg": { "$value": "{radius.lg}" },
29
+ "icon": { "$value": "{radius.md}" }
30
+ },
31
+ "stream-item": {
32
+ "$description": "Shared look for stream rows (time, inbox). Mirrors what the app used to hand-roll in theme.ts.",
33
+ "$type": "dimension",
34
+ "border-radius": { "$value": "{radius.md}" },
35
+ "border-width": { "$value": { "value": 1, "unit": "px" } },
36
+ "gap": { "$value": "{spacing-scale.2}" },
37
+ "icon-size": { "$value": "{component-size.md}" },
38
+ "min-height": { "$value": { "value": 50, "unit": "px" } },
39
+ "padding-horizontal": { "$value": "{spacing-scale.2}" },
40
+ "padding-vertical": { "$value": "{spacing-scale.1}" }
41
+ },
42
+ "font-family": {
43
+ "$type": "string",
44
+ "primary": { "$value": "System" },
45
+ "mono": { "$value": "Menlo" }
46
+ },
47
+ "text": {
48
+ "$description": "Apple-HIG-aligned type ramp for native editorial text.",
49
+ "large-title": {
50
+ "$type": "typography",
51
+ "$value": {
52
+ "fontSize": { "value": 34, "unit": "px" },
53
+ "lineHeight": { "value": 41, "unit": "px" },
54
+ "fontWeight": "{font-weight.bold}",
55
+ "letterSpacing": { "value": -0.6, "unit": "px" }
56
+ }
57
+ },
58
+ "title1": {
59
+ "$type": "typography",
60
+ "$value": {
61
+ "fontSize": { "value": 28, "unit": "px" },
62
+ "lineHeight": { "value": 34, "unit": "px" },
63
+ "fontWeight": "{font-weight.bold}",
64
+ "letterSpacing": { "value": -0.4, "unit": "px" }
65
+ }
66
+ },
67
+ "title2": {
68
+ "$type": "typography",
69
+ "$value": {
70
+ "fontSize": { "value": 22, "unit": "px" },
71
+ "lineHeight": { "value": 28, "unit": "px" },
72
+ "fontWeight": "{font-weight.semibold}",
73
+ "letterSpacing": { "value": -0.2, "unit": "px" }
74
+ }
75
+ },
76
+ "headline": {
77
+ "$type": "typography",
78
+ "$value": {
79
+ "fontSize": { "value": 17, "unit": "px" },
80
+ "lineHeight": { "value": 22, "unit": "px" },
81
+ "fontWeight": "{font-weight.semibold}",
82
+ "letterSpacing": { "value": -0.1, "unit": "px" }
83
+ }
84
+ },
85
+ "body": {
86
+ "$type": "typography",
87
+ "$value": {
88
+ "fontSize": { "value": 17, "unit": "px" },
89
+ "lineHeight": { "value": 24, "unit": "px" },
90
+ "fontWeight": "{font-weight.normal}",
91
+ "letterSpacing": { "value": 0, "unit": "px" }
92
+ }
93
+ },
94
+ "callout": {
95
+ "$type": "typography",
96
+ "$value": {
97
+ "fontSize": { "value": 16, "unit": "px" },
98
+ "lineHeight": { "value": 22, "unit": "px" },
99
+ "fontWeight": "{font-weight.normal}",
100
+ "letterSpacing": { "value": -0.1, "unit": "px" }
101
+ }
102
+ },
103
+ "subhead": {
104
+ "$type": "typography",
105
+ "$value": {
106
+ "fontSize": { "value": 15, "unit": "px" },
107
+ "lineHeight": { "value": 20, "unit": "px" },
108
+ "fontWeight": "{font-weight.normal}",
109
+ "letterSpacing": { "value": 0, "unit": "px" }
110
+ }
111
+ },
112
+ "footnote": {
113
+ "$type": "typography",
114
+ "$value": {
115
+ "fontSize": { "value": 13, "unit": "px" },
116
+ "lineHeight": { "value": 18, "unit": "px" },
117
+ "fontWeight": "{font-weight.normal}",
118
+ "letterSpacing": { "value": 0, "unit": "px" }
119
+ }
120
+ },
121
+ "caption1": {
122
+ "$type": "typography",
123
+ "$value": {
124
+ "fontSize": { "value": 12, "unit": "px" },
125
+ "lineHeight": { "value": 16, "unit": "px" },
126
+ "fontWeight": "{font-weight.normal}",
127
+ "letterSpacing": { "value": 0, "unit": "px" }
128
+ }
129
+ },
130
+ "caption2": {
131
+ "$type": "typography",
132
+ "$value": {
133
+ "fontSize": { "value": 11, "unit": "px" },
134
+ "lineHeight": { "value": 14, "unit": "px" },
135
+ "fontWeight": "{font-weight.medium}",
136
+ "letterSpacing": { "value": 0.2, "unit": "px" }
137
+ }
138
+ },
139
+ "display": {
140
+ "$type": "typography",
141
+ "$value": {
142
+ "fontSize": { "value": 40, "unit": "px" },
143
+ "lineHeight": { "value": 44, "unit": "px" },
144
+ "fontWeight": "{font-weight.bold}",
145
+ "letterSpacing": { "value": -1.2, "unit": "px" }
146
+ }
147
+ },
148
+ "overline": {
149
+ "$type": "typography",
150
+ "$value": {
151
+ "fontSize": { "value": 11, "unit": "px" },
152
+ "lineHeight": { "value": 16, "unit": "px" },
153
+ "fontWeight": "{font-weight.medium}",
154
+ "letterSpacing": { "value": 0.8, "unit": "px" },
155
+ "textTransform": "uppercase"
156
+ }
157
+ },
158
+ "mono": {
159
+ "$type": "typography",
160
+ "$value": {
161
+ "fontFamily": "{font-family.mono}",
162
+ "fontSize": { "value": 12, "unit": "px" },
163
+ "lineHeight": { "value": 16, "unit": "px" },
164
+ "fontWeight": "{font-weight.normal}",
165
+ "letterSpacing": { "value": 0, "unit": "px" }
166
+ }
167
+ }
168
+ }
169
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Emits ONLY the native semantic/component tokens (native.tokens.json) as a
3
+ * typed module. Foundations are kept in the dictionary so references resolve,
4
+ * but are filtered out of the output — they already ship via
5
+ * foundations.generated.ts.
6
+ */
7
+ export default {
8
+ source: ["src/styles/tokens/source/foundations.tokens.json", "src/styles/tokens/source/native.tokens.json"],
9
+ platforms: {
10
+ typescript: {
11
+ transformGroup: "js",
12
+ buildPath: "src/styles/tokens/generated/",
13
+ files: [
14
+ {
15
+ destination: "native.generated.ts",
16
+ format: "javascript/esm",
17
+ options: { minify: true },
18
+ filter: (token) => token.filePath.includes("native.tokens.json"),
19
+ },
20
+ ],
21
+ },
22
+ },
23
+ };