forked from
esb.lol/alf
Bluesky's "Application Layout Framework"
1import React from 'react'
2import {
3 Text as RNText,
4 View,
5 ScrollView,
6 type ViewStyle,
7 type TextStyle,
8 type StyleProp,
9} from 'react-native'
10
11import {
12 atoms as a,
13 utils,
14 useTheme,
15 Provider,
16 createThemes,
17 DEFAULT_PALETTE,
18 DEFAULT_SUBDUED_PALETTE,
19} from '@bsky.app/alf'
20
21export function Spec() {
22 const themes = createThemes({
23 defaultPalette: DEFAULT_PALETTE,
24 subduedPalette: DEFAULT_SUBDUED_PALETTE,
25 })
26
27 return (
28 <ScrollView style={[a.gutter_x_default, a.py_5xl]}>
29 <View style={[a.gap_5xl, {maxWidth: 600}]}>
30 <Text style={[a.text_5xl, a.font_bold]}>ALF Spec</Text>
31
32 {['light', 'dark', 'dim'].map(theme => {
33 return (
34 <Provider activeTheme={theme} themes={themes} key={theme}>
35 <Colors />
36 </Provider>
37 )
38 })}
39
40 <Section>
41 <SectionTitle>Typography</SectionTitle>
42 <View style={[a.gap_sm]}>
43 <Text style={[a.text_xs]}>a.text_xs</Text>
44 <Text style={[a.text_sm]}>a.text_sm</Text>
45 <Text style={[a.text_md]}>a.text_md</Text>
46 <Text style={[a.text_lg]}>a.text_lg</Text>
47 <Text style={[a.text_xl]}>a.text_xl</Text>
48 </View>
49 <View style={[a.gap_sm]}>
50 <Text style={[a.text_xs, a.leading_tight]}>
51 [a.text_xs a.leading_tight] {lorem}
52 </Text>
53 <Text style={[a.text_sm, a.leading_tight]}>
54 [a.text_sm a.leading_tight] {lorem}
55 </Text>
56 <Text style={[a.text_md, a.leading_tight]}>
57 [a.text_md a.leading_tight] {lorem}
58 </Text>
59 <Text style={[a.text_lg, a.leading_tight]}>
60 [a.text_lg a.leading_tight] {lorem}
61 </Text>
62 <Text style={[a.text_xl, a.leading_tight]}>
63 [a.text_xl a.leading_tight] {lorem}
64 </Text>
65 </View>
66 <View style={[a.gap_sm]}>
67 <Text style={[a.text_xs, a.leading_snug]}>
68 [a.text_xs a.leading_snug] {lorem}
69 </Text>
70 <Text style={[a.text_sm, a.leading_snug]}>
71 [a.text_sm a.leading_snug] {lorem}
72 </Text>
73 <Text style={[a.text_md, a.leading_snug]}>
74 [a.text_md a.leading_snug] {lorem}
75 </Text>
76 <Text style={[a.text_lg, a.leading_snug]}>
77 [a.text_lg a.leading_snug] {lorem}
78 </Text>
79 <Text style={[a.text_xl, a.leading_snug]}>
80 [a.text_xl a.leading_snug] {lorem}
81 </Text>
82 </View>
83 <View style={[a.gap_sm]}>
84 <Text style={[a.text_xs, a.leading_relaxed]}>
85 [a.text_xs a.leading_relaxed] {lorem}
86 </Text>
87 <Text style={[a.text_sm, a.leading_relaxed]}>
88 [a.text_sm a.leading_relaxed] {lorem}
89 </Text>
90 <Text style={[a.text_md, a.leading_relaxed]}>
91 [a.text_md a.leading_relaxed] {lorem}
92 </Text>
93 <Text style={[a.text_lg, a.leading_relaxed]}>
94 [a.text_lg a.leading_relaxed] {lorem}
95 </Text>
96 <Text style={[a.text_xl, a.leading_relaxed]}>
97 [a.text_xl a.leading_relaxed] {lorem}
98 </Text>
99 </View>
100 </Section>
101 </View>
102 </ScrollView>
103 )
104}
105
106function Colors() {
107 const t = useTheme()
108 console.log(t)
109 return (
110 <Section
111 style={[
112 a.gutter_default,
113 a.border,
114 t.atoms.bg,
115 t.atoms.border_contrast_low,
116 ]}>
117 <SectionTitle>Theme: {t.name}</SectionTitle>
118
119 <View style={[a.gap_md]}>
120 <Text style={[a.text_md, t.atoms.text_contrast_low]}>
121 t.atoms.text_contrast_low
122 </Text>
123 <Text style={[a.text_md, t.atoms.text_contrast_medium]}>
124 t.atoms.text_contrast_medium
125 </Text>
126 <Text style={[a.text_md, t.atoms.text_contrast_high]}>
127 t.atoms.text_contrast_high
128 </Text>
129 <Text style={[a.text_md, t.atoms.text]}>t.atoms.text</Text>
130 <View style={[t.atoms.bg_contrast_500]}>
131 <Text style={[a.text_md, t.atoms.text_inverted]}>
132 t.atoms.text_inverted
133 </Text>
134 </View>
135 </View>
136
137 <View style={[a.gap_md]}>
138 <Text style={[a.text_md, t.atoms.border_contrast_low]}>
139 t.atoms.border_contrast_low
140 </Text>
141 <View style={[t.atoms.border_contrast_low, a.border_t]} />
142 <Text style={[a.text_md, t.atoms.border_contrast_medium]}>
143 t.atoms.border_contrast_medium
144 </Text>
145 <View style={[t.atoms.border_contrast_medium, a.border_t]} />
146 <Text style={[a.text_md, t.atoms.border_contrast_high]}>
147 t.atoms.border_contrast_high
148 </Text>
149 <View style={[t.atoms.border_contrast_high, a.border_t]} />
150 </View>
151
152 <Text style={[a.text_md, a.font_bold]}>t.atoms.shadow_*</Text>
153 <View style={[a.gap_md]}>
154 {Object.entries(t.atoms)
155 .filter(([key]) => key.startsWith('shadow'))
156 .map(([key]) => {
157 return (
158 <View
159 key={key}
160 style={[
161 a.w_full,
162 t.atoms[key as keyof typeof t.atoms] as ViewStyle,
163 {
164 height: 50,
165 },
166 ]}
167 />
168 )
169 })}
170 </View>
171
172 <Text style={[a.text_md, a.font_bold]}>t.atoms.bg_contrast_*</Text>
173 <View style={[a.flex_row]}>
174 {Object.entries(t.atoms)
175 .filter(([key]) => key.startsWith('bg'))
176 .map(([key]) => {
177 return (
178 <View
179 key={key}
180 style={[
181 a.flex_1,
182 t.atoms[key as keyof typeof t.atoms] as ViewStyle,
183 {
184 height: 50,
185 },
186 ]}>
187 <Text style={[a.text_2xs, a.text_center]}>
188 {key.replace(/\D/g, '') || 'bg'}
189 </Text>
190 </View>
191 )
192 })}
193 </View>
194
195 <Text style={[a.text_md, a.font_bold]}>Palettes</Text>
196 {['contrast', 'primary', 'positive', 'negative'].map(palette => {
197 return (
198 <View key={palette} style={[a.flex_row]}>
199 {Object.entries(t.palette)
200 .filter(([key]) => key.startsWith(palette))
201 .map(([key]) => {
202 return (
203 <View
204 key={key}
205 style={[
206 a.flex_1,
207 {
208 height: 50,
209 backgroundColor:
210 t.palette[key as keyof typeof t.palette],
211 },
212 ]}>
213 <Text style={[a.text_2xs, a.text_center]}>
214 {key.replace(/\D/g, '')}
215 </Text>
216 </View>
217 )
218 })}
219 </View>
220 )
221 })}
222 </Section>
223 )
224}
225
226function Text({
227 children,
228 style,
229}: {
230 children: React.ReactNode
231 style?: StyleProp<TextStyle>
232}) {
233 const t = useTheme()
234 return (
235 <RNText style={[t.atoms.text, style, utils.leading(utils.flatten(style))]}>
236 {children}
237 </RNText>
238 )
239}
240
241function SectionTitle({children}: {children: React.ReactNode}) {
242 return <Text style={[a.text_3xl, a.font_bold]}>{children}</Text>
243}
244
245function Section({
246 children,
247 style,
248}: {
249 children: React.ReactNode
250 style?: StyleProp<ViewStyle>
251}) {
252 0
253 return <View style={[a.gap_lg, style]}>{children}</View>
254}
255
256const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.`