@butternutbox/pawprint-native 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.turbo/turbo-build.log +30 -0
  2. package/COMPONENT_GUIDELINES.md +610 -0
  3. package/README.md +72 -0
  4. package/dist/ibm-plex-sans-condensed-400-normal-I2XLJNNB.woff2 +0 -0
  5. package/dist/ibm-plex-sans-condensed-500-normal-IEQBNVGX.woff2 +0 -0
  6. package/dist/ibm-plex-sans-condensed-600-normal-UX5ZU5T6.woff2 +0 -0
  7. package/dist/ibm-plex-sans-condensed-700-normal-4PFYFTSO.woff2 +0 -0
  8. package/dist/ida-narrow-500-normal-C6I2PK4T.woff2 +0 -0
  9. package/dist/ida-narrow-700-normal-UPHPRIN6.woff2 +0 -0
  10. package/dist/index.cjs +2686 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +780 -0
  13. package/dist/index.d.ts +780 -0
  14. package/dist/index.js +2617 -0
  15. package/dist/index.js.map +1 -0
  16. package/eslint.config.js +3 -0
  17. package/llms.txt +458 -0
  18. package/package.json +57 -0
  19. package/src/components/atoms/Avatar/Avatar.stories.tsx +125 -0
  20. package/src/components/atoms/Avatar/Avatar.tsx +159 -0
  21. package/src/components/atoms/Avatar/index.ts +7 -0
  22. package/src/components/atoms/Badge/Badge.stories.tsx +231 -0
  23. package/src/components/atoms/Badge/Badge.tsx +184 -0
  24. package/src/components/atoms/Badge/index.ts +2 -0
  25. package/src/components/atoms/Button/Button.stories.tsx +145 -0
  26. package/src/components/atoms/Button/Button.tsx +261 -0
  27. package/src/components/atoms/Button/index.ts +7 -0
  28. package/src/components/atoms/Hint/Hint.stories.tsx +84 -0
  29. package/src/components/atoms/Hint/Hint.tsx +59 -0
  30. package/src/components/atoms/Hint/index.ts +2 -0
  31. package/src/components/atoms/Icon/Icon.stories.tsx +200 -0
  32. package/src/components/atoms/Icon/Icon.tsx +112 -0
  33. package/src/components/atoms/Icon/index.ts +8 -0
  34. package/src/components/atoms/IconButton/IconButton.stories.tsx +162 -0
  35. package/src/components/atoms/IconButton/IconButton.tsx +227 -0
  36. package/src/components/atoms/IconButton/index.ts +7 -0
  37. package/src/components/atoms/Illustration/Illustration.stories.tsx +167 -0
  38. package/src/components/atoms/Illustration/Illustration.tsx +81 -0
  39. package/src/components/atoms/Illustration/index.ts +6 -0
  40. package/src/components/atoms/Input/Input.stories.tsx +142 -0
  41. package/src/components/atoms/Input/Input.tsx +110 -0
  42. package/src/components/atoms/Input/InputDescription.tsx +49 -0
  43. package/src/components/atoms/Input/InputError.tsx +39 -0
  44. package/src/components/atoms/Input/InputField.tsx +119 -0
  45. package/src/components/atoms/Input/InputLabel.tsx +61 -0
  46. package/src/components/atoms/Input/index.ts +10 -0
  47. package/src/components/atoms/Link/Link.stories.tsx +119 -0
  48. package/src/components/atoms/Link/Link.tsx +118 -0
  49. package/src/components/atoms/Link/index.ts +2 -0
  50. package/src/components/atoms/Logo/Logo.registry.ts +39 -0
  51. package/src/components/atoms/Logo/Logo.tsx +68 -0
  52. package/src/components/atoms/Logo/index.ts +4 -0
  53. package/src/components/atoms/Spinner/Spinner.stories.tsx +98 -0
  54. package/src/components/atoms/Spinner/Spinner.tsx +91 -0
  55. package/src/components/atoms/Spinner/index.ts +2 -0
  56. package/src/components/atoms/Switch/Switch.stories.tsx +120 -0
  57. package/src/components/atoms/Switch/Switch.tsx +196 -0
  58. package/src/components/atoms/Switch/index.ts +2 -0
  59. package/src/components/atoms/Tag/Tag.stories.tsx +89 -0
  60. package/src/components/atoms/Tag/Tag.tsx +122 -0
  61. package/src/components/atoms/Tag/index.ts +2 -0
  62. package/src/components/atoms/Typography/Typography.stories.tsx +315 -0
  63. package/src/components/atoms/Typography/Typography.tsx +284 -0
  64. package/src/components/atoms/Typography/index.ts +2 -0
  65. package/src/components/atoms/index.ts +14 -0
  66. package/src/components/index.ts +2 -0
  67. package/src/components/molecules/ButtonDock/ButtonDock.stories.tsx +95 -0
  68. package/src/components/molecules/ButtonDock/ButtonDock.tsx +148 -0
  69. package/src/components/molecules/ButtonDock/index.ts +2 -0
  70. package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +82 -0
  71. package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +94 -0
  72. package/src/components/molecules/ButtonGroup/index.ts +2 -0
  73. package/src/components/molecules/Checkbox/Checkbox.stories.tsx +148 -0
  74. package/src/components/molecules/Checkbox/Checkbox.tsx +279 -0
  75. package/src/components/molecules/Checkbox/CheckboxGroup.tsx +53 -0
  76. package/src/components/molecules/Checkbox/index.ts +4 -0
  77. package/src/components/molecules/Radio/Radio.stories.tsx +182 -0
  78. package/src/components/molecules/Radio/Radio.tsx +249 -0
  79. package/src/components/molecules/Radio/RadioGroup.tsx +142 -0
  80. package/src/components/molecules/Radio/index.ts +4 -0
  81. package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +151 -0
  82. package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +323 -0
  83. package/src/components/molecules/SegmentedControl/index.ts +5 -0
  84. package/src/components/molecules/Slider/Slider.stories.tsx +144 -0
  85. package/src/components/molecules/Slider/Slider.tsx +303 -0
  86. package/src/components/molecules/Slider/index.ts +2 -0
  87. package/src/components/molecules/index.ts +6 -0
  88. package/src/fonts/ibm-plex-sans-condensed-400-normal.woff2 +0 -0
  89. package/src/fonts/ibm-plex-sans-condensed-500-normal.woff2 +0 -0
  90. package/src/fonts/ibm-plex-sans-condensed-600-normal.woff2 +0 -0
  91. package/src/fonts/ibm-plex-sans-condensed-700-normal.woff2 +0 -0
  92. package/src/fonts/ida-narrow-500-normal.woff2 +0 -0
  93. package/src/fonts/ida-narrow-700-normal.woff2 +0 -0
  94. package/src/fonts/index.ts +49 -0
  95. package/src/index.ts +9 -0
  96. package/src/theme/PawprintProvider.tsx +26 -0
  97. package/src/theme/ThemeProvider.tsx +63 -0
  98. package/src/theme/index.ts +5 -0
  99. package/src/theme/theme.ts +3 -0
  100. package/src/theme/utils.ts +31 -0
  101. package/src/types/fonts.d.ts +4 -0
  102. package/src/types/index.ts +1 -0
  103. package/src/types/theme.ts +24 -0
  104. package/tsconfig.json +5 -0
  105. package/tsup.config.ts +11 -0
