Bluesky's "Application Layout Framework"
at main 256 lines 7.9 kB view raw
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.`