···99import {type Alf, applyFonts, atoms, flatten} from '#/alf'
10101111/**
1212- * Util to calculate lineHeight from a text size atom and a leading atom
1313- *
1414- * Example:
1515- * `leading(atoms.text_md, atoms.leading_normal)` // => 24
1616- */
1717-export function leading<
1818- Size extends {fontSize?: number},
1919- Leading extends {lineHeight?: number},
2020->(textSize: Size, leading: Leading) {
2121- const size = textSize?.fontSize || atoms.text_md.fontSize
2222- const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight
2323- return Math.round(size * lineHeight)
2424-}
2525-2626-/**
2712 * Ensures that `lineHeight` defaults to a relative value of `1`, or applies
2813 * other relative leading atoms.
2914 *
-8
src/alf/util/__tests__/colors.test.ts
···11import {jest} from '@jest/globals'
2233-import {logger} from '#/logger'
43import {transparentifyColor} from '../colorGeneration'
55-66-jest.mock('#/logger', () => ({
77- logger: {warn: jest.fn()},
88-}))
94105describe('transparentifyColor', () => {
116 beforeEach(() => {
···4136 const unsupported = 'blue'
4237 const result = transparentifyColor(unsupported, 0.5)
4338 expect(result).toBe(unsupported)
4444- expect(logger.warn).toHaveBeenCalledWith(
4545- `Could not make '${unsupported}' transparent`,
4646- )
4739 })
4840})
+3-44
src/alf/util/colorGeneration.ts
···11-import {logger} from '#/logger'
11+import {utils} from '@bsky.app/alf'
2233export const BLUE_HUE = 211
44-export const RED_HUE = 346
55-export const GREEN_HUE = 152
6475/**
88- * Smooth progression of lightness "stops" for generating HSL colors.
66+ * @deprecated use `utils.alpha` from `@bsky.app/alf` instead
97 */
1010-export const COLOR_STOPS = [
1111- 0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95, 1,
1212-]
1313-1414-export function generateScale(start: number, end: number) {
1515- const range = end - start
1616- return COLOR_STOPS.map(stop => {
1717- return start + range * stop
1818- })
1919-}
2020-2121-export const defaultScale = generateScale(6, 100)
2222-// dim shifted 6% lighter
2323-export const dimScale = generateScale(12, 100)
2424-2525-export function transparentifyColor(color: string, alpha: number) {
2626- if (color.startsWith('hsl(')) {
2727- return 'hsla(' + color.slice('hsl('.length, -1) + `, ${alpha})`
2828- } else if (color.startsWith('rgb(')) {
2929- return 'rgba(' + color.slice('rgb('.length, -1) + `, ${alpha})`
3030- } else if (color.startsWith('#')) {
3131- if (color.length === 7) {
3232- const alphaHex = Math.round(alpha * 255).toString(16)
3333- // Per MDN: If there is only one number, it is duplicated: e means ee
3434- // https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
3535- return color.slice(0, 7) + alphaHex.padStart(2, alphaHex)
3636- } else if (color.length === 4) {
3737- // convert to 6-digit hex before adding alpha
3838- const [r, g, b] = color.slice(1).split('')
3939- const alphaHex = Math.round(alpha * 255).toString(16)
4040- return `#${r.repeat(2)}${g.repeat(2)}${b.repeat(2)}${alphaHex.padStart(
4141- 2,
4242- alphaHex,
4343- )}`
4444- }
4545- } else {
4646- logger.warn(`Could not make '${color}' transparent`)
4747- }
4848- return color
4949-}
88+export const transparentifyColor = utils.alpha
+1-62
src/alf/util/platform.ts
···11-import {Platform} from 'react-native'
22-33-import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
44-55-/**
66- * Identity function on web. Returns nothing on other platforms.
77- *
88- * Note: Platform splitting does not tree-shake away the other platforms,
99- * so don't do stuff like e.g. rely on platform-specific imports. Use
1010- * platform-split files instead.
1111- */
1212-export function web(value: any) {
1313- if (isWeb) {
1414- return value
1515- }
1616-}
1717-1818-/**
1919- * Identity function on iOS. Returns nothing on other platforms.
2020- *
2121- * Note: Platform splitting does not tree-shake away the other platforms,
2222- * so don't do stuff like e.g. rely on platform-specific imports. Use
2323- * platform-split files instead.
2424- */
2525-export function ios(value: any) {
2626- if (isIOS) {
2727- return value
2828- }
2929-}
3030-3131-/**
3232- * Identity function on Android. Returns nothing on other platforms..
3333- *
3434- * Note: Platform splitting does not tree-shake away the other platforms,
3535- * so don't do stuff like e.g. rely on platform-specific imports. Use
3636- * platform-split files instead.
3737- */
3838-export function android(value: any) {
3939- if (isAndroid) {
4040- return value
4141- }
4242-}
4343-4444-/**
4545- * Identity function on iOS and Android. Returns nothing on web.
4646- *
4747- * Note: Platform splitting does not tree-shake away the other platforms,
4848- * so don't do stuff like e.g. rely on platform-specific imports. Use
4949- * platform-split files instead.
5050- */
5151-export function native(value: any) {
5252- if (isNative) {
5353- return value
5454- }
5555-}
5656-5757-/**
5858- * Note: Platform splitting does not tree-shake away the other platforms,
5959- * so don't do stuff like e.g. rely on platform-specific imports. Use
6060- * platform-split files instead.
6161- */
6262-export const platform = Platform.select
11+export {android, ios, native, platform, web} from '@bsky.app/alf'
+1-1
src/alf/util/systemUI.ts
···11import * as SystemUI from 'expo-system-ui'
22+import {type Theme} from '@bsky.app/alf'
2334import {isAndroid} from '#/platform/detection'
44-import {type Theme} from '../types'
5566export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
77 if (isAndroid) {
+2-13
src/alf/util/themeSelector.ts
···11-import {type ThemeName} from '#/alf/types'
11+import {utils} from '@bsky.app/alf'
2233-export function select<T>(name: ThemeName, options: Record<ThemeName, T>) {
44- switch (name) {
55- case 'light':
66- return options.light
77- case 'dark':
88- return options.dark || options.dim
99- case 'dim':
1010- return options.dim || options.dark
1111- default:
1212- throw new Error(`select(theme, options) received unknown theme ${name}`)
1313- }
1414-}
33+export const select = utils.select
+1-1
src/alf/util/useColorModeTheme.ts
···11import React from 'react'
22import {type ColorSchemeName, useColorScheme} from 'react-native'
33+import {type ThemeName} from '@bsky.app/alf'
3445import {isWeb} from '#/platform/detection'
56import {useThemePrefs} from '#/state/shell'
67import {dark, dim, light} from '#/alf/themes'
77-import {type ThemeName} from '#/alf/types'
8899export function useColorModeTheme(): ThemeName {
1010 const theme = useThemeName()
···6262 </View>
63636464 <View style={[a.gap_sm, a.pb_lg]}>
6565- <Text style={[a.text_xl, a.leading_snug, a.font_heavy]}>
6565+ <Text style={[a.text_xl, a.leading_snug, a.font_bold]}>
6666 <Trans>
6767 You must complete age assurance in order to access this screen.
6868 </Trans>
···6262 <Dialog.ScrollableInner label={_(msg`How should we open this link?`)}>
6363 <View style={[a.gap_2xl]}>
6464 <View style={[a.gap_sm]}>
6565- <Text style={[a.font_heavy, a.text_2xl]}>
6565+ <Text style={[a.font_bold, a.text_2xl]}>
6666 <Trans>How should we open this link?</Trans>
6767 </Text>
6868 <Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_md]}>
···8282 style={[
8383 t.atoms.text_contrast_medium,
8484 a.text_sm,
8585- a.font_bold,
8585+ a.font_semi_bold,
8686 ]}>
8787 {' '}
8888 </Text>
···101101 style={[
102102 t.atoms.text,
103103 a.text_sm,
104104- a.font_bold,
104104+ a.font_semi_bold,
105105 {opacity: 0.7}, // NOTE: we use opacity 0.7 instead of a color to match the color of the home pager tab bar
106106 ]}>
107107 {topic.topic}
···11import {type ReactNode} from 'react'
22import {createContext, useContext} from 'react'
33import {type TextStyle, type ViewStyle} from 'react-native'
44+import {type ThemeName} from '@bsky.app/alf'
4555-import {type ThemeName} from '#/alf/types'
66import {darkTheme, defaultTheme, dimTheme} from './themes'
7788export type ColorScheme = 'light' | 'dark'
···133133134134 return (
135135 <View style={[a.align_start]} testID="onboardingInterests">
136136- <Text style={[a.font_heavy, a.text_3xl]}>
136136+ <Text style={[a.font_bold, a.text_3xl]}>
137137 <Trans comment="Accounts suggested to the user for them to follow">
138138 Suggested for you
139139 </Trans>
···2626 <Text style={[a.text_xl, a.font_medium, a.italic]}>
2727 This is medium italic text
2828 </Text>
2929- <Text style={[a.text_xl, a.font_bold]}>This is bold text</Text>
3030- <Text style={[a.text_xl, a.font_bold, a.italic]}>
2929+ <Text style={[a.text_xl, a.font_semi_bold]}>This is bold text</Text>
3030+ <Text style={[a.text_xl, a.font_semi_bold, a.italic]}>
3131 This is bold italic text
3232 </Text>
3333- <Text style={[a.text_xl, a.font_heavy]}>This is heavy text</Text>
3434- <Text style={[a.text_xl, a.font_heavy, a.italic]}>
3333+ <Text style={[a.text_xl, a.font_bold]}>This is heavy text</Text>
3434+ <Text style={[a.text_xl, a.font_bold, a.italic]}>
3535 This is heavy italic text
3636 </Text>
3737