diff --git a/web/components/font-picker.tsx b/web/components/font-picker.tsx
new file mode 100644
index 0000000..774d2b4
--- /dev/null
+++ b/web/components/font-picker.tsx
@@ -0,0 +1,34 @@
+'use client'
+
+import clsx from 'clsx'
+import {FontOption, useFontPreference} from 'web/hooks/use-font-preference'
+import {useT} from 'web/lib/locale'
+
+const FONT_OPTIONS: FontOption[] = ['atkinson', 'system-sans', 'classic-serif']
+
+const EN_TRANSLATIONS = {
+ "atkinson": "Atkinson Hyperlegible",
+ "system-sans": "Sytem Sans-serif",
+ "classic-serif": "Classic Serif"
+}
+
+export function FontPicker(props: { className?: string } = {}) {
+ const {className} = props
+ const {font, setFont} = useFontPreference()
+ const t = useT()
+
+ return (
+
+ )
+}
diff --git a/web/hooks/use-font-preference.ts b/web/hooks/use-font-preference.ts
new file mode 100644
index 0000000..0922b9e
--- /dev/null
+++ b/web/hooks/use-font-preference.ts
@@ -0,0 +1,37 @@
+import {useEffect} from 'react'
+import {usePersistentLocalState} from 'web/hooks/use-persistent-local-state'
+
+export type FontOption = 'atkinson' | 'system-sans' | 'classic-serif'
+
+const FONT_VARIABLES: Record = {
+ atkinson: '"Atkinson Hyperlegible Next", Georgia, "Times New Roman", Times, serif',
+ 'system-sans': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif',
+ 'classic-serif': 'Georgia, "Times New Roman", Times, serif',
+}
+
+export const useFontPreference = () => {
+ const [font, setFontState] = usePersistentLocalState('atkinson', 'font-preference')
+
+ const setFont = (newFont: FontOption) => {
+ setFontState(newFont)
+ applyFontPreference(newFont)
+ }
+
+ return {font, setFont}
+}
+
+export const useFontPreferenceManager = () => {
+ useEffect(() => {
+ applyFontPreference(getStoredFontPreference())
+ }, [])
+}
+
+export const applyFontPreference = (font: FontOption) => {
+ const fontFamily = FONT_VARIABLES[font] ?? FONT_VARIABLES.atkinson
+ document.documentElement.style.setProperty('--font-main', fontFamily)
+}
+
+const getStoredFontPreference = (): FontOption => {
+ if (typeof window === 'undefined') return 'atkinson'
+ return JSON.parse(localStorage.getItem('font-preference') ?? 'null') ?? 'atkinson'
+}
diff --git a/web/messages/de.json b/web/messages/de.json
index 9b52f9e..bc3ffee 100644
--- a/web/messages/de.json
+++ b/web/messages/de.json
@@ -1042,5 +1042,9 @@
"more_options_user.edit_profile": "Profil bearbeiten",
"more_options_user.profile_options": "Profiloptionen",
"more_options_user.edit_bio": "Bio bearbeiten",
- "vote.with_priority": "mit Priorität"
-}
\ No newline at end of file
+ "vote.with_priority": "mit Priorität",
+ "settings.general.font": "Schriftart",
+ "font.atkinson": "Atkinson Hyperlegible",
+ "font.system-sans": "System Sans",
+ "font.classic-serif": "Klassische Serifenschrift"
+}
diff --git a/web/messages/fr.json b/web/messages/fr.json
index 22c8c05..c168ebd 100644
--- a/web/messages/fr.json
+++ b/web/messages/fr.json
@@ -1042,5 +1042,9 @@
"more_options_user.edit_profile": "Modifier le profil",
"more_options_user.profile_options": "Options du profil",
"more_options_user.edit_bio": "Options de biographie",
- "vote.with_priority": "avec priorité"
-}
\ No newline at end of file
+ "vote.with_priority": "avec priorité",
+ "settings.general.font": "Police",
+ "font.atkinson": "Atkinson Hyperlegible",
+ "font.system-sans": "Sans-serif système",
+ "font.classic-serif": "Serif classique"
+}
diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx
index b72951e..033476c 100644
--- a/web/pages/_app.tsx
+++ b/web/pages/_app.tsx
@@ -23,6 +23,7 @@ import {getLocale, resetCachedLocale} from "web/lib/locale-cookie"
import {I18nContext} from "web/lib/locale"
import {HiddenProfilesProvider} from 'web/hooks/use-hidden-profiles'
import {updateStatusBar} from "web/hooks/use-theme"
+import {useFontPreferenceManager} from "web/hooks/use-font-preference"
import {DAYJS_LOCALE_IMPORTS} from "web/lib/dayjs";
import 'web/lib/dayjs'
@@ -94,6 +95,7 @@ function MyApp(props: AppProps) {
const {Component, pageProps} = props
useEffect(printBuildInfo, [])
useHasLoaded()
+ useFontPreferenceManager()
const router = useRouter()
const [locale, setLocaleState] = useState(getLocale())
diff --git a/web/pages/settings.tsx b/web/pages/settings.tsx
index 0aec2da..8906622 100644
--- a/web/pages/settings.tsx
+++ b/web/pages/settings.tsx
@@ -19,6 +19,7 @@ import {WithPrivateUser} from "web/components/user/with-user";
import {sendPasswordReset} from "web/lib/firebase/password";
import {AboutSettings} from "web/components/about-settings";
import {LanguagePicker} from "web/components/language/language-picker";
+import {FontPicker} from 'web/components/font-picker'
import {useT} from "web/lib/locale";
import HiddenProfilesModal from 'web/components/settings/hidden-profiles-modal'
import {EmailVerificationButton} from "web/components/email-verification-button";
@@ -119,6 +120,9 @@ const LoadedGeneralSettings = (props: {
{t('settings.general.language', 'Language')}
+ {t('settings.general.font', 'Font')}
+
+
{t('settings.data_privacy.title', 'Data & Privacy')}
diff --git a/web/public/init-theme.js b/web/public/init-theme.js
index 50b667d..45dec46 100644
--- a/web/public/init-theme.js
+++ b/web/public/init-theme.js
@@ -6,4 +6,16 @@
if (theme === 'dark' || (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
}
+
+ const localFontPreference = localStorage.getItem('font-preference')
+ const fontPreference = localFontPreference ? JSON.parse(localFontPreference) : 'atkinson'
+
+ const fontFamilies = {
+ atkinson: '"Atkinson Hyperlegible Next", Georgia, "Times New Roman", Times, serif',
+ 'system-sans': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif',
+ 'classic-serif': 'Georgia, "Times New Roman", Times, serif',
+ }
+
+ document.documentElement.style.setProperty('--font-main', fontFamilies[fontPreference] ?? fontFamilies.atkinson)
+
}