@olympusoss/canvas 3.2.1 → 5.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 +75 -65
- package/package.json +11 -5
- package/src/atoms/avatar/avatar.md +185 -0
- package/src/atoms/avatar/avatar.styles.ts +48 -0
- package/src/atoms/avatar/avatar.tsx +99 -0
- package/src/atoms/badge/badge.md +237 -0
- package/src/atoms/badge/badge.styles.ts +79 -0
- package/src/atoms/badge/badge.tsx +86 -0
- package/src/atoms/breadcrumb/breadcrumb.md +233 -0
- package/src/atoms/breadcrumb/breadcrumb.styles.ts +40 -0
- package/src/atoms/breadcrumb/breadcrumb.tsx +130 -0
- package/src/atoms/button/button.android.tsx +6 -0
- package/src/atoms/button/button.ios.tsx +6 -0
- package/src/atoms/button/button.md +184 -0
- package/src/atoms/button/button.shared.tsx +79 -0
- package/src/atoms/button/button.styles.ts +152 -0
- package/src/atoms/button/button.tsx +6 -0
- package/src/atoms/button-group/button-group.android.tsx +6 -0
- package/src/atoms/button-group/button-group.ios.tsx +6 -0
- package/src/atoms/button-group/button-group.md +120 -0
- package/src/atoms/button-group/button-group.shared.tsx +398 -0
- package/src/atoms/button-group/button-group.styles.ts +483 -0
- package/src/atoms/button-group/button-group.tsx +6 -0
- package/src/atoms/checkbox/checkbox.android.tsx +6 -0
- package/src/atoms/checkbox/checkbox.ios.tsx +6 -0
- package/src/atoms/checkbox/checkbox.md +150 -0
- package/src/atoms/checkbox/checkbox.shared.tsx +103 -0
- package/src/atoms/checkbox/checkbox.styles.ts +106 -0
- package/src/atoms/checkbox/checkbox.tsx +6 -0
- package/src/atoms/combobox/combobox.android.tsx +6 -0
- package/src/atoms/combobox/combobox.ios.tsx +6 -0
- package/src/atoms/combobox/combobox.md +213 -0
- package/src/atoms/combobox/combobox.shared.tsx +160 -0
- package/src/atoms/combobox/combobox.styles.ts +270 -0
- package/src/atoms/combobox/combobox.tsx +6 -0
- package/src/atoms/divider/divider.md +140 -0
- package/src/atoms/divider/divider.styles.ts +35 -0
- package/src/atoms/divider/divider.tsx +67 -0
- package/src/atoms/dropdown/dropdown.android.tsx +6 -0
- package/src/atoms/dropdown/dropdown.ios.tsx +6 -0
- package/src/atoms/dropdown/dropdown.md +221 -0
- package/src/atoms/dropdown/dropdown.shared.tsx +190 -0
- package/src/atoms/dropdown/dropdown.styles.ts +233 -0
- package/src/atoms/dropdown/dropdown.tsx +6 -0
- package/src/atoms/icon/icon.md +131 -0
- package/src/atoms/icon/icon.styles.ts +30 -0
- package/src/atoms/icon/icon.tsx +328 -0
- package/src/atoms/index.ts +24 -0
- package/src/atoms/input/input.android.tsx +6 -0
- package/src/atoms/input/input.ios.tsx +6 -0
- package/src/atoms/input/input.md +118 -0
- package/src/atoms/input/input.shared.tsx +203 -0
- package/src/atoms/input/input.styles.ts +286 -0
- package/src/atoms/input/input.tsx +6 -0
- package/src/atoms/kbd/kbd.md +91 -0
- package/src/atoms/kbd/kbd.styles.ts +33 -0
- package/src/atoms/kbd/kbd.tsx +27 -0
- package/src/atoms/listbox/listbox.md +177 -0
- package/src/atoms/listbox/listbox.styles.ts +60 -0
- package/src/atoms/listbox/listbox.tsx +113 -0
- package/src/atoms/pagination/pagination.android.tsx +6 -0
- package/src/atoms/pagination/pagination.ios.tsx +6 -0
- package/src/atoms/pagination/pagination.md +133 -0
- package/src/atoms/pagination/pagination.shared.tsx +289 -0
- package/src/atoms/pagination/pagination.styles.ts +245 -0
- package/src/atoms/pagination/pagination.tsx +6 -0
- package/src/atoms/popover/popover.android.tsx +8 -0
- package/src/atoms/popover/popover.ios.tsx +6 -0
- package/src/atoms/popover/popover.md +87 -0
- package/src/atoms/popover/popover.shared.tsx +124 -0
- package/src/atoms/popover/popover.styles.ts +144 -0
- package/src/atoms/popover/popover.tsx +6 -0
- package/src/atoms/radio/radio.android.tsx +6 -0
- package/src/atoms/radio/radio.ios.tsx +6 -0
- package/src/atoms/radio/radio.md +173 -0
- package/src/atoms/radio/radio.shared.tsx +98 -0
- package/src/atoms/radio/radio.styles.ts +109 -0
- package/src/atoms/radio/radio.tsx +6 -0
- package/src/atoms/select/select.android.tsx +6 -0
- package/src/atoms/select/select.ios.tsx +6 -0
- package/src/atoms/select/select.md +156 -0
- package/src/atoms/select/select.shared.tsx +143 -0
- package/src/atoms/select/select.styles.ts +310 -0
- package/src/atoms/select/select.tsx +6 -0
- package/src/atoms/skeleton/skeleton.md +135 -0
- package/src/atoms/skeleton/skeleton.styles.ts +117 -0
- package/src/atoms/skeleton/skeleton.tsx +145 -0
- package/src/atoms/spinner/spinner.android.tsx +7 -0
- package/src/atoms/spinner/spinner.ios.tsx +7 -0
- package/src/atoms/spinner/spinner.md +94 -0
- package/src/atoms/spinner/spinner.shared.tsx +92 -0
- package/src/atoms/spinner/spinner.styles.tsx +115 -0
- package/src/atoms/spinner/spinner.tsx +7 -0
- package/src/atoms/switch/switch.android.tsx +6 -0
- package/src/atoms/switch/switch.ios.tsx +6 -0
- package/src/atoms/switch/switch.md +91 -0
- package/src/atoms/switch/switch.shared.tsx +97 -0
- package/src/atoms/switch/switch.styles.ts +79 -0
- package/src/atoms/switch/switch.tsx +6 -0
- package/src/atoms/textarea/textarea.android.tsx +6 -0
- package/src/atoms/textarea/textarea.ios.tsx +6 -0
- package/src/atoms/textarea/textarea.md +140 -0
- package/src/atoms/textarea/textarea.shared.tsx +74 -0
- package/src/atoms/textarea/textarea.styles.ts +116 -0
- package/src/atoms/textarea/textarea.tsx +6 -0
- package/src/atoms/tooltip/tooltip.android.tsx +6 -0
- package/src/atoms/tooltip/tooltip.ios.tsx +7 -0
- package/src/atoms/tooltip/tooltip.md +122 -0
- package/src/atoms/tooltip/tooltip.shared.tsx +113 -0
- package/src/atoms/tooltip/tooltip.styles.ts +113 -0
- package/src/atoms/tooltip/tooltip.tsx +6 -0
- package/src/atoms/typography/typography.md +330 -0
- package/src/atoms/typography/typography.styles.ts +95 -0
- package/src/atoms/typography/typography.tsx +76 -0
- package/src/index.ts +12 -2
- package/src/molecules/action-panels/action-panels.md +133 -0
- package/src/molecules/action-panels/action-panels.styles.ts +39 -0
- package/src/molecules/action-panels/action-panels.tsx +113 -0
- package/src/molecules/alert/alert.md +119 -0
- package/src/molecules/alert/alert.styles.ts +88 -0
- package/src/molecules/alert/alert.tsx +74 -0
- package/src/molecules/alert-dialog/alert-dialog.android.tsx +6 -0
- package/src/molecules/alert-dialog/alert-dialog.ios.tsx +6 -0
- package/src/molecules/alert-dialog/alert-dialog.md +177 -0
- package/src/molecules/alert-dialog/alert-dialog.shared.tsx +187 -0
- package/src/molecules/alert-dialog/alert-dialog.styles.ts +248 -0
- package/src/molecules/alert-dialog/alert-dialog.tsx +6 -0
- package/src/molecules/card/card.md +190 -0
- package/src/molecules/card/card.styles.ts +67 -0
- package/src/molecules/card/card.tsx +176 -0
- package/src/molecules/code-block/code-block.md +159 -0
- package/src/molecules/code-block/code-block.styles.ts +167 -0
- package/src/molecules/code-block/code-block.tsx +176 -0
- package/src/molecules/description-lists/description-lists.md +129 -0
- package/src/molecules/description-lists/description-lists.styles.ts +102 -0
- package/src/molecules/description-lists/description-lists.tsx +133 -0
- package/src/molecules/empty-state/empty-state.md +218 -0
- package/src/molecules/empty-state/empty-state.styles.ts +63 -0
- package/src/molecules/empty-state/empty-state.tsx +77 -0
- package/src/molecules/feeds/feeds.md +102 -0
- package/src/molecules/feeds/feeds.styles.ts +120 -0
- package/src/molecules/feeds/feeds.tsx +167 -0
- package/src/molecules/field/field.md +117 -0
- package/src/molecules/field/field.styles.ts +85 -0
- package/src/molecules/field/field.tsx +175 -0
- package/src/molecules/fieldset/fieldset.md +141 -0
- package/src/molecules/fieldset/fieldset.styles.ts +79 -0
- package/src/molecules/fieldset/fieldset.tsx +182 -0
- package/src/molecules/form/form.md +137 -0
- package/src/molecules/form/form.styles.ts +39 -0
- package/src/molecules/form/form.tsx +246 -0
- package/src/molecules/grid-lists/grid-lists.md +114 -0
- package/src/molecules/grid-lists/grid-lists.styles.ts +79 -0
- package/src/molecules/grid-lists/grid-lists.tsx +157 -0
- package/src/molecules/index.ts +16 -0
- package/src/molecules/media-objects/media-objects.md +87 -0
- package/src/molecules/media-objects/media-objects.styles.ts +94 -0
- package/src/molecules/media-objects/media-objects.tsx +128 -0
- package/src/molecules/stacked-lists/stacked-lists.md +116 -0
- package/src/molecules/stacked-lists/stacked-lists.styles.ts +111 -0
- package/src/molecules/stacked-lists/stacked-lists.tsx +195 -0
- package/src/molecules/stats/stats.md +166 -0
- package/src/molecules/stats/stats.styles.ts +91 -0
- package/src/molecules/stats/stats.tsx +88 -0
- package/src/organisms/calendar/calendar.android.tsx +6 -0
- package/src/organisms/calendar/calendar.ios.tsx +6 -0
- package/src/organisms/calendar/calendar.md +114 -0
- package/src/organisms/calendar/calendar.shared.tsx +146 -0
- package/src/organisms/calendar/calendar.styles.ts +315 -0
- package/src/organisms/calendar/calendar.tsx +6 -0
- package/src/organisms/charts/charts.md +326 -0
- package/src/organisms/charts/charts.styles.ts +135 -0
- package/src/organisms/charts/charts.tsx +124 -0
- package/src/organisms/command/command.md +117 -0
- package/src/organisms/command/command.styles.ts +179 -0
- package/src/organisms/command/command.tsx +164 -0
- package/src/organisms/data-table/data-table.md +182 -0
- package/src/organisms/data-table/data-table.styles.ts +103 -0
- package/src/organisms/data-table/data-table.tsx +105 -0
- package/src/organisms/dialog/dialog.android.tsx +6 -0
- package/src/organisms/dialog/dialog.ios.tsx +6 -0
- package/src/organisms/dialog/dialog.md +271 -0
- package/src/organisms/dialog/dialog.shared.tsx +230 -0
- package/src/organisms/dialog/dialog.styles.ts +272 -0
- package/src/organisms/dialog/dialog.tsx +6 -0
- package/src/organisms/filter-panel/filter-panel.md +116 -0
- package/src/organisms/filter-panel/filter-panel.styles.ts +83 -0
- package/src/organisms/filter-panel/filter-panel.tsx +91 -0
- package/src/organisms/index.ts +13 -0
- package/src/organisms/navbars/navbars.android.tsx +6 -0
- package/src/organisms/navbars/navbars.ios.tsx +6 -0
- package/src/organisms/navbars/navbars.md +144 -0
- package/src/organisms/navbars/navbars.shared.tsx +137 -0
- package/src/organisms/navbars/navbars.styles.ts +251 -0
- package/src/organisms/navbars/navbars.tsx +6 -0
- package/src/organisms/overlays/overlays.android.tsx +6 -0
- package/src/organisms/overlays/overlays.ios.tsx +6 -0
- package/src/organisms/overlays/overlays.md +123 -0
- package/src/organisms/overlays/overlays.shared.tsx +175 -0
- package/src/organisms/overlays/overlays.styles.ts +309 -0
- package/src/organisms/overlays/overlays.tsx +6 -0
- package/src/organisms/row-menu/row-menu.android.tsx +6 -0
- package/src/organisms/row-menu/row-menu.ios.tsx +6 -0
- package/src/organisms/row-menu/row-menu.md +102 -0
- package/src/organisms/row-menu/row-menu.shared.tsx +105 -0
- package/src/organisms/row-menu/row-menu.styles.ts +262 -0
- package/src/organisms/row-menu/row-menu.tsx +6 -0
- package/src/organisms/sidebar/sidebar.android.tsx +6 -0
- package/src/organisms/sidebar/sidebar.ios.tsx +6 -0
- package/src/organisms/sidebar/sidebar.md +188 -0
- package/src/organisms/sidebar/sidebar.shared.tsx +167 -0
- package/src/organisms/sidebar/sidebar.styles.ts +262 -0
- package/src/organisms/sidebar/sidebar.tsx +6 -0
- package/src/organisms/stepper/stepper.android.tsx +6 -0
- package/src/organisms/stepper/stepper.ios.tsx +6 -0
- package/src/organisms/stepper/stepper.md +150 -0
- package/src/organisms/stepper/stepper.shared.tsx +158 -0
- package/src/organisms/stepper/stepper.styles.ts +280 -0
- package/src/organisms/stepper/stepper.tsx +6 -0
- package/src/organisms/tabs/tabs.android.tsx +6 -0
- package/src/organisms/tabs/tabs.ios.tsx +6 -0
- package/src/organisms/tabs/tabs.md +127 -0
- package/src/organisms/tabs/tabs.shared.tsx +281 -0
- package/src/organisms/tabs/tabs.styles.ts +398 -0
- package/src/organisms/tabs/tabs.tsx +6 -0
- package/src/style/color.ts +17 -0
- package/src/style/index.ts +14 -0
- package/src/style/primitives.ts +26 -0
- package/src/style/responsive.ts +45 -0
- package/src/style/shadow.ts +21 -0
- package/src/style/theme.tsx +56 -0
- package/src/style/tokens.ts +487 -0
- package/styles/canvas.css +127 -74
- package/tsconfig.json +4 -2
- package/src/cn.ts +0 -3
- package/styles/atoms/avatar.css +0 -22
- package/styles/atoms/badge.css +0 -83
- package/styles/atoms/breadcrumb.css +0 -35
- package/styles/atoms/button-group.css +0 -23
- package/styles/atoms/button.css +0 -107
- package/styles/atoms/checkbox.css +0 -55
- package/styles/atoms/combobox.css +0 -76
- package/styles/atoms/dropdown.css +0 -54
- package/styles/atoms/icon.css +0 -8
- package/styles/atoms/input-group.css +0 -45
- package/styles/atoms/input.css +0 -56
- package/styles/atoms/kbd.css +0 -15
- package/styles/atoms/pagination.css +0 -48
- package/styles/atoms/popover.css +0 -14
- package/styles/atoms/radio.css +0 -28
- package/styles/atoms/select.css +0 -57
- package/styles/atoms/separator.css +0 -32
- package/styles/atoms/skeleton.css +0 -32
- package/styles/atoms/spinner.css +0 -26
- package/styles/atoms/switch.css +0 -45
- package/styles/atoms/textarea.css +0 -31
- package/styles/atoms/tooltip.css +0 -53
- package/styles/atoms/typography.css +0 -105
- package/styles/base.css +0 -17
- package/styles/molecules/alert.css +0 -66
- package/styles/molecules/card.css +0 -58
- package/styles/molecules/code-block.css +0 -18
- package/styles/molecules/empty-state.css +0 -17
- package/styles/molecules/field.css +0 -27
- package/styles/molecules/form.css +0 -27
- package/styles/molecules/page-header.css +0 -52
- package/styles/molecules/section-card.css +0 -49
- package/styles/molecules/stat-card.css +0 -71
- package/styles/molecules/toast.css +0 -95
- package/styles/organisms/app-shell.css +0 -46
- package/styles/organisms/calendar.css +0 -73
- package/styles/organisms/command.css +0 -95
- package/styles/organisms/data-table.css +0 -142
- package/styles/organisms/dialog.css +0 -72
- package/styles/organisms/filter-panel.css +0 -58
- package/styles/organisms/row-menu.css +0 -69
- package/styles/organisms/sheet.css +0 -70
- package/styles/organisms/sidebar.css +0 -146
- package/styles/organisms/stepper.css +0 -63
- package/styles/organisms/tabs.css +0 -40
- package/styles/organisms/topbar.css +0 -24
- package/styles/patterns/backdrops.css +0 -35
- package/styles/patterns/density.css +0 -66
- package/styles/patterns/focus.css +0 -22
- package/styles/patterns/glass.css +0 -85
- package/styles/patterns/high-contrast.css +0 -70
- package/styles/patterns/reduced-motion.css +0 -12
- package/styles/patterns/scrollbar.css +0 -10
- package/styles/reset.css +0 -89
- package/styles/tokens/colors.css +0 -108
- package/styles/tokens/motion.css +0 -33
- package/styles/tokens/radius.css +0 -10
- package/styles/tokens/shadows.css +0 -35
- package/styles/tokens/spacing.css +0 -19
- package/styles/tokens/typography.css +0 -6
- package/styles/tokens/z-index.css +0 -12
- package/styles/utilities/display.css +0 -66
- package/styles/utilities/flexbox.css +0 -240
- package/styles/utilities/gap.css +0 -288
- package/styles/utilities/grid.css +0 -138
- package/styles/utilities/position.css +0 -78
- package/styles/utilities/sizing.css +0 -138
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Typography
|
|
2
|
+
|
|
3
|
+
Type scale classes for headings, body text, and helper styles.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Typography h1>The quick brown fox</Typography>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
### Style - display
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Typography display>The quick brown fox</Typography>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Style - h2
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<Typography h2>The quick brown fox</Typography>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Style - h3
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<Typography h3>The quick brown fox</Typography>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Style - h4
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Typography h4>The quick brown fox</Typography>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Style - h5
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<Typography h5>The quick brown fox</Typography>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Style - body
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Typography body>The quick brown fox</Typography>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Style - small
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Typography small>The quick brown fox</Typography>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Style - tiny
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Typography tiny>The quick brown fox</Typography>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Style - muted
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<Typography muted>The quick brown fox</Typography>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Style - caption
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
<Typography caption>The quick brown fox</Typography>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Style - code
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<Typography code>The quick brown fox</Typography>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Style - mono
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<Typography mono>The quick brown fox</Typography>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Do & Don't
|
|
86
|
+
|
|
87
|
+
### display
|
|
88
|
+
|
|
89
|
+
**Do** — Use display once per hero, then drop to a muted line for the supporting copy.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<View>
|
|
93
|
+
<Typography display>Welcome</Typography>
|
|
94
|
+
<Typography muted style={{ marginTop: 8 }}>Sign in to pick up where you left off.</Typography>
|
|
95
|
+
</View>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Don't** — Two display-size lines in one view fight for attention and leave no clear focal point.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<View style={{ gap: 8 }}>
|
|
102
|
+
<Typography display>Welcome</Typography>
|
|
103
|
+
<Typography display>Get started</Typography>
|
|
104
|
+
</View>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### h1
|
|
108
|
+
|
|
109
|
+
**Do** — Give each page a single h1, then step down to h2 for the sections beneath it.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<View>
|
|
113
|
+
<Typography h1>Billing</Typography>
|
|
114
|
+
<Typography h2 style={{ marginTop: 16 }}>Invoices</Typography>
|
|
115
|
+
</View>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Don't** — Two h1 titles on a page break the document outline and confuse assistive tech.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<View style={{ gap: 4 }}>
|
|
122
|
+
<Typography h1>Billing</Typography>
|
|
123
|
+
<Typography h1>Invoices</Typography>
|
|
124
|
+
</View>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### h2
|
|
128
|
+
|
|
129
|
+
**Do** — Follow an h1 with h2 for its top-level sections; don't skip the scale.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<View>
|
|
133
|
+
<Typography h1>Settings</Typography>
|
|
134
|
+
<Typography h2 style={{ marginTop: 16 }}>Profile</Typography>
|
|
135
|
+
</View>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Don't** — Jumping from h1 straight to h4 skips a level and flattens the visible hierarchy.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
<View>
|
|
142
|
+
<Typography h1>Settings</Typography>
|
|
143
|
+
<Typography h4 style={{ marginTop: 16 }}>Profile</Typography>
|
|
144
|
+
</View>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### h3
|
|
148
|
+
|
|
149
|
+
**Do** — Reserve heading styles for titles; set running text in a small body utility.
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<View style={{ maxWidth: 340 }}>
|
|
153
|
+
<Typography h3>About Canvas</Typography>
|
|
154
|
+
<Typography body style={{ marginTop: 4 }}>Canvas is a universal React Native UI kit for building consistent product interfaces.</Typography>
|
|
155
|
+
</View>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Don't** — Body copy set in a heading style is hard to read in bulk and flattens the hierarchy.
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<Typography h3 style={{ maxWidth: 340 }}>Canvas is a universal React Native UI kit for building consistent product interfaces.</Typography>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### h4
|
|
165
|
+
|
|
166
|
+
**Do** — Keep h4 to a short label and carry the explanation in a small supporting line.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<View>
|
|
170
|
+
<Typography h4>Notifications</Typography>
|
|
171
|
+
<Typography small style={{ marginTop: 4 }}>Choose how and when we reach you.</Typography>
|
|
172
|
+
</View>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Don't** — h4 is a minor heading, not a place for full sentences; long text at this weight reads as a wall.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
<View style={{ gap: 4 }}>
|
|
179
|
+
<Typography h4>Notifications</Typography>
|
|
180
|
+
<Typography h4>A long descriptive sentence that explains everything in detail.</Typography>
|
|
181
|
+
</View>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### h5
|
|
185
|
+
|
|
186
|
+
**Do** — Use h5 only for the label; render the value in body so the pair stays scannable.
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
<View>
|
|
190
|
+
<Typography h5>Members</Typography>
|
|
191
|
+
<Typography body style={{ marginTop: 2 }}>Aisha, Bao, Cleo, and 9 others have access.</Typography>
|
|
192
|
+
</View>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Don't** — Setting the value in h5 too makes the label and its data indistinguishable.
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
<View style={{ gap: 4 }}>
|
|
199
|
+
<Typography h5>Members</Typography>
|
|
200
|
+
<Typography h5>Aisha, Bao, Cleo, and 9 others have access.</Typography>
|
|
201
|
+
</View>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### body
|
|
205
|
+
|
|
206
|
+
**Do** — Keep body copy in sentence case and let inline code carry the technical emphasis.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<Typography body style={{ maxWidth: 340 }}>
|
|
210
|
+
Run
|
|
211
|
+
<Typography code>npm install</Typography>
|
|
212
|
+
, then restart the dev server before you continue.
|
|
213
|
+
</Typography>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Don't** — All-caps emphasis inside body copy shouts and undercuts the relaxed reading rhythm.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
<Typography body style={{ maxWidth: 340 }}>
|
|
220
|
+
<Typography code>npm install</Typography>
|
|
221
|
+
THEN restart the dev server BEFORE you continue.
|
|
222
|
+
</Typography>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### small
|
|
226
|
+
|
|
227
|
+
**Do** — Use small for secondary captions on a plain surface, not for the primary label.
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
<View>
|
|
231
|
+
<Typography body>Save changes</Typography>
|
|
232
|
+
<Typography small style={{ marginTop: 2 }}>Last saved 2 minutes ago.</Typography>
|
|
233
|
+
</View>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Don't** — small is muted-foreground; on a colored button it loses contrast and looks disabled.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
<Button secondary>
|
|
240
|
+
<Typography small>Save changes</Typography>
|
|
241
|
+
</Button>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### tiny
|
|
245
|
+
|
|
246
|
+
**Do** — Reserve tiny for short metadata like timestamps and counts beside the main text.
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
250
|
+
<Typography body>Deploy succeeded</Typography>
|
|
251
|
+
<Typography tiny>3m ago</Typography>
|
|
252
|
+
</View>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Don't** — tiny is for metadata, not legal prose; long copy at 12px strains the eye.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<Typography tiny style={{ maxWidth: 300 }}>These terms govern your use of the service and your data; please read them carefully before you continue past this screen.</Typography>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### muted
|
|
262
|
+
|
|
263
|
+
**Do** — Keep muted for de-emphasized context; give the actual action full foreground or primary color.
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
<Typography body style={{ maxWidth: 340 }}>
|
|
267
|
+
Payment due May 31.
|
|
268
|
+
<Typography body style={{ color: tokens.primary, textDecorationLine: "underline" }}>View invoices</Typography>
|
|
269
|
+
</Typography>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Don't** — A primary, clickable action in muted-foreground reads as disabled and is easy to miss.
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
<Typography muted style={{ alignSelf: "flex-start", textDecorationLine: "underline" }}>View your invoices</Typography>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### caption
|
|
279
|
+
|
|
280
|
+
**Do** — Use caption as a short eyebrow label above a section, then explain in body.
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
<View>
|
|
284
|
+
<Typography caption>Billing</Typography>
|
|
285
|
+
<Typography body style={{ marginTop: 4 }}>Your subscription renews automatically each month.</Typography>
|
|
286
|
+
</View>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Don't** — Uppercase, letter-spaced caption text is illegible for anything longer than a label.
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
<Typography caption style={{ maxWidth: 320 }}>Your subscription renews automatically each month unless you cancel from the billing page.</Typography>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### code
|
|
296
|
+
|
|
297
|
+
**Do** — Use code for inline tokens inside a sentence; reach for the code block component for multi-line snippets.
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
<Typography body>
|
|
301
|
+
Create a branch with
|
|
302
|
+
<Typography code>git checkout -b feature</Typography>
|
|
303
|
+
before committing.
|
|
304
|
+
</Typography>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Don't** — The inline code utility has tight padding and no scroll; multi-line blocks overflow and clip.
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
<Typography code>git checkout -b feature
|
|
311
|
+
git add .
|
|
312
|
+
git commit -m "wip"</Typography>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### mono
|
|
316
|
+
|
|
317
|
+
**Do** — Use mono for identifiers, hashes, and tabular values where character alignment matters.
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 16 }}>
|
|
321
|
+
<Typography small>Request ID</Typography>
|
|
322
|
+
<Typography mono>req_8f2c10ab</Typography>
|
|
323
|
+
</View>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Don't** — Mono spacing makes prose sentences sparse and slow to read; it is meant for fixed-width data.
|
|
327
|
+
|
|
328
|
+
```tsx
|
|
329
|
+
<Typography mono style={{ maxWidth: 320 }}>We could not process your request because the upstream service returned an unexpected response.</Typography>
|
|
330
|
+
```
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located Typography styles. One axis (role), each role mapping to a single
|
|
5
|
+
// TextStyle built from the active brand tokens (so the type color follows
|
|
6
|
+
// light/dark and the glass surface). The component resolves the active role by
|
|
7
|
+
// first-match precedence and spreads the matching fragment.
|
|
8
|
+
//
|
|
9
|
+
// Every fragment is a TextStyle: Typography renders a single Text, so layout +
|
|
10
|
+
// type + color all live on the Text. The `code` role additionally carries the
|
|
11
|
+
// muted fill and pill padding (a Text can hold backgroundColor/padding in RN).
|
|
12
|
+
|
|
13
|
+
export type Role =
|
|
14
|
+
| "display"
|
|
15
|
+
| "h1"
|
|
16
|
+
| "h2"
|
|
17
|
+
| "h3"
|
|
18
|
+
| "h4"
|
|
19
|
+
| "h5"
|
|
20
|
+
| "body"
|
|
21
|
+
| "small"
|
|
22
|
+
| "tiny"
|
|
23
|
+
| "muted"
|
|
24
|
+
| "caption"
|
|
25
|
+
| "code"
|
|
26
|
+
| "mono";
|
|
27
|
+
|
|
28
|
+
// Type + layout per role, color-free (the parts that don't read a token).
|
|
29
|
+
// Mirrors the docs' typeScale: heading sizes get the tight tracking, body gets
|
|
30
|
+
// the relaxed line height, caption gets uppercase + wide tracking, and `code`
|
|
31
|
+
// carries the self-start pill box (radius + padding).
|
|
32
|
+
export const roleType: Record<Role, TextStyle> = {
|
|
33
|
+
// text-5xl font-bold tracking-tight
|
|
34
|
+
display: { fontSize: 48, lineHeight: 48, fontWeight: "700", letterSpacing: -0.4 },
|
|
35
|
+
// text-4xl font-bold tracking-tight
|
|
36
|
+
h1: { fontSize: 36, lineHeight: 40, fontWeight: "700", letterSpacing: -0.4 },
|
|
37
|
+
// text-3xl font-semibold tracking-tight
|
|
38
|
+
h2: { fontSize: 30, lineHeight: 36, fontWeight: "600", letterSpacing: -0.4 },
|
|
39
|
+
// text-2xl font-semibold tracking-tight
|
|
40
|
+
h3: { fontSize: 24, lineHeight: 32, fontWeight: "600", letterSpacing: -0.4 },
|
|
41
|
+
// text-xl font-semibold tracking-tight
|
|
42
|
+
h4: { fontSize: 20, lineHeight: 28, fontWeight: "600", letterSpacing: -0.4 },
|
|
43
|
+
// text-lg font-semibold
|
|
44
|
+
h5: { fontSize: 18, lineHeight: 28, fontWeight: "600" },
|
|
45
|
+
// text-sm leading-relaxed (the relaxed line height overrides text-sm's 20)
|
|
46
|
+
body: { fontSize: 14, lineHeight: 28 },
|
|
47
|
+
// text-sm
|
|
48
|
+
small: { fontSize: 14, lineHeight: 20 },
|
|
49
|
+
// text-xs
|
|
50
|
+
tiny: { fontSize: 12, lineHeight: 16 },
|
|
51
|
+
// text-sm
|
|
52
|
+
muted: { fontSize: 14, lineHeight: 20 },
|
|
53
|
+
// text-xs uppercase tracking-wide
|
|
54
|
+
caption: { fontSize: 12, lineHeight: 16, textTransform: "uppercase", letterSpacing: 0.4 },
|
|
55
|
+
// self-start rounded bg-muted px-1.5 py-0.5 text-sm (fill added in roleColor)
|
|
56
|
+
code: {
|
|
57
|
+
alignSelf: "flex-start",
|
|
58
|
+
borderRadius: 4,
|
|
59
|
+
paddingHorizontal: 6,
|
|
60
|
+
paddingVertical: 2,
|
|
61
|
+
fontSize: 14,
|
|
62
|
+
lineHeight: 20,
|
|
63
|
+
},
|
|
64
|
+
// text-sm
|
|
65
|
+
mono: { fontSize: 14, lineHeight: 20 },
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Text color (and the `code` role's muted fill) per role. The docs relied on
|
|
69
|
+
// inherited page color; RN text does not cascade, so every role names its color
|
|
70
|
+
// token explicitly: headings/body/code/mono on `foreground`, the helper styles
|
|
71
|
+
// on `muted-foreground`.
|
|
72
|
+
export function roleColor(tokens: ColorTokens, role: Role): TextStyle {
|
|
73
|
+
switch (role) {
|
|
74
|
+
case "display":
|
|
75
|
+
case "h1":
|
|
76
|
+
case "h2":
|
|
77
|
+
case "h3":
|
|
78
|
+
case "h4":
|
|
79
|
+
case "h5":
|
|
80
|
+
case "body":
|
|
81
|
+
case "mono":
|
|
82
|
+
return { color: tokens.foreground };
|
|
83
|
+
case "code":
|
|
84
|
+
return { color: tokens.foreground, backgroundColor: tokens.muted };
|
|
85
|
+
case "small":
|
|
86
|
+
case "tiny":
|
|
87
|
+
case "muted":
|
|
88
|
+
case "caption":
|
|
89
|
+
return { color: tokens["muted-foreground"] };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Roles whose face is monospace; RN has no font-family utility, so the component
|
|
94
|
+
// supplies the cross-platform monospace alias inline (matches Badge's mono).
|
|
95
|
+
export const MONO_ROLES = new Set<Role>(["code", "mono"]);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { Text, useTheme, type StyleProp, type TextStyle } from "../../style/index.js";
|
|
3
|
+
import { type Role, roleType, roleColor, MONO_ROLES } from "./typography.styles.js";
|
|
4
|
+
|
|
5
|
+
// Typography: the Canvas type scale as a single styled Text. One boolean role
|
|
6
|
+
// prop per style (display / h1..h5 / body / small / tiny / muted / caption /
|
|
7
|
+
// code / mono) selects a token-backed style set; omit all for the plain body
|
|
8
|
+
// look. The foundation's `Text` primitive is the host element, so the public
|
|
9
|
+
// component is named `Typography` (the bare `Text` name belongs to the
|
|
10
|
+
// foundation).
|
|
11
|
+
//
|
|
12
|
+
// Boolean-prop API: one boolean per role on a single axis, first-match
|
|
13
|
+
// precedence (mirrors Button's intentOf / Badge's toneOf). Roles are mutually
|
|
14
|
+
// exclusive; pass at most one. The text content comes from children.
|
|
15
|
+
//
|
|
16
|
+
// Two roles want a monospace face (code, mono). There is no font-family
|
|
17
|
+
// utility, so each requests RN's cross-platform monospace alias via inline
|
|
18
|
+
// style, the same pattern Badge's `mono` modifier uses. The docs set `code` at
|
|
19
|
+
// text-[13px]; the type scale resolves only token sizes, so it falls back to the
|
|
20
|
+
// nearest one (text-sm), as Kbd does for its 11px label.
|
|
21
|
+
|
|
22
|
+
export interface TypographyProps {
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
// Role (pick one; default is the plain body style). First-match precedence.
|
|
25
|
+
display?: boolean;
|
|
26
|
+
h1?: boolean;
|
|
27
|
+
h2?: boolean;
|
|
28
|
+
h3?: boolean;
|
|
29
|
+
h4?: boolean;
|
|
30
|
+
h5?: boolean;
|
|
31
|
+
body?: boolean;
|
|
32
|
+
small?: boolean;
|
|
33
|
+
tiny?: boolean;
|
|
34
|
+
muted?: boolean;
|
|
35
|
+
caption?: boolean;
|
|
36
|
+
code?: boolean;
|
|
37
|
+
mono?: boolean;
|
|
38
|
+
/** Escape hatch for layout/positioning composition (margins, alignment). */
|
|
39
|
+
style?: StyleProp<TextStyle>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Role precedence when more than one is passed: first match wins. Order runs
|
|
43
|
+
// largest-to-smallest, headings before helper styles, so a conflicting pair
|
|
44
|
+
// resolves to the more prominent role.
|
|
45
|
+
function roleOf(p: TypographyProps): Role {
|
|
46
|
+
if (p.display) return "display";
|
|
47
|
+
if (p.h1) return "h1";
|
|
48
|
+
if (p.h2) return "h2";
|
|
49
|
+
if (p.h3) return "h3";
|
|
50
|
+
if (p.h4) return "h4";
|
|
51
|
+
if (p.h5) return "h5";
|
|
52
|
+
if (p.code) return "code";
|
|
53
|
+
if (p.mono) return "mono";
|
|
54
|
+
if (p.caption) return "caption";
|
|
55
|
+
if (p.muted) return "muted";
|
|
56
|
+
if (p.small) return "small";
|
|
57
|
+
if (p.tiny) return "tiny";
|
|
58
|
+
if (p.body) return "body";
|
|
59
|
+
return "body";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function Typography(props: TypographyProps) {
|
|
63
|
+
const { children, style } = props;
|
|
64
|
+
const { tokens } = useTheme();
|
|
65
|
+
const role = roleOf(props);
|
|
66
|
+
|
|
67
|
+
// The mono/code roles ask for a monospace face; there is no font-family
|
|
68
|
+
// utility, so request the cross-platform monospace alias via inline style.
|
|
69
|
+
const monoStyle = MONO_ROLES.has(role) ? { fontFamily: "monospace" as const } : null;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<Text style={[roleType[role], roleColor(tokens, role), monoStyle, style]}>
|
|
73
|
+
{children}
|
|
74
|
+
</Text>
|
|
75
|
+
);
|
|
76
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { cn } from "./cn.js";
|
|
2
1
|
export { token, hsl } from "./tokens.js";
|
|
3
2
|
export {
|
|
4
3
|
getTheme,
|
|
@@ -10,4 +9,15 @@ export {
|
|
|
10
9
|
setDensity,
|
|
11
10
|
} from "./theme.js";
|
|
12
11
|
|
|
13
|
-
export type { Theme,
|
|
12
|
+
export type { Theme, Density } from "./theme.js";
|
|
13
|
+
|
|
14
|
+
// The style foundation: design tokens, the theme runtime (ThemeProvider/useTheme),
|
|
15
|
+
// desktop-first responsive helpers, the shadow/alpha helpers, and the raw React
|
|
16
|
+
// Native primitives (View/Text/Pressable/Image/TextInput/ScrollView). This also
|
|
17
|
+
// exports the Surface type (the glass/default theming switch).
|
|
18
|
+
export * from "./style/index.js";
|
|
19
|
+
|
|
20
|
+
// Components, grouped by atomic-design level (atoms / molecules / organisms).
|
|
21
|
+
export * from "./atoms/index.js";
|
|
22
|
+
export * from "./molecules/index.js";
|
|
23
|
+
export * from "./organisms/index.js";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Action Panels
|
|
2
|
+
|
|
3
|
+
Section card with headline, body text, and a primary action. Used to surface a single decision or call-to-action.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<ActionPanel
|
|
9
|
+
title="Delete this project"
|
|
10
|
+
description="Once you delete a project, there is no going back. Please be certain."
|
|
11
|
+
actionLabel="Delete project"
|
|
12
|
+
destructive
|
|
13
|
+
/>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Variants
|
|
17
|
+
|
|
18
|
+
### Variant - side-by-side
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<ActionPanel
|
|
22
|
+
title="Delete this project"
|
|
23
|
+
description="You have unsaved edits in this form. Leaving now will lose all progress."
|
|
24
|
+
actionLabel="Discard"
|
|
25
|
+
destructive
|
|
26
|
+
inline
|
|
27
|
+
/>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Variant - toggle
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<ActionPanel
|
|
34
|
+
title="Delete this project"
|
|
35
|
+
description="Add an extra layer of security to your account by requiring a verification code on login."
|
|
36
|
+
toggle
|
|
37
|
+
checked
|
|
38
|
+
/>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Do & Don't
|
|
42
|
+
|
|
43
|
+
### Simple
|
|
44
|
+
|
|
45
|
+
**Do** — Spell out the consequence above the button so the stakes are clear before the click.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<ActionPanel title="Delete this project" description="Once you delete a project, there is no going back. Please be certain." actionLabel="Delete project" destructive style={{ maxWidth: 420 }} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Don't** — A destructive action with no consequence copy invites accidental, irreversible clicks.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<ActionPanel title="Delete this project" actionLabel="Delete project" destructive style={{ maxWidth: 420 }} />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### With form inline
|
|
58
|
+
|
|
59
|
+
**Do** — Keep the input and its submit button on one row so the input + action reads as one step.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<Card padded style={{ maxWidth: 420, gap: 16 }}>
|
|
63
|
+
<View style={{ gap: 4 }}>
|
|
64
|
+
<Text style={{ fontSize: 15, fontWeight: "600", color: tokens["card-foreground"] }}>Subscribe to updates</Text>
|
|
65
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>We'll send you a weekly digest of what changed.</Text>
|
|
66
|
+
</View>
|
|
67
|
+
<View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
|
|
68
|
+
<Input placeholder="you@example.com" style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%" }} />
|
|
69
|
+
<Button primary>Subscribe</Button>
|
|
70
|
+
</View>
|
|
71
|
+
</Card>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Don't** — Stacking the field above its button breaks the single-decision rhythm and adds a row of dead space.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<Card padded style={{ maxWidth: 420, gap: 16 }}>
|
|
78
|
+
<View style={{ gap: 4 }}>
|
|
79
|
+
<Text style={{ fontSize: 15, fontWeight: "600", color: tokens["card-foreground"] }}>Subscribe to updates</Text>
|
|
80
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>We'll send you a weekly digest of what changed.</Text>
|
|
81
|
+
</View>
|
|
82
|
+
<Input placeholder="you@example.com" />
|
|
83
|
+
<View style={{ alignItems: "flex-start" }}>
|
|
84
|
+
<Button primary>Subscribe</Button>
|
|
85
|
+
</View>
|
|
86
|
+
</Card>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Side-by-side
|
|
90
|
+
|
|
91
|
+
**Do** — Style only the irreversible action as destructive; keep the cancel/escape as a quiet outline button.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<Card padded style={{ maxWidth: 460, flexDirection: "row", alignItems: "flex-start", gap: 24 }}>
|
|
95
|
+
<View style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", gap: 4 }}>
|
|
96
|
+
<Text style={{ fontSize: 15, fontWeight: "600", color: tokens["card-foreground"] }}>Discard unsaved changes?</Text>
|
|
97
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>You have unsaved edits in this form. Leaving now will lose all progress.</Text>
|
|
98
|
+
</View>
|
|
99
|
+
<View style={{ flexShrink: 0, flexDirection: "row", gap: 8 }}>
|
|
100
|
+
<Button outline small>Cancel</Button>
|
|
101
|
+
<Button destructive small>Discard</Button>
|
|
102
|
+
</View>
|
|
103
|
+
</Card>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Don't** — Two destructive-styled buttons make the safe escape (Cancel) look as dangerous as the discard.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<Card padded style={{ maxWidth: 460, flexDirection: "row", alignItems: "flex-start", gap: 24 }}>
|
|
110
|
+
<View style={{ flexGrow: 1, flexShrink: 1, flexBasis: "0%", gap: 4 }}>
|
|
111
|
+
<Text style={{ fontSize: 15, fontWeight: "600", color: tokens["card-foreground"] }}>Discard unsaved changes?</Text>
|
|
112
|
+
<Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>You have unsaved edits in this form. Leaving now will lose all progress.</Text>
|
|
113
|
+
</View>
|
|
114
|
+
<View style={{ flexShrink: 0, flexDirection: "row", gap: 8 }}>
|
|
115
|
+
<Button destructive small>Discard</Button>
|
|
116
|
+
<Button destructive small>Cancel</Button>
|
|
117
|
+
</View>
|
|
118
|
+
</Card>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### With toggle
|
|
122
|
+
|
|
123
|
+
**Do** — Pair the switch with a one-line explanation of what turning it on or off does.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<ActionPanel title="Two-factor authentication" description="Add an extra layer of security to your account by requiring a verification code on login." toggle checked style={{ maxWidth: 460 }} />
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Don't** — A bare switch with no description leaves the user guessing what flipping it actually changes.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
<ActionPanel title="Two-factor authentication" toggle checked style={{ maxWidth: 460 }} />
|
|
133
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ViewStyle, type TextStyle } from "react-native";
|
|
2
|
+
import { type ColorTokens, palette } from "../../style/index.js";
|
|
3
|
+
|
|
4
|
+
// Co-located ActionPanel styles. Layout-only fragments are static objects; the
|
|
5
|
+
// title color is a function of the active tokens (neutral) or the dark scheme
|
|
6
|
+
// (the destructive danger-zone red, which rides the Tailwind palette so it stays
|
|
7
|
+
// fixed per scheme rather than following the semantic tokens).
|
|
8
|
+
|
|
9
|
+
export type Tone = "destructive" | "neutral";
|
|
10
|
+
export type Layout = "inline" | "stacked";
|
|
11
|
+
|
|
12
|
+
// The card surface caps its width; passed to the Card child via its `style` prop.
|
|
13
|
+
export const cardWidth: ViewStyle = { maxWidth: 560 };
|
|
14
|
+
|
|
15
|
+
// The copy block: title stacked above its consequence line. In the inline layout
|
|
16
|
+
// it grows to push the action to the right (flex-1); stacked, it stays natural.
|
|
17
|
+
export const copyBlock: ViewStyle = { gap: 4 };
|
|
18
|
+
export const copyGrow: ViewStyle = { flexGrow: 1, flexShrink: 1, flexBasis: "0%" };
|
|
19
|
+
|
|
20
|
+
// The action wrapper: pinned (shrink-0) in the inline/toggle layouts, or
|
|
21
|
+
// left-aligned (items-start) on its own line in the stacked layout.
|
|
22
|
+
export const actionPinned: ViewStyle = { flexShrink: 0 };
|
|
23
|
+
export const actionStacked: ViewStyle = { alignItems: "flex-start" };
|
|
24
|
+
|
|
25
|
+
// The two body layouts: a side-by-side row (inline/toggle) or a vertical stack.
|
|
26
|
+
export const inlineBody: ViewStyle = { flexDirection: "row", alignItems: "flex-start", gap: 24 };
|
|
27
|
+
export const stackedBody: ViewStyle = { gap: 16 };
|
|
28
|
+
|
|
29
|
+
// Title and description share Canvas's settings-row type scale (text-sm).
|
|
30
|
+
export function titleText(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
|
|
31
|
+
// Danger-zone red rides the palette (fixed per scheme); neutral follows the
|
|
32
|
+
// semantic card foreground so it tracks light/dark/glass.
|
|
33
|
+
const color = tone === "destructive" ? (dark ? palette["red-400"] : palette["red-700"]) : tokens["card-foreground"];
|
|
34
|
+
return { fontSize: 14, lineHeight: 20, fontWeight: "600", color };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function descriptionText(tokens: ColorTokens): TextStyle {
|
|
38
|
+
return { fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] };
|
|
39
|
+
}
|