diff --git a/assets/icons/textSize_stroke2_corner0_rounded.svg b/assets/icons/textSize_stroke2_corner0_rounded.svg
new file mode 100644
index 0000000000..6c7537d100
--- /dev/null
+++ b/assets/icons/textSize_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/titleCase_stroke2_corner0_rounded.svg b/assets/icons/titleCase_stroke2_corner0_rounded.svg
new file mode 100644
index 0000000000..facdfc0e16
--- /dev/null
+++ b/assets/icons/titleCase_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts
index c38ba6be3c..f20de30b94 100644
--- a/src/alf/fonts.ts
+++ b/src/alf/fonts.ts
@@ -2,8 +2,21 @@ import {useFonts as defaultUseFonts} from 'expo-font'
import {Device, device} from '#/storage'
+const factor = 0.0625 // 1 - (15/16)
+const fontScaleMultipliers: Record = {
+ '-2': 1 - factor * 3,
+ '-1': 1 - factor * 2,
+ '0': 1 - factor * 1, // default
+ '1': 1,
+ '2': 1 + factor * 1,
+}
+
+export function computeFontScaleMultiplier(scale: Device['fontScale']) {
+ return fontScaleMultipliers[scale]
+}
+
export function getFontScale() {
- return device.get(['fontScale']) || 1
+ return device.get(['fontScale']) || '0'
}
export function setFontScale(fontScale: Device['fontScale']) {
diff --git a/src/alf/index.tsx b/src/alf/index.tsx
index 96eb49f95d..20cd9c4583 100644
--- a/src/alf/index.tsx
+++ b/src/alf/index.tsx
@@ -3,6 +3,7 @@ import {useMediaQuery} from 'react-responsive'
import {useGate} from '#/lib/statsig/statsig'
import {
+ computeFontScaleMultiplier,
getFontFamily,
getFontScale,
setFontFamily as persistFontFamily,
@@ -27,6 +28,7 @@ export type Alf = {
themes: ReturnType
fonts: {
scale: Exclude
+ scaleMultiplier: number
family: Device['fontFamily']
setFontScale: (fontScale: Exclude) => void
setFontFamily: (fontFamily: Device['fontFamily']) => void
@@ -51,6 +53,7 @@ export const Context = React.createContext({
}),
fonts: {
scale: getFontScale(),
+ scaleMultiplier: computeFontScaleMultiplier(getFontScale()),
family: getFontFamily(),
setFontScale: () => {},
setFontFamily: () => {},
@@ -66,11 +69,11 @@ export function ThemeProvider({
}: React.PropsWithChildren<{theme: ThemeName}>) {
const gate = useGate()
const [neue] = React.useState(() => gate('typography_neue'))
- const [fontScale, setFontScale] = React.useState(
- () => {
- if (!neue) return 1
- return getFontScale()
- },
+ const [fontScale, setFontScale] = React.useState(() =>
+ getFontScale(),
+ )
+ const [fontScaleMultiplier, setFontScaleMultiplier] = React.useState(() =>
+ computeFontScaleMultiplier(fontScale),
)
const setFontScaleAndPersist = React.useCallback<
Alf['fonts']['setFontScale']
@@ -78,6 +81,7 @@ export function ThemeProvider({
fontScale => {
setFontScale(fontScale)
persistFontScale(fontScale)
+ setFontScaleMultiplier(computeFontScaleMultiplier(fontScale))
},
[setFontScale],
)
@@ -112,6 +116,7 @@ export function ThemeProvider({
theme: themes[themeName],
fonts: {
scale: fontScale,
+ scaleMultiplier: fontScaleMultiplier,
family: fontFamily,
setFontScale: setFontScaleAndPersist,
setFontFamily: setFontFamilyAndPersist,
@@ -128,6 +133,7 @@ export function ThemeProvider({
setFontScaleAndPersist,
fontFamily,
setFontFamilyAndPersist,
+ fontScaleMultiplier,
],
)}>
{children}
diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx
index 0cfeb84cc8..f889edd00d 100644
--- a/src/components/Typography.tsx
+++ b/src/components/Typography.tsx
@@ -72,7 +72,7 @@ export function Text({style, selectable, ...rest}: TextProps) {
const {fonts, flags} = useAlf()
const t = useTheme()
const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)], {
- fontScale: fonts.scale,
+ fontScale: fonts.scaleMultiplier,
fontFamily: fonts.family,
flags,
})
diff --git a/src/components/icons/TextSize.tsx b/src/components/icons/TextSize.tsx
new file mode 100644
index 0000000000..73a6a085db
--- /dev/null
+++ b/src/components/icons/TextSize.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const TextSize_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M9 5a1 1 0 0 1 1-1h12a1 1 0 1 1 0 2h-5v14a1 1 0 1 1-2 0V6h-5a1 1 0 0 1-1-1Zm-3.073 7v8a1 1 0 1 0 2 0v-8H12a1 1 0 1 0 0-2H6.971a1.015 1.015 0 0 0-.089 0H2a1 1 0 1 0 0 2h3.927Z',
+})
diff --git a/src/components/icons/TitleCase.tsx b/src/components/icons/TitleCase.tsx
new file mode 100644
index 0000000000..9d040c9e5a
--- /dev/null
+++ b/src/components/icons/TitleCase.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const TitleCase_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M3.65 17.247c-.242.832-.632 1.178-1.325 1.178-.814 0-1.325-.476-1.325-1.23 0-.216.06-.51.173-.831L4.586 7.07c.364-1.014.979-1.482 1.966-1.482 1.022 0 1.629.45 2.001 1.473l3.43 9.303c.121.337.165.571.165.831 0 .72-.546 1.23-1.308 1.23-.736 0-1.126-.338-1.36-1.152l-.658-1.975H4.309l-.658 1.95ZM6.5 8.152l-1.62 5.12h3.335l-1.654-5.12H6.5Zm13.005 8.688c-.52.988-1.68 1.568-2.84 1.568-1.768 0-3.11-1.144-3.11-2.815 0-1.69 1.299-2.668 3.62-2.807l2.34-.138v-.615c0-.867-.607-1.369-1.56-1.369-.771 0-1.239.251-1.802.979-.277.312-.597.468-1.004.468-.615 0-1.057-.399-1.057-.97 0-.2.043-.382.13-.572.433-1.109 1.923-1.793 3.845-1.793 2.383 0 3.933 1.23 3.933 3.1v5.293c0 .84-.511 1.273-1.23 1.273-.684 0-1.16-.38-1.213-1.126v-.476h-.052Zm-3.43-1.386c0 .693.572 1.126 1.42 1.126 1.11 0 2.02-.719 2.02-1.723v-.676l-1.959.121c-.944.07-1.48.494-1.48 1.152Z',
+})
diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx
index 04f39af6eb..712187b436 100644
--- a/src/screens/Settings/AppearanceSettings.tsx
+++ b/src/screens/Settings/AppearanceSettings.tsx
@@ -16,11 +16,11 @@ import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, native, useAlf, useTheme} from '#/alf'
-import {Divider} from '#/components/Divider'
-import * as Toggle from '#/components/forms/Toggle'
import * as ToggleButton from '#/components/forms/ToggleButton'
import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon'
import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone'
+import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/TextSize'
+import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase'
import {Text} from '#/components/Typography'
type Props = NativeStackScreenProps
@@ -35,10 +35,8 @@ export function AppearanceSettingsScreen({}: Props) {
const {colorMode, darkTheme} = useThemePrefs()
const {setColorMode, setDarkTheme} = useSetThemePrefs()
- const [fontScale, setFontScale] = React.useState(() => fonts.scale !== 1)
- const [fontFamily, setFontFamily] = React.useState(
- () => fonts.family === 'theme',
- )
+ const [fontFamily, setFontFamily] = React.useState(fonts.family)
+ const [fontScale, setFontScale] = React.useState(() => fonts.scale)
const onChangeAppearance = useCallback(
(keys: string[]) => {
@@ -65,6 +63,24 @@ export function AppearanceSettingsScreen({}: Props) {
[setDarkTheme, darkTheme],
)
+ const onChangeFontFamily = useCallback(
+ (values: string[]) => {
+ const next = values[0] === 'system' ? 'system' : 'theme'
+ setFontFamily(next)
+ fonts.setFontFamily(next)
+ },
+ [setFontFamily, fonts],
+ )
+
+ const onChangeFontScale = useCallback(
+ (values: string[]) => {
+ const next = values[0] || ('0' as any)
+ setFontScale(next)
+ fonts.setFontScale(next)
+ },
+ [setFontScale, fonts],
+ )
+
return (
@@ -87,7 +103,7 @@ export function AppearanceSettingsScreen({}: Props) {
-
+
Mode
@@ -120,7 +136,7 @@ export function AppearanceSettingsScreen({}: Props) {
style={[a.gap_md]}>
-
+
Dark theme
@@ -142,105 +158,80 @@ export function AppearanceSettingsScreen({}: Props) {
)}
+
+ {neue && (
+ <>
+
+
+
+
+
+ Font
+
+
+
+
+ For the best experience, we recommend using the theme
+ font.
+
+
+
+
+
+
+ System
+
+
+
+
+ Theme
+
+
+
+
+
+
+
+
+
+
+ Font size
+
+
+
+
+
+
+
+ Smaller
+
+
+
+
+ Default
+
+
+
+
+ Larger
+
+
+
+
+ >
+ )}
-
- {neue && (
-
- Typography
-
-
-
-
-
-
- Theme font
-
-
-
- Enables a consistent typeface for a clean user
- experience across devices. To use your default system
- font, disable this.
-
-
-
-
- {
- setFontFamily(checked)
- fonts.setFontFamily(checked ? 'theme' : 'system')
- }}>
-
- {fontFamily ? (
- Enabled
- ) : (
- Disabled
- )}
-
-
-
-
-
-
-
-
-
-
- Theme font size
-
-
-
- Subtly scales the type size for a clean user experience
- across devices. To use the font size defined by your
- operating system, disable this.
-
-
-
-
- {
- setFontScale(checked)
- fonts.setFontScale(checked ? 0.9375 : 1)
- }}>
-
- {fontScale ? (
- Enabled
- ) : (
- Disabled
- )}
-
-
-
-
-
- )}
diff --git a/src/storage/schema.ts b/src/storage/schema.ts
index 1bffe646bb..1a9656fede 100644
--- a/src/storage/schema.ts
+++ b/src/storage/schema.ts
@@ -2,7 +2,7 @@
* Device data that's specific to the device and does not vary based account
*/
export type Device = {
- fontScale: number
+ fontScale: '-2' | '-1' | '0' | '1' | '2'
fontFamily: 'system' | 'theme'
lastNuxDialog: string | undefined
}
diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx
index 2a6a4c6e85..c06a7f7700 100644
--- a/src/view/com/util/text/Text.tsx
+++ b/src/view/com/util/text/Text.tsx
@@ -51,7 +51,7 @@ export function Text({
// @ts-ignore
if (flattened.fontSize) {
// @ts-ignore
- flattened.fontSize = flattened.fontSize * fonts.scale
+ flattened.fontSize = flattened.fontSize * fonts.scaleMultiplier
}
return (
@@ -81,7 +81,7 @@ export function Text({
// @ts-ignore
if (flattened.fontSize) {
// @ts-ignore
- flattened.fontSize = flattened.fontSize * fonts.scale
+ flattened.fontSize = flattened.fontSize * fonts.scaleMultiplier
}
return (