Skip to content

Commit 0f9a061

Browse files
committed
feat(theme): per-theme font pickers now actually drive terracotta/luxe typography
- Persist headerFont/bodyFont per theme (brutalist/luxe/terracotta) - Defaults: terracotta → Fraunces + IBM Plex Mono, luxe → Playfair + Outfit - Layout injects high-specificity CSS scoped by data-fezcodex-theme - Hardcoded font-fraunces/playfair classes no longer win over user picks - Mono elements (code, pre, kbd, .font-mono) stay monospace regardless
1 parent 19005f9 commit 0f9a061

2 files changed

Lines changed: 77 additions & 6 deletions

File tree

src/components/Layout.jsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,55 @@ const Layout = ({
4242
toggleSidebar,
4343
isAppFullscreen,
4444
fezcodexTheme,
45+
headerFont,
46+
bodyFont,
4547
} = useVisualSettings();
48+
49+
const FONT_FAMILY = {
50+
'font-sans': "'Space Mono', monospace",
51+
'font-mono': "'JetBrains Mono', monospace",
52+
'font-inter': "'Inter', sans-serif",
53+
'font-arvo': "'Arvo', serif",
54+
'font-playfairDisplay': "'Playfair Display', serif",
55+
'font-syne': "'Syne', sans-serif",
56+
'font-outfit': "'Outfit', sans-serif",
57+
'font-ibm-plex-mono': "'IBM Plex Mono', monospace",
58+
'font-instr-serif': "'Instrument Serif', serif",
59+
'font-nunito': "'Nunito', sans-serif",
60+
'font-fraunces': "'Fraunces', 'Times New Roman', serif",
61+
};
62+
63+
const themedFontCss =
64+
fezcodexTheme === 'terracotta' || fezcodexTheme === 'luxe'
65+
? `
66+
[data-fezcodex-theme="${fezcodexTheme}"],
67+
[data-fezcodex-theme="${fezcodexTheme}"] p,
68+
[data-fezcodex-theme="${fezcodexTheme}"] li,
69+
[data-fezcodex-theme="${fezcodexTheme}"] span,
70+
[data-fezcodex-theme="${fezcodexTheme}"] div,
71+
[data-fezcodex-theme="${fezcodexTheme}"] a,
72+
[data-fezcodex-theme="${fezcodexTheme}"] button,
73+
[data-fezcodex-theme="${fezcodexTheme}"] blockquote {
74+
font-family: ${FONT_FAMILY[bodyFont] || FONT_FAMILY['font-outfit']};
75+
}
76+
[data-fezcodex-theme="${fezcodexTheme}"] h1,
77+
[data-fezcodex-theme="${fezcodexTheme}"] h2,
78+
[data-fezcodex-theme="${fezcodexTheme}"] h3,
79+
[data-fezcodex-theme="${fezcodexTheme}"] h4,
80+
[data-fezcodex-theme="${fezcodexTheme}"] h5,
81+
[data-fezcodex-theme="${fezcodexTheme}"] h6 {
82+
font-family: ${FONT_FAMILY[headerFont] || FONT_FAMILY['font-outfit']};
83+
}
84+
/* Respect explicit monospace labels — mono stays mono regardless */
85+
[data-fezcodex-theme="${fezcodexTheme}"] code,
86+
[data-fezcodex-theme="${fezcodexTheme}"] pre,
87+
[data-fezcodex-theme="${fezcodexTheme}"] kbd,
88+
[data-fezcodex-theme="${fezcodexTheme}"] .font-mono,
89+
[data-fezcodex-theme="${fezcodexTheme}"] .font-ibm-plex-mono {
90+
font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
91+
}
92+
`
93+
: '';
4694
const location = useLocation();
4795
const { projects } = useProjects();
4896

@@ -85,6 +133,7 @@ const Layout = ({
85133
<DndProvider>{children}</DndProvider>
86134
) : (
87135
<div
136+
data-fezcodex-theme={fezcodexTheme}
88137
className={`${
89138
fezcodexTheme === 'luxe'
90139
? 'bg-[#F5F5F0]'
@@ -93,6 +142,7 @@ const Layout = ({
93142
: 'bg-[#050505]'
94143
} min-h-screen font-sans flex`}
95144
>
145+
{themedFontCss && <style>{themedFontCss}</style>}
96146
{!hideLayout &&
97147
(fezcodexTheme === 'luxe' ? (
98148
<LuxeSidebar

src/context/VisualSettingsContext.jsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,23 @@ export const VisualSettingsProvider = ({ children }) => {
2626
{ id: 'font-fraunces', name: 'Fraunces' },
2727
];
2828

29-
const [headerFont, setHeaderFont] = usePersistentState(
30-
'header-font',
31-
'font-outfit',
29+
// Font picks are persisted per-theme so switching themes does not reset
30+
// your typography preferences in the other themes.
31+
const [headerFontsByTheme, setHeaderFontsByTheme] = usePersistentState(
32+
'header-fonts-by-theme',
33+
{
34+
brutalist: 'font-outfit',
35+
luxe: 'font-playfairDisplay',
36+
terracotta: 'font-fraunces',
37+
},
3238
);
33-
const [bodyFont, setBodyFont] = usePersistentState(
34-
'body-font',
35-
'font-outfit',
39+
const [bodyFontsByTheme, setBodyFontsByTheme] = usePersistentState(
40+
'body-fonts-by-theme',
41+
{
42+
brutalist: 'font-outfit',
43+
luxe: 'font-outfit',
44+
terracotta: 'font-ibm-plex-mono',
45+
},
3646
);
3747
const [isInverted, setIsInverted] = usePersistentState('is-inverted', false);
3848
const [isRetro, setIsRetro] = usePersistentState('is-retro', false);
@@ -101,6 +111,17 @@ export const VisualSettingsProvider = ({ children }) => {
101111
'brutalist',
102112
); // 'brutalist' | 'luxe' | 'terracotta'
103113

114+
// Derived font values for the active theme. Setting these updates only the
115+
// active theme's entry, preserving the other themes' picks.
116+
const headerFont =
117+
headerFontsByTheme?.[fezcodexTheme] || 'font-outfit';
118+
const bodyFont =
119+
bodyFontsByTheme?.[fezcodexTheme] || 'font-outfit';
120+
const setHeaderFont = (value) =>
121+
setHeaderFontsByTheme((prev) => ({ ...(prev || {}), [fezcodexTheme]: value }));
122+
const setBodyFont = (value) =>
123+
setBodyFontsByTheme((prev) => ({ ...(prev || {}), [fezcodexTheme]: value }));
124+
104125
// URL Parameter Observer - Consumes ?fezTheme=... and ?fezBlogMode=...
105126
useEffect(() => {
106127
const params = new URLSearchParams(window.location.search);

0 commit comments

Comments
 (0)