Skip to content

Commit e6d4217

Browse files
authored
Refresh UI fonts and theme presets (#97)
- Add selectable interface fonts with persisted preference - Replace the old Cursor Dark preset with new color themes - Update base typography and markdown preview font variables
1 parent ada71d4 commit e6d4217

6 files changed

Lines changed: 278 additions & 61 deletions

File tree

apps/web/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<link rel="preconnect" href="https://fonts.googleapis.com" />
99
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1010
<link
11-
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..800;1,9..40,300..800&display=swap"
11+
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..800;1,9..40,300..800&family=Inter:wght@300..800&family=Plus+Jakarta+Sans:wght@300..800&display=swap"
1212
rel="stylesheet"
1313
/>
1414
<title>OK Code</title>

apps/web/src/components/MarkdownPreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export const MarkdownPreview = memo(function MarkdownPreview({ contents }: Markd
9999
"--cm-callout-bg": "var(--secondary)",
100100
"--cm-radius": "12px",
101101
"--cm-font":
102-
'"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
102+
'var(--font-ui, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif)',
103103
"--cm-mono":
104104
'"SF Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace',
105105
} as CSSProperties

apps/web/src/hooks/useTheme.ts

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,39 @@ type ColorTheme =
55
| "default"
66
| "iridescent-void"
77
| "solar-witch"
8-
| "cursor-dark"
8+
| "midnight-clarity"
9+
| "carbon"
10+
| "vapor"
911
| "cathedral-circuit";
1012

13+
type FontFamily = "dm-sans" | "inter" | "plus-jakarta-sans";
14+
1115
type ThemeSnapshot = {
1216
theme: Theme;
1317
systemDark: boolean;
1418
colorTheme: ColorTheme;
19+
fontFamily: FontFamily;
1520
};
1621

1722
export const COLOR_THEMES: { id: ColorTheme; label: string }[] = [
1823
{ id: "default", label: "Default" },
1924
{ id: "iridescent-void", label: "Iridescent Void" },
2025
{ id: "solar-witch", label: "Solar Witch" },
21-
{ id: "cursor-dark", label: "Cursor Dark" },
26+
{ id: "midnight-clarity", label: "Midnight Clarity" },
27+
{ id: "carbon", label: "Carbon" },
28+
{ id: "vapor", label: "Vapor" },
2229
{ id: "cathedral-circuit", label: "Cathedral Circuit" },
2330
];
2431

32+
export const FONT_FAMILIES: { id: FontFamily; label: string }[] = [
33+
{ id: "inter", label: "Inter" },
34+
{ id: "dm-sans", label: "DM Sans" },
35+
{ id: "plus-jakarta-sans", label: "Plus Jakarta Sans" },
36+
];
37+
2538
const STORAGE_KEY = "okcode:theme";
2639
const COLOR_THEME_STORAGE_KEY = "okcode:color-theme";
40+
const FONT_FAMILY_STORAGE_KEY = "okcode:font-family";
2741
const MEDIA_QUERY = "(prefers-color-scheme: dark)";
2842

2943
let listeners: Array<() => void> = [];
@@ -49,14 +63,36 @@ function getStoredColorTheme(): ColorTheme {
4963
raw === "default" ||
5064
raw === "iridescent-void" ||
5165
raw === "solar-witch" ||
52-
raw === "cursor-dark" ||
66+
raw === "midnight-clarity" ||
67+
raw === "carbon" ||
68+
raw === "vapor" ||
5369
raw === "cathedral-circuit"
5470
) {
5571
return raw;
5672
}
5773
return "default";
5874
}
5975

76+
function getStoredFontFamily(): FontFamily {
77+
const raw = localStorage.getItem(FONT_FAMILY_STORAGE_KEY);
78+
if (raw === "dm-sans" || raw === "inter" || raw === "plus-jakarta-sans") {
79+
return raw;
80+
}
81+
return "inter";
82+
}
83+
84+
const FONT_FAMILY_MAP: Record<FontFamily, string> = {
85+
inter: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
86+
"dm-sans": '"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
87+
"plus-jakarta-sans":
88+
'"Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
89+
};
90+
91+
function applyFont(fontFamily?: FontFamily) {
92+
const font = fontFamily ?? getStoredFontFamily();
93+
document.documentElement.style.setProperty("--font-ui", FONT_FAMILY_MAP[font]);
94+
}
95+
6096
function applyTheme(theme: Theme, suppressTransitions = false) {
6197
if (suppressTransitions) {
6298
document.documentElement.classList.add("no-transitions");
@@ -78,6 +114,9 @@ function applyTheme(theme: Theme, suppressTransitions = false) {
78114
document.documentElement.classList.add(`theme-${colorTheme}`);
79115
}
80116

117+
// Apply font family
118+
applyFont();
119+
81120
syncDesktopTheme(theme);
82121
if (suppressTransitions) {
83122
// Force a reflow so the no-transitions class takes effect before removal
@@ -110,17 +149,19 @@ function getSnapshot(): ThemeSnapshot {
110149
const theme = getStored();
111150
const systemDark = theme === "system" ? getSystemDark() : false;
112151
const colorTheme = getStoredColorTheme();
152+
const fontFamily = getStoredFontFamily();
113153

114154
if (
115155
lastSnapshot &&
116156
lastSnapshot.theme === theme &&
117157
lastSnapshot.systemDark === systemDark &&
118-
lastSnapshot.colorTheme === colorTheme
158+
lastSnapshot.colorTheme === colorTheme &&
159+
lastSnapshot.fontFamily === fontFamily
119160
) {
120161
return lastSnapshot;
121162
}
122163

123-
lastSnapshot = { theme, systemDark, colorTheme };
164+
lastSnapshot = { theme, systemDark, colorTheme, fontFamily };
124165
return lastSnapshot;
125166
}
126167

@@ -137,7 +178,11 @@ function subscribe(listener: () => void): () => void {
137178

138179
// Listen for storage changes from other tabs
139180
const handleStorage = (e: StorageEvent) => {
140-
if (e.key === STORAGE_KEY || e.key === COLOR_THEME_STORAGE_KEY) {
181+
if (
182+
e.key === STORAGE_KEY ||
183+
e.key === COLOR_THEME_STORAGE_KEY ||
184+
e.key === FONT_FAMILY_STORAGE_KEY
185+
) {
141186
applyTheme(getStored(), true);
142187
emitChange();
143188
}
@@ -155,6 +200,7 @@ export function useTheme() {
155200
const snapshot = useSyncExternalStore(subscribe, getSnapshot);
156201
const theme = snapshot.theme;
157202
const colorTheme = snapshot.colorTheme;
203+
const fontFamily = snapshot.fontFamily;
158204

159205
const resolvedTheme: "light" | "dark" =
160206
theme === "system" ? (snapshot.systemDark ? "dark" : "light") : theme;
@@ -171,10 +217,24 @@ export function useTheme() {
171217
emitChange();
172218
}, []);
173219

220+
const setFontFamily = useCallback((next: FontFamily) => {
221+
localStorage.setItem(FONT_FAMILY_STORAGE_KEY, next);
222+
applyFont(next);
223+
emitChange();
224+
}, []);
225+
174226
// Keep DOM in sync on mount/change
175227
useEffect(() => {
176228
applyTheme(theme);
177229
}, [theme]);
178230

179-
return { theme, setTheme, resolvedTheme, colorTheme, setColorTheme } as const;
231+
return {
232+
theme,
233+
setTheme,
234+
resolvedTheme,
235+
colorTheme,
236+
setColorTheme,
237+
fontFamily,
238+
setFontFamily,
239+
} as const;
180240
}

apps/web/src/index.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,15 @@
122122
}
123123

124124
body {
125-
font-family:
126-
"DM Sans",
125+
font-family: var(
126+
--font-ui,
127+
"Inter",
127128
-apple-system,
128129
BlinkMacSystemFont,
129130
"Segoe UI",
130131
system-ui,
131-
sans-serif;
132+
sans-serif
133+
);
132134
margin: 0;
133135
padding: 0;
134136
min-height: 100vh;

apps/web/src/routes/_chat.settings.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { SidebarInset } from "../components/ui/sidebar";
3434
import { Tooltip, TooltipPopup, TooltipTrigger } from "../components/ui/tooltip";
3535
import { resolveAndPersistPreferredEditor } from "../editorPreferences";
3636
import { isElectron } from "../env";
37-
import { useTheme, COLOR_THEMES } from "../hooks/useTheme";
37+
import { useTheme, COLOR_THEMES, FONT_FAMILIES } from "../hooks/useTheme";
3838
import {
3939
environmentVariablesQueryKeys,
4040
globalEnvironmentVariablesQueryOptions,
@@ -209,7 +209,7 @@ function getErrorMessage(error: unknown): string {
209209
}
210210

211211
function SettingsRouteView() {
212-
const { theme, setTheme, colorTheme, setColorTheme } = useTheme();
212+
const { theme, setTheme, colorTheme, setColorTheme, fontFamily, setFontFamily } = useTheme();
213213
const { settings, defaults, updateSettings, resetSettings } = useAppSettings();
214214
const serverConfigQuery = useQuery(serverConfigQueryOptions());
215215
const queryClient = useQueryClient();
@@ -300,6 +300,7 @@ function SettingsRouteView() {
300300
const changedSettingLabels = [
301301
...(theme !== "system" ? ["Theme"] : []),
302302
...(colorTheme !== "default" ? ["Color theme"] : []),
303+
...(fontFamily !== "inter" ? ["Font"] : []),
303304
...(settings.timestampFormat !== defaults.timestampFormat ? ["Time format"] : []),
304305
...(settings.diffWordWrap !== defaults.diffWordWrap ? ["Diff line wrapping"] : []),
305306
...(settings.enableAssistantStreaming !== defaults.enableAssistantStreaming
@@ -445,6 +446,7 @@ function SettingsRouteView() {
445446

446447
setTheme("system");
447448
setColorTheme("default");
449+
setFontFamily("inter");
448450
resetSettings();
449451
setOpenInstallProviders({
450452
codex: false,
@@ -571,6 +573,39 @@ function SettingsRouteView() {
571573
}
572574
/>
573575

576+
<SettingsRow
577+
title="Font"
578+
description="Choose the typeface for the interface."
579+
resetAction={
580+
fontFamily !== "inter" ? (
581+
<SettingResetButton label="font" onClick={() => setFontFamily("inter")} />
582+
) : null
583+
}
584+
control={
585+
<Select
586+
value={fontFamily}
587+
onValueChange={(value) => {
588+
const match = FONT_FAMILIES.find((f) => f.id === value);
589+
if (!match) return;
590+
setFontFamily(match.id);
591+
}}
592+
>
593+
<SelectTrigger className="w-full sm:w-40" aria-label="Font family">
594+
<SelectValue>
595+
{FONT_FAMILIES.find((f) => f.id === fontFamily)?.label ?? "Inter"}
596+
</SelectValue>
597+
</SelectTrigger>
598+
<SelectPopup align="end" alignItemWithTrigger={false}>
599+
{FONT_FAMILIES.map((f) => (
600+
<SelectItem hideIndicator key={f.id} value={f.id}>
601+
{f.label}
602+
</SelectItem>
603+
))}
604+
</SelectPopup>
605+
</Select>
606+
}
607+
/>
608+
574609
<SettingsRow
575610
title="Window opacity"
576611
description="Adjust the transparency of the entire application window."

0 commit comments

Comments
 (0)