diff --git a/src/App.native.tsx b/src/App.native.tsx
index 83f133e990..b488c5fe88 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -107,51 +107,46 @@ function InnerApp() {
}, [_])
return (
-
-
-
-
-
-
+
+
+
+
+
+
-
-
- {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
)
}
diff --git a/src/App.web.tsx b/src/App.web.tsx
index ff9944fa4a..a7a99ed3eb 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -92,48 +92,44 @@ function InnerApp() {
return (
-
-
-
-
-
+
+
+
+
+
-
-
- {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
)
}
diff --git a/src/alf/index.tsx b/src/alf/index.tsx
index d699de6a5b..963daab2a7 100644
--- a/src/alf/index.tsx
+++ b/src/alf/index.tsx
@@ -1,9 +1,11 @@
import React from 'react'
import {useMediaQuery} from 'react-responsive'
+import {useGate} from '#/lib/statsig/statsig'
import {createThemes, defaultTheme} from '#/alf/themes'
import {Theme, ThemeName} from '#/alf/types'
import {BLUE_HUE, GREEN_HUE, RED_HUE} from '#/alf/util/colorGeneration'
+import * as fontScaling from '#/alf/util/fontScaling'
export {atoms} from '#/alf/atoms'
export * as tokens from '#/alf/tokens'
@@ -12,14 +14,21 @@ export * from '#/alf/util/flatten'
export * from '#/alf/util/platform'
export * from '#/alf/util/themeSelector'
-/*
- * Context
- */
-export const Context = React.createContext<{
+export type Alf = {
themeName: ThemeName
theme: Theme
themes: ReturnType
-}>({
+ fontScale: number
+ setFontScale: (fontScale: number) => void
+ flags: {
+ neue: boolean
+ }
+}
+
+/*
+ * Context
+ */
+export const Context = React.createContext({
themeName: 'light',
theme: defaultTheme,
themes: createThemes({
@@ -29,12 +38,27 @@ export const Context = React.createContext<{
positive: GREEN_HUE,
},
}),
+ fontScale: fontScaling.get(),
+ setFontScale: () => {},
+ flags: {
+ neue: false,
+ },
})
export function ThemeProvider({
children,
theme: themeName,
}: React.PropsWithChildren<{theme: ThemeName}>) {
+ const gate = useGate()
+ const [neue] = React.useState(() => gate('typography_neue'))
+ const [fontScale, setFontScale] = React.useState(() => fontScaling.get())
+ const setFontScaleAndPersist = React.useCallback(
+ (fontScale: number) => {
+ setFontScale(fontScale)
+ fontScaling.set(fontScale)
+ },
+ [setFontScale],
+ )
const themes = React.useMemo(() => {
return createThemes({
hues: {
@@ -44,28 +68,41 @@ export function ThemeProvider({
},
})
}, [])
- const theme = themes[themeName]
return (
(
() => ({
themes,
themeName: themeName,
- theme: theme,
+ theme: themes[themeName],
+ fontScale,
+ setFontScale: setFontScaleAndPersist,
+ flags: {
+ neue,
+ },
}),
- [theme, themeName, themes],
+ [themeName, themes, fontScale, setFontScaleAndPersist, neue],
)}>
{children}
)
}
+export function useAlf() {
+ return React.useContext(Context)
+}
+
+export function useFontScale() {
+ const {fontScale, setFontScale} = useAlf()
+ return {fontScale, setFontScale}
+}
+
export function useTheme(theme?: ThemeName) {
- const ctx = React.useContext(Context)
+ const alf = useAlf()
return React.useMemo(() => {
- return theme ? ctx.themes[theme] : ctx.theme
- }, [theme, ctx])
+ return theme ? alf.themes[theme] : alf.theme
+ }, [theme, alf])
}
export function useBreakpoints() {
diff --git a/src/alf/util/fontScaling.ts b/src/alf/util/fontScaling.ts
new file mode 100644
index 0000000000..000d1b1689
--- /dev/null
+++ b/src/alf/util/fontScaling.ts
@@ -0,0 +1,10 @@
+import {device} from '#/storage'
+
+export function set(fontScale: number) {
+ device.set(['fontScale'], fontScale)
+}
+
+export function get() {
+ const fontScale = device.get(['fontScale']) || 1
+ return fontScale
+}
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index d65444e1f7..704aa9d987 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -7,7 +7,6 @@ import {
PressableProps,
StyleProp,
StyleSheet,
- Text,
TextProps,
TextStyle,
View,
@@ -17,7 +16,7 @@ import {LinearGradient} from 'expo-linear-gradient'
import {android, atoms as a, flatten, select, tokens, useTheme} from '#/alf'
import {Props as SVGIconProps} from '#/components/icons/common'
-import {normalizeTextStyles} from '#/components/Typography'
+import {Text} from '#/components/Typography'
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient'
export type ButtonColor =
@@ -635,14 +634,7 @@ export function ButtonText({children, style, ...rest}: ButtonTextProps) {
const textStyles = useSharedButtonTextStyles()
return (
-
+
{children}
)
diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx
index 31dd931c6a..5cdcd7d7c2 100644
--- a/src/components/Typography.tsx
+++ b/src/components/Typography.tsx
@@ -3,7 +3,7 @@ import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native'
import {UITextView} from 'react-native-uitextview'
import {isNative} from '#/platform/detection'
-import {atoms, flatten, useTheme, web} from '#/alf'
+import {atoms, flatten, useAlf, useTheme, web} from '#/alf'
export type TextProps = RNTextProps & {
/**
@@ -34,14 +34,17 @@ export function leading<
* If the `lineHeight` value is > 2, we assume it's an absolute value and
* returns it as-is.
*/
-export function normalizeTextStyles(styles: StyleProp) {
+export function normalizeTextStyles(
+ styles: StyleProp,
+ {fontScale}: {fontScale: number},
+) {
const s = flatten(styles)
// should always be defined on these components
- const fontSize = s.fontSize || atoms.text_md.fontSize
+ s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale
if (s?.lineHeight) {
if (s.lineHeight !== 0 && s.lineHeight <= 2) {
- s.lineHeight = Math.round(fontSize * s.lineHeight)
+ s.lineHeight = Math.round(s.fontSize * s.lineHeight)
}
} else if (!isNative) {
s.lineHeight = s.fontSize
@@ -54,8 +57,11 @@ export function normalizeTextStyles(styles: StyleProp) {
* Our main text component. Use this most of the time.
*/
export function Text({style, selectable, ...rest}: TextProps) {
+ const {fontScale} = useAlf()
const t = useTheme()
- const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)])
+ const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)], {
+ fontScale,
+ })
return
}
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 909b93e6be..5c260633b8 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -1,5 +1,6 @@
export type Gate =
// Keep this alphabetic please.
| 'debug_show_feedcontext'
+ | 'typography_neue'
| 'suggested_feeds_interstitial'
| 'ten_million_dialog'
diff --git a/src/storage/schema.ts b/src/storage/schema.ts
index bc41fd3edf..6ef042e021 100644
--- a/src/storage/schema.ts
+++ b/src/storage/schema.ts
@@ -2,5 +2,6 @@
* Device data that's specific to the device and does not vary based account
*/
export type Device = {
+ fontScale: number | undefined
lastNuxDialog: string | undefined
}