···1-import {type StyleProp, type ViewStyle} from 'react-native'
2-import {atoms as baseAtoms} from '@bsky.app/alf'
000034-import {native, platform, web} from '#/alf/util/platform'
05import * as Layout from '#/components/Layout'
67export const atoms = {
8- ...baseAtoms,
00090000000000000000000000000000000000000000000000000000000000000000000000000000000000000010 h_full_vh: web({
11 height: '100vh',
12 }),
0000001314 /**
15 * Used for the outermost components on screens, to ensure that they can fill
···32 },
3334 /*
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035 * Transition
36 */
37 transition_none: web({
···110 transform: [],
111 },
112 }) as {transform: Exclude<ViewStyle['transform'], string | undefined>},
0000113} as const
···9import {type Alf, applyFonts, atoms, flatten} from '#/alf'
1011/**
00000000000000012 * Ensures that `lineHeight` defaults to a relative value of `1`, or applies
13 * other relative leading atoms.
14 *
···9import {type Alf, applyFonts, atoms, flatten} from '#/alf'
1011/**
12+ * Util to calculate lineHeight from a text size atom and a leading atom
13+ *
14+ * Example:
15+ * `leading(atoms.text_md, atoms.leading_normal)` // => 24
16+ */
17+export function leading<
18+ Size extends {fontSize?: number},
19+ Leading extends {lineHeight?: number},
20+>(textSize: Size, leading: Leading) {
21+ const size = textSize?.fontSize || atoms.text_md.fontSize
22+ const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight
23+ return Math.round(size * lineHeight)
24+}
25+26+/**
27 * Ensures that `lineHeight` defaults to a relative value of `1`, or applies
28 * other relative leading atoms.
29 *
···1import {jest} from '@jest/globals'
23+import {logger} from '#/logger'
4import {transparentifyColor} from '../colorGeneration'
5+6+jest.mock('#/logger', () => ({
7+ logger: {warn: jest.fn()},
8+}))
910describe('transparentifyColor', () => {
11 beforeEach(() => {
···41 const unsupported = 'blue'
42 const result = transparentifyColor(unsupported, 0.5)
43 expect(result).toBe(unsupported)
44+ expect(logger.warn).toHaveBeenCalledWith(
45+ `Could not make '${unsupported}' transparent`,
46+ )
47 })
48})
+62-1
src/alf/util/platform.ts
···1-export {android, ios, native, platform, web} from '@bsky.app/alf'
0000000000000000000000000000000000000000000000000000000000000
···1+import {Platform} from 'react-native'
2+3+import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
4+5+/**
6+ * Identity function on web. Returns nothing on other platforms.
7+ *
8+ * Note: Platform splitting does not tree-shake away the other platforms,
9+ * so don't do stuff like e.g. rely on platform-specific imports. Use
10+ * platform-split files instead.
11+ */
12+export function web(value: any) {
13+ if (isWeb) {
14+ return value
15+ }
16+}
17+18+/**
19+ * Identity function on iOS. Returns nothing on other platforms.
20+ *
21+ * Note: Platform splitting does not tree-shake away the other platforms,
22+ * so don't do stuff like e.g. rely on platform-specific imports. Use
23+ * platform-split files instead.
24+ */
25+export function ios(value: any) {
26+ if (isIOS) {
27+ return value
28+ }
29+}
30+31+/**
32+ * Identity function on Android. Returns nothing on other platforms..
33+ *
34+ * Note: Platform splitting does not tree-shake away the other platforms,
35+ * so don't do stuff like e.g. rely on platform-specific imports. Use
36+ * platform-split files instead.
37+ */
38+export function android(value: any) {
39+ if (isAndroid) {
40+ return value
41+ }
42+}
43+44+/**
45+ * Identity function on iOS and Android. Returns nothing on web.
46+ *
47+ * Note: Platform splitting does not tree-shake away the other platforms,
48+ * so don't do stuff like e.g. rely on platform-specific imports. Use
49+ * platform-split files instead.
50+ */
51+export function native(value: any) {
52+ if (isNative) {
53+ return value
54+ }
55+}
56+57+/**
58+ * Note: Platform splitting does not tree-shake away the other platforms,
59+ * so don't do stuff like e.g. rely on platform-specific imports. Use
60+ * platform-split files instead.
61+ */
62+export const platform = Platform.select
+1-1
src/alf/util/systemUI.ts
···1import * as SystemUI from 'expo-system-ui'
2-import {type Theme} from '@bsky.app/alf'
34import {isAndroid} from '#/platform/detection'
056export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
7 if (isAndroid) {
···1import * as SystemUI from 'expo-system-ui'
023import {isAndroid} from '#/platform/detection'
4+import {type Theme} from '../types'
56export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
7 if (isAndroid) {
+13-2
src/alf/util/themeSelector.ts
···1-import {utils} from '@bsky.app/alf'
23-export const select = utils.select
00000000000
···1+import {type ThemeName} from '#/alf/types'
23+export function select<T>(name: ThemeName, options: Record<ThemeName, T>) {
4+ switch (name) {
5+ case 'light':
6+ return options.light
7+ case 'dark':
8+ return options.dark || options.dim
9+ case 'dim':
10+ return options.dim || options.dark
11+ default:
12+ throw new Error(`select(theme, options) received unknown theme ${name}`)
13+ }
14+}
+1-1
src/alf/util/useColorModeTheme.ts
···1import React from 'react'
2import {type ColorSchemeName, useColorScheme} from 'react-native'
3-import {type ThemeName} from '@bsky.app/alf'
45import {isWeb} from '#/platform/detection'
6import {useThemePrefs} from '#/state/shell'
7import {dark, dim, light} from '#/alf/themes'
089export function useColorModeTheme(): ThemeName {
10 const theme = useThemeName()
···1import React from 'react'
2import {type ColorSchemeName, useColorScheme} from 'react-native'
034import {isWeb} from '#/platform/detection'
5import {useThemePrefs} from '#/state/shell'
6import {dark, dim, light} from '#/alf/themes'
7+import {type ThemeName} from '#/alf/types'
89export function useColorModeTheme(): ThemeName {
10 const theme = useThemeName()
···188 label={_(msg`Dialog: adjust who can interact with this post`)}
189 style={web({maxWidth: 400})}>
190 <View style={[a.gap_sm]}>
191- <Text style={[a.font_semi_bold, a.text_xl, a.pb_sm]}>
192 <Trans>Who can interact with this post?</Trans>
193 </Text>
194 <Rules
···188 label={_(msg`Dialog: adjust who can interact with this post`)}
189 style={web({maxWidth: 400})}>
190 <View style={[a.gap_sm]}>
191+ <Text style={[a.font_bold, a.text_xl, a.pb_sm]}>
192 <Trans>Who can interact with this post?</Trans>
193 </Text>
194 <Rules
···62 </View>
6364 <View style={[a.gap_sm, a.pb_lg]}>
65- <Text style={[a.text_xl, a.leading_snug, a.font_bold]}>
66 <Trans>
67 You must complete age assurance in order to access this screen.
68 </Trans>
···62 </View>
6364 <View style={[a.gap_sm, a.pb_lg]}>
65+ <Text style={[a.text_xl, a.leading_snug, a.font_heavy]}>
66 <Trans>
67 You must complete age assurance in order to access this screen.
68 </Trans>
···62 <Dialog.ScrollableInner label={_(msg`How should we open this link?`)}>
63 <View style={[a.gap_2xl]}>
64 <View style={[a.gap_sm]}>
65- <Text style={[a.font_bold, a.text_2xl]}>
66 <Trans>How should we open this link?</Trans>
67 </Text>
68 <Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_md]}>
···62 <Dialog.ScrollableInner label={_(msg`How should we open this link?`)}>
63 <View style={[a.gap_2xl]}>
64 <View style={[a.gap_sm]}>
65+ <Text style={[a.font_heavy, a.text_2xl]}>
66 <Trans>How should we open this link?</Trans>
67 </Text>
68 <Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_md]}>
···82 style={[
83 t.atoms.text_contrast_medium,
84 a.text_sm,
85- a.font_semi_bold,
86 ]}>
87 {' '}
88 </Text>
···101 style={[
102 t.atoms.text,
103 a.text_sm,
104- a.font_semi_bold,
105 {opacity: 0.7}, // NOTE: we use opacity 0.7 instead of a color to match the color of the home pager tab bar
106 ]}>
107 {topic.topic}
···82 style={[
83 t.atoms.text_contrast_medium,
84 a.text_sm,
85+ a.font_bold,
86 ]}>
87 {' '}
88 </Text>
···101 style={[
102 t.atoms.text,
103 a.text_sm,
104+ a.font_bold,
105 {opacity: 0.7}, // NOTE: we use opacity 0.7 instead of a color to match the color of the home pager tab bar
106 ]}>
107 {topic.topic}
···1import {type ReactNode} from 'react'
2import {createContext, useContext} from 'react'
3import {type TextStyle, type ViewStyle} from 'react-native'
4-import {type ThemeName} from '@bsky.app/alf'
506import {darkTheme, defaultTheme, dimTheme} from './themes'
78export type ColorScheme = 'light' | 'dark'
···1import {type ReactNode} from 'react'
2import {createContext, useContext} from 'react'
3import {type TextStyle, type ViewStyle} from 'react-native'
045+import {type ThemeName} from '#/alf/types'
6import {darkTheme, defaultTheme, dimTheme} from './themes'
78export type ColorScheme = 'light' | 'dark'
···38 {/* spacer, since you can't nest pressables */}
39 <View style={[a.pt_md, a.pb_xs, a.w_full, {opacity: 0}]} aria-hidden>
40 {/* Placeholder text so that it responds to the font height */}
41- <Text style={[a.text_xs, a.leading_tight, a.font_semi_bold]}>
42 <Trans comment="Accept a chat request">Accept Request</Trans>
43 </Text>
44 </View>
···38 {/* spacer, since you can't nest pressables */}
39 <View style={[a.pt_md, a.pb_xs, a.w_full, {opacity: 0}]} aria-hidden>
40 {/* Placeholder text so that it responds to the font height */}
41+ <Text style={[a.text_xs, a.leading_tight, a.font_bold]}>
42 <Trans comment="Accept a chat request">Accept Request</Trans>
43 </Text>
44 </View>
···133134 return (
135 <View style={[a.align_start]} testID="onboardingInterests">
136- <Text style={[a.font_bold, a.text_3xl]}>
137 <Trans comment="Accounts suggested to the user for them to follow">
138 Suggested for you
139 </Trans>
···133134 return (
135 <View style={[a.align_start]} testID="onboardingInterests">
136+ <Text style={[a.font_heavy, a.text_3xl]}>
137 <Trans comment="Accounts suggested to the user for them to follow">
138 Suggested for you
139 </Trans>