@@ -0,0 +1,315 @@
1
+ import { View, StyleSheet } from "react-native"
2
+ import { Typography } from "./Typography"
3
+ import type { TypographyProps } from "./Typography"
4
+
5
+ const bodyText =
6
+ "We're all familiar with the various sounds our dogs make. The polite little 'woof' at the door, the frantic yipping during a game of fetch."
7
+ const headingText = "Fresh food. Happy dogs."
8
+ const shortText = "Fresh food. Happy dogs."
9
+
10
+ export default {
11
+ title: "Atoms/Typography",
12
+ component: Typography,
13
+ argTypes: {
14
+ variant: {
15
+ control: { type: "select" },
16
+ options: ["body", "heading", "display"],
17
+ description: "Text style variant"
18
+ },
19
+ size: {
20
+ control: { type: "select" },
21
+ options: ["2xs", "xs", "sm", "md", "lg", "xl"],
22
+ description: "Size (available options vary by variant)"
23
+ },
24
+ weight: {
25
+ control: { type: "select" },
26
+ options: ["medium", "semiBold", "bold"],
27
+ description: "Font weight (body variant only)"
28
+ },
29
+ color: {
30
+ control: { type: "select" },
31
+ options: [
32
+ "primary",
33
+ "secondary",
34
+ "success",
35
+ "warning",
36
+ "error",
37
+ "promo",
38
+ "placeholder",
39
+ "disabled",
40
+ "tertiary",
41
+ "alt"
42
+ ],
43
+ description: "Semantic text colour"
44
+ },
45
+ align: {
46
+ control: { type: "select" },
47
+ options: ["left", "center", "right", "justify"],
48
+ description: "Text alignment"
49
+ },
50
+ textTransform: {
51
+ control: { type: "select" },
52
+ options: ["none", "uppercase", "lowercase", "capitalize"],
53
+ description: "CSS text-transform"
54
+ },
55
+ textDecoration: {
56
+ control: { type: "select" },
57
+ options: ["none", "underline", "line-through"],
58
+ description: "Text decoration style"
59
+ },
60
+ noWrap: {
61
+ control: { type: "boolean" },
62
+ description: "Truncate with ellipsis instead of wrapping"
63
+ }
64
+ }
65
+ }
66
+
67
+ export const Playground = (args: TypographyProps) => (
68
+ <View style={styles.container}>
69
+ <Typography {...args}>{bodyText}</Typography>
70
+ </View>
71
+ )
72
+ Playground.args = {
73
+ variant: "body",
74
+ size: "md",
75
+ weight: "medium",
76
+ color: "primary"
77
+ } as TypographyProps
78
+
79
+ export const AllVariants = () => (
80
+ <View style={styles.column}>
81
+ <View style={styles.section}>
82
+ <Typography variant="heading" size="sm" color="tertiary">
83
+ Display
84
+ </Typography>
85
+ {(["lg", "md"] as const).map((size) => (
86
+ <View key={size} style={styles.item}>
87
+ <Typography size="sm" weight="semiBold" color="promo">
88
+ Display {size.toUpperCase()}
89
+ </Typography>
90
+ <Typography variant="display" size={size}>
91
+ {headingText}
92
+ </Typography>
93
+ </View>
94
+ ))}
95
+ </View>
96
+
97
+ <View style={styles.section}>
98
+ <Typography variant="heading" size="sm" color="tertiary">
99
+ Heading
100
+ </Typography>
101
+ {(["xl", "lg", "md", "sm", "xs", "2xs"] as const).map((size) => (
102
+ <View key={size} style={styles.item}>
103
+ <Typography size="sm" weight="semiBold" color="promo">
104
+ Heading {size.toUpperCase()}
105
+ </Typography>
106
+ <Typography variant="heading" size={size}>
107
+ {headingText}
108
+ </Typography>
109
+ </View>
110
+ ))}
111
+ </View>
112
+
113
+ <View style={styles.section}>
114
+ <Typography variant="heading" size="sm" color="tertiary">
115
+ Body
116
+ </Typography>
117
+ {(["lg", "md", "sm", "xs"] as const).map((size) => (
118
+ <View key={size} style={styles.item}>
119
+ <Typography size="sm" weight="semiBold" color="promo">
120
+ Body {size.toUpperCase()} — medium / semiBold / bold
121
+ </Typography>
122
+ <View style={styles.weightColumn}>
123
+ {(["medium", "semiBold", "bold"] as const).map((weight) => (
124
+ <Typography key={weight} size={size} weight={weight}>
125
+ {bodyText}
126
+ </Typography>
127
+ ))}
128
+ </View>
129
+ </View>
130
+ ))}
131
+ </View>
132
+ </View>
133
+ )
134
+
135
+ export const Body = () => (
136
+ <View style={styles.column}>
137
+ <View style={styles.section}>
138
+ <Typography variant="heading" size="sm" color="tertiary">
139
+ All Sizes
140
+ </Typography>
141
+ {(["xs", "sm", "md", "lg"] as const).map((size) => (
142
+ <View key={size} style={styles.item}>
143
+ <Typography size="sm" weight="semiBold" color="promo">
144
+ Body {size.toUpperCase()}
145
+ </Typography>
146
+ <Typography size={size}>{bodyText}</Typography>
147
+ </View>
148
+ ))}
149
+ </View>
150
+
151
+ <View style={styles.section}>
152
+ <Typography variant="heading" size="sm" color="tertiary">
153
+ All Weights
154
+ </Typography>
155
+ {(["medium", "semiBold", "bold"] as const).map((weight) => (
156
+ <View key={weight} style={styles.item}>
157
+ <Typography size="sm" weight="semiBold" color="promo">
158
+ {weight}
159
+ </Typography>
160
+ <Typography size="md" weight={weight}>
161
+ {bodyText}
162
+ </Typography>
163
+ </View>
164
+ ))}
165
+ </View>
166
+ </View>
167
+ )
168
+
169
+ export const Heading = () => (
170
+ <View style={styles.column}>
171
+ <Typography variant="heading" size="sm" color="tertiary">
172
+ All Sizes
173
+ </Typography>
174
+ {(["xl", "lg", "md", "sm", "xs", "2xs"] as const).map((size) => (
175
+ <View key={size} style={styles.item}>
176
+ <Typography size="sm" weight="semiBold" color="promo">
177
+ Heading {size.toUpperCase()}
178
+ </Typography>
179
+ <Typography variant="heading" size={size}>
180
+ {headingText}
181
+ </Typography>
182
+ </View>
183
+ ))}
184
+ </View>
185
+ )
186
+
187
+ export const Display = () => (
188
+ <View style={styles.column}>
189
+ <Typography variant="heading" size="sm" color="tertiary">
190
+ All Sizes
191
+ </Typography>
192
+ {(["lg", "md"] as const).map((size) => (
193
+ <View key={size} style={styles.item}>
194
+ <Typography size="sm" weight="semiBold" color="promo">
195
+ Display {size.toUpperCase()}
196
+ </Typography>
197
+ <Typography variant="display" size={size}>
198
+ {headingText}
199
+ </Typography>
200
+ </View>
201
+ ))}
202
+ </View>
203
+ )
204
+
205
+ export const TextAlignment = () => (
206
+ <View style={styles.column}>
207
+ {(["left", "center", "right", "justify"] as const).map((align) => (
208
+ <View key={align} style={styles.item}>
209
+ <Typography size="sm" weight="semiBold" color="tertiary">
210
+ {align.charAt(0).toUpperCase() + align.slice(1)}
211
+ </Typography>
212
+ <Typography align={align}>{bodyText}</Typography>
213
+ </View>
214
+ ))}
215
+ </View>
216
+ )
217
+
218
+ export const TextTransform = () => (
219
+ <View style={styles.column}>
220
+ {(["none", "uppercase", "lowercase", "capitalize"] as const).map(
221
+ (transform) => (
222
+ <View key={transform} style={styles.item}>
223
+ <Typography size="sm" weight="semiBold" color="tertiary">
224
+ {transform}
225
+ </Typography>
226
+ <Typography textTransform={transform}>
227
+ {transform === "capitalize" ? "fresh food. happy dogs." : shortText}
228
+ </Typography>
229
+ </View>
230
+ )
231
+ )}
232
+ </View>
233
+ )
234
+
235
+ export const TextDecoration = () => (
236
+ <View style={styles.column}>
237
+ {(["none", "underline", "line-through"] as const).map((decoration) => (
238
+ <View key={decoration} style={styles.item}>
239
+ <Typography size="sm" weight="semiBold" color="tertiary">
240
+ {decoration}
241
+ </Typography>
242
+ <Typography textDecoration={decoration}>{shortText}</Typography>
243
+ </View>
244
+ ))}
245
+ </View>
246
+ )
247
+
248
+ export const NoWrap = () => (
249
+ <View style={styles.column}>
250
+ <View style={styles.item}>
251
+ <Typography size="sm" weight="semiBold" color="tertiary">
252
+ Normal (wraps)
253
+ </Typography>
254
+ <View style={styles.constrainedBox}>
255
+ <Typography>{bodyText}</Typography>
256
+ </View>
257
+ </View>
258
+
259
+ <View style={styles.item}>
260
+ <Typography size="sm" weight="semiBold" color="tertiary">
261
+ No Wrap (truncates with ellipsis)
262
+ </Typography>
263
+ <View style={styles.constrainedBox}>
264
+ <Typography noWrap>{bodyText}</Typography>
265
+ </View>
266
+ </View>
267
+ </View>
268
+ )
269
+
270
+ export const Colors = () => (
271
+ <View style={styles.column}>
272
+ {(
273
+ [
274
+ "primary",
275
+ "secondary",
276
+ "success",
277
+ "warning",
278
+ "error",
279
+ "promo",
280
+ "placeholder",
281
+ "disabled",
282
+ "tertiary"
283
+ ] as const
284
+ ).map((color) => (
285
+ <Typography key={color} color={color}>
286
+ {`${color} — ${shortText}`}
287
+ </Typography>
288
+ ))}
289
+ </View>
290
+ )
291
+
292
+ const styles = StyleSheet.create({
293
+ container: {
294
+ maxWidth: 600
295
+ },
296
+ column: {
297
+ flexDirection: "column",
298
+ gap: 32
299
+ },
300
+ section: {
301
+ flexDirection: "column",
302
+ gap: 16
303
+ },
304
+ item: {
305
+ flexDirection: "column",
306
+ gap: 4
307
+ },
308
+ weightColumn: {
309
+ flexDirection: "column",
310
+ gap: 4
311
+ },
312
+ constrainedBox: {
313
+ maxWidth: 300
314
+ }
315
+ })
@@ -0,0 +1,284 @@
1
+ import React from "react"
2
+ import { Text, TextStyle, TextProps } from "react-native"
3
+ import { useTheme } from "@emotion/react"
4
+ import styled from "@emotion/native"
5
+ import { type BrandThemeSemanticsColourText } from "@butternutbox/pawprint-tokens"
6
+ import { resolveFont } from "../../../fonts"
7
+
8
+ type BodySize = "xs" | "sm" | "md" | "lg"
9
+ type HeadingSize = "2xs" | "xs" | "sm" | "md" | "lg" | "xl"
10
+ type DisplaySize = "md" | "lg"
11
+ type BodyWeight = "medium" | "semiBold" | "bold"
12
+
13
+ type TextAlign = "left" | "right" | "center" | "justify"
14
+ type TextTransform = "none" | "capitalize" | "uppercase" | "lowercase"
15
+ type TextDecoration = "none" | "underline" | "line-through"
16
+
17
+ type SemanticTextColorKey = Exclude<
18
+ keyof BrandThemeSemanticsColourText,
19
+ "link" | "action"
20
+ >
21
+
22
+ type FlattenTextColorValues<T> = T extends string
23
+ ? T
24
+ : T extends object
25
+ ? {
26
+ [K in keyof T]: FlattenTextColorValues<T[K]>
27
+ }[keyof T]
28
+ : never
29
+
30
+ type SemanticTextColorValue =
31
+ FlattenTextColorValues<BrandThemeSemanticsColourText>
32
+
33
+ type NativeTypographyToken = {
34
+ fontFamily?: string
35
+ fontWeight?: string
36
+ fontSize?: string
37
+ lineHeight?: string
38
+ letterSpacing?: string
39
+ }
40
+
41
+ type NativeTextProps = Omit<TextProps, "style">
42
+
43
+ type BodyVariant = {
44
+ variant?: "body"
45
+ size?: BodySize
46
+ weight?: BodyWeight
47
+ }
48
+
49
+ type HeadingVariant = {
50
+ variant: "heading"
51
+ size?: HeadingSize
52
+ weight?: never
53
+ }
54
+
55
+ type DisplayVariant = {
56
+ variant: "display"
57
+ size?: DisplaySize
58
+ weight?: never
59
+ }
60
+
61
+ type CommonProps = {
62
+ align?: TextAlign
63
+ noWrap?: boolean
64
+ textTransform?: TextTransform
65
+ textDecoration?: TextDecoration
66
+ }
67
+
68
+ type SemanticVariant = (BodyVariant | HeadingVariant | DisplayVariant) & {
69
+ color?: SemanticTextColorKey
70
+ token?: never
71
+ } & CommonProps
72
+
73
+ type TokenVariant = {
74
+ token: NativeTypographyToken
75
+ color?: SemanticTextColorValue
76
+ } & CommonProps
77
+
78
+ export type TypographyProps =
79
+ | (SemanticVariant & NativeTextProps)
80
+ | (TokenVariant & NativeTextProps)
81
+
82
+ const parseTokenValue = (value: string): number => parseFloat(value)
83
+
84
+ /**
85
+ * Converts a letter-spacing token value to an absolute pixel number.
86
+ * Token values can be percentage strings (e.g. "2%", "0.12%") or
87
+ * pixel strings (e.g. "1px"). Percentages are resolved relative to fontSize.
88
+ */
89
+ const resolveLetterSpacing = (
90
+ letterSpacing: string,
91
+ fontSize: number
92
+ ): number => {
93
+ if (letterSpacing.endsWith("%")) {
94
+ return (parseFloat(letterSpacing) / 100) * fontSize
95
+ }
96
+ return parseFloat(letterSpacing)
97
+ }
98
+
99
+ const semanticToStyle = (token: {
100
+ fontFamily: string
101
+ fontWeight: string
102
+ fontSize: string
103
+ lineHeight: string
104
+ letterSpacing?: string
105
+ }): TextStyle => {
106
+ const { fontFamily } = resolveFont(token.fontFamily, token.fontWeight)
107
+ const fontSize = parseTokenValue(token.fontSize)
108
+ return {
109
+ fontFamily,
110
+ fontSize,
111
+ lineHeight: parseTokenValue(token.lineHeight),
112
+ ...(token.letterSpacing && {
113
+ letterSpacing: resolveLetterSpacing(token.letterSpacing, fontSize)
114
+ })
115
+ }
116
+ }
117
+
118
+ const nativeTokenToStyle = (token: NativeTypographyToken): TextStyle => {
119
+ const style: TextStyle = {}
120
+ if (token.fontFamily && token.fontWeight) {
121
+ const { fontFamily } = resolveFont(token.fontFamily, token.fontWeight)
122
+ style.fontFamily = fontFamily
123
+ } else if (token.fontFamily) {
124
+ style.fontFamily = token.fontFamily
125
+ }
126
+ if (token.fontSize) style.fontSize = parseTokenValue(token.fontSize)
127
+ if (token.lineHeight) style.lineHeight = parseTokenValue(token.lineHeight)
128
+ if (token.letterSpacing && style.fontSize) {
129
+ style.letterSpacing = resolveLetterSpacing(
130
+ token.letterSpacing,
131
+ style.fontSize
132
+ )
133
+ }
134
+ return style
135
+ }
136
+
137
+ const VARIANT_DEFAULTS = {
138
+ body: { size: "md" } as const,
139
+ heading: { size: "md" } as const,
140
+ display: { size: "lg" } as const
141
+ }
142
+
143
+ const StyledText = styled(Text)({})
144
+
145
+ /**
146
+ * Typography component for body text, headings, and display copy.
147
+ * Styling and semantics only — no i18n concerns.
148
+ *
149
+ * **Semantic mode:**
150
+ * - `variant="body"` — 4 sizes (xs–lg), 3 weights.
151
+ * - `variant="heading"` — 6 sizes (2xs–xl), responsive (steps down one size on narrow screens).
152
+ * - `variant="display"` — 2 sizes (md/lg), responsive.
153
+ *
154
+ * **Token mode:**
155
+ * - `token` — Pass resolved typography token object from theme.tokens.components
156
+ *
157
+ * @param {"body" | "heading" | "display"} [variant="body"] - Text style variant.
158
+ * @param {"xs" | "sm" | "md" | "lg" | "2xs" | "xl"} [size="md"] - Size token. Available sizes depend on variant.
159
+ * @param {"medium" | "semiBold" | "bold"} [weight="medium"] - Font weight. Body only — heading and display have fixed weight.
160
+ * @param {NativeTypographyToken} [token] - Resolved typography token object (alternative to semantic props).
161
+ * @param {SemanticTextColorKey} [color] - Semantic text color key for semantic mode.
162
+ * @param {SemanticTextColorValue} [color] - Hex color string for token mode.
163
+ * @param {"left" | "right" | "center" | "justify"} [align] - Text alignment.
164
+ * @param {boolean} [noWrap] - If true, text is truncated to one line with an ellipsis.
165
+ * @param {"none" | "capitalize" | "uppercase" | "lowercase"} [textTransform] - Text transformation.
166
+ * @param {"none" | "underline" | "line-through"} [textDecoration] - Text decoration.
167
+ *
168
+ * @example
169
+ * // Semantic usage
170
+ * <Typography size="md" weight="semiBold">Body text</Typography>
171
+ * <Typography variant="heading" size="xl">Page title</Typography>
172
+ * <Typography variant="display" size="lg" color="secondary">Hero copy</Typography>
173
+ *
174
+ * // Token usage (for component-owned text)
175
+ * <Typography
176
+ * token={theme.tokens.components.checkbox.typography.label}
177
+ * color={theme.tokens.components.checkbox.colour.text.title}
178
+ * >
179
+ * Accept terms
180
+ * </Typography>
181
+ */
182
+ export const Typography = React.forwardRef<Text, TypographyProps>(
183
+ (props, ref) => {
184
+ const theme = useTheme()
185
+ const { typography, colour } = theme.tokens.semantics
186
+
187
+ const getCommonStyles = (
188
+ align?: TextAlign,
189
+ textTransform?: TextTransform,
190
+ textDecoration?: TextDecoration
191
+ ): TextStyle => ({
192
+ ...(align && { textAlign: align }),
193
+ ...(textTransform && { textTransform }),
194
+ ...(textDecoration && {
195
+ textDecorationLine: textDecoration as TextStyle["textDecorationLine"]
196
+ })
197
+ })
198
+
199
+ if ("token" in props && props.token) {
200
+ const {
201
+ token,
202
+ color,
203
+ align,
204
+ noWrap,
205
+ textTransform,
206
+ textDecoration,
207
+ ...rest
208
+ } = props
209
+
210
+ const style: TextStyle = {
211
+ ...nativeTokenToStyle(token),
212
+ ...(color ? { color } : {}),
213
+ ...getCommonStyles(align, textTransform, textDecoration)
214
+ }
215
+
216
+ return (
217
+ <StyledText
218
+ ref={ref}
219
+ style={style}
220
+ numberOfLines={noWrap ? 1 : undefined}
221
+ ellipsizeMode={noWrap ? "tail" : undefined}
222
+ {...rest}
223
+ />
224
+ )
225
+ }
226
+
227
+ const {
228
+ variant = "body",
229
+ size,
230
+ weight = "medium",
231
+ color = "primary",
232
+ align,
233
+ noWrap,
234
+ textTransform,
235
+ textDecoration,
236
+ ...rest
237
+ } = props
238
+
239
+ const resolvedSize = size ?? VARIANT_DEFAULTS[variant].size
240
+ const commonStyles = getCommonStyles(align, textTransform, textDecoration)
241
+
242
+ const getSemanticColor = (colorKey: SemanticTextColorKey): TextStyle => ({
243
+ color: colour.text[colorKey]
244
+ })
245
+
246
+ let textStyle: TextStyle = {}
247
+
248
+ if (variant === "body") {
249
+ textStyle = {
250
+ ...semanticToStyle(typography.body[weight][resolvedSize as BodySize]),
251
+ ...getSemanticColor(color),
252
+ ...commonStyles
253
+ }
254
+ } else if (variant === "heading") {
255
+ const headingSize = resolvedSize as HeadingSize
256
+ const tokenSet = typography.heading.mobile[headingSize]
257
+ textStyle = {
258
+ ...semanticToStyle(tokenSet),
259
+ ...getSemanticColor(color),
260
+ ...commonStyles
261
+ }
262
+ } else if (variant === "display") {
263
+ const displaySize = resolvedSize as DisplaySize
264
+ const tokenSet = typography.heading.display.mobile[displaySize]
265
+ textStyle = {
266
+ ...semanticToStyle(tokenSet),
267
+ ...getSemanticColor(color),
268
+ ...commonStyles
269
+ }
270
+ }
271
+
272
+ return (
273
+ <StyledText
274
+ ref={ref}
275
+ style={textStyle}
276
+ numberOfLines={noWrap ? 1 : undefined}
277
+ ellipsizeMode={noWrap ? "tail" : undefined}
278
+ {...rest}
279
+ />
280
+ )
281
+ }
282
+ )
283
+
284
+ Typography.displayName = "Typography"
@@ -0,0 +1,2 @@
1
+ export { Typography } from "./Typography"
2
+ export type { TypographyProps } from "./Typography"
@@ -0,0 +1,14 @@
1
+ export * from "./Avatar"
2
+ export * from "./Typography"
3
+ export * from "./Badge"
4
+ export * from "./Button"
5
+ export * from "./Hint"
6
+ export * from "./Icon"
7
+ export * from "./IconButton"
8
+ export * from "./Illustration"
9
+ export * from "./Input"
10
+ export * from "./Link"
11
+ export * from "./Logo"
12
+ export * from "./Spinner"
13
+ export * from "./Switch"
14
+ export * from "./Tag"
@@ -0,0 +1,2 @@
1
+ export * from "./atoms"
2
+ export * from "./molecules"