From 14a08a0d8ce7184fb7c7fefbe8977bc73dc6dc0b Mon Sep 17 00:00:00 2001 From: Aidan Date: Tue, 30 Jun 2026 19:39:02 -0700 Subject: [PATCH] [aidan] ux/font-stack: unify font families via shared typography module (#116) --- frontend/public/index.html | 3 + frontend/src/app/Main.tsx | 23 +++++-- .../src/app/components/Layout/AppShell.tsx | 4 +- .../components/Onboarding/OnboardingPanel.tsx | 7 ++- .../Onboarding/OnboardingRoadmapModal.tsx | 3 +- .../overlays/GlobalSearchPalette.tsx | 10 ++- .../app/components/overlays/SignInDialog.tsx | 5 +- .../pages/AgentChat/bubbles/MessageBubble.tsx | 4 +- .../tool-bubbles/AgentResponseBody.tsx | 2 +- .../Dashboard/canvas/DashboardEmptyState.tsx | 20 +++--- .../DashboardSelection/DashboardSelection.tsx | 3 +- frontend/src/app/pages/Skills/Skills.tsx | 15 ++--- frontend/src/app/pages/Views/ViewEditor.tsx | 3 +- frontend/src/app/pages/Views/Views.tsx | 3 +- .../app/pages/Workflows/app/CalendarView.tsx | 8 +-- .../app/pages/Workflows/app/ComposeView.tsx | 6 +- .../app/pages/Workflows/app/DetailView.tsx | 6 +- .../src/app/pages/Workflows/app/HomeView.tsx | 12 ++-- .../src/app/pages/Workflows/app/TrashView.tsx | 6 +- frontend/src/app/pages/Workflows/app/uiKit.ts | 4 +- frontend/src/shared/styles/claudeTokens.ts | 14 +++-- frontend/src/shared/styles/text.module.scss | 63 ------------------- frontend/src/shared/styles/typography.ts | 22 +++++++ 23 files changed, 115 insertions(+), 131 deletions(-) delete mode 100644 frontend/src/shared/styles/text.module.scss create mode 100644 frontend/src/shared/styles/typography.ts diff --git a/frontend/public/index.html b/frontend/public/index.html index 00e577f6..364fb262 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -38,6 +38,9 @@ + + + diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx index 2021e7a6..e32bcc49 100644 --- a/frontend/src/app/Main.tsx +++ b/frontend/src/app/Main.tsx @@ -100,12 +100,13 @@ function buildMuiTheme(c: ClaudeTokens, mode: 'light' | 'dark') { }, typography: { fontFamily: c.font.sans, - h1: { fontWeight: 600 }, - h2: { fontWeight: 600 }, - h3: { fontWeight: 600 }, - h5: { fontWeight: 600 }, - h6: { fontWeight: 600 }, - button: { textTransform: 'none' as const, fontWeight: 500 }, + h1: { fontFamily: c.font.sans, fontWeight: 700 }, + h2: { fontFamily: c.font.sans, fontWeight: 700 }, + h3: { fontFamily: c.font.sans, fontWeight: 600 }, + h4: { fontFamily: c.font.sans, fontWeight: 600 }, + h5: { fontFamily: c.font.sans, fontWeight: 600 }, + h6: { fontFamily: c.font.sans, fontWeight: 600 }, + button: { textTransform: 'none' as const, fontWeight: 600 }, }, shape: { borderRadius: c.radius.xl, @@ -113,12 +114,22 @@ function buildMuiTheme(c: ClaudeTokens, mode: 'light' | 'dark') { components: { MuiCssBaseline: { styleOverrides: { + html: { + fontFamily: c.font.sans, + }, body: { backgroundColor: c.bg.page, color: c.text.primary, + fontFamily: c.font.sans, scrollbarWidth: 'thin', scrollbarColor: `${c.border.strong} transparent`, }, + '#root': { + fontFamily: c.font.sans, + }, + 'button, input, textarea, select': { + fontFamily: c.font.sans, + }, '*': { scrollbarWidth: 'thin', scrollbarColor: `${c.border.strong} transparent`, diff --git a/frontend/src/app/components/Layout/AppShell.tsx b/frontend/src/app/components/Layout/AppShell.tsx index a0eb15d6..7c26d13e 100644 --- a/frontend/src/app/components/Layout/AppShell.tsx +++ b/frontend/src/app/components/Layout/AppShell.tsx @@ -47,6 +47,7 @@ import { setInstalling } from '@/shared/state/updateSlice'; import { findBrowserByWebContentsId } from '@/shared/browserRegistry'; import { byPreviewRecency } from '@/shared/previewOrder'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { font } from '@/shared/styles/typography'; import { ErrorSlime } from '@/app/components/feedback/ErrorSlime'; const SIDEBAR_MIN = 160; @@ -584,9 +585,10 @@ const AppShell: React.FC = () => { diff --git a/frontend/src/app/components/Onboarding/OnboardingPanel.tsx b/frontend/src/app/components/Onboarding/OnboardingPanel.tsx index 77cf11a8..14f56103 100644 --- a/frontend/src/app/components/Onboarding/OnboardingPanel.tsx +++ b/frontend/src/app/components/Onboarding/OnboardingPanel.tsx @@ -10,6 +10,7 @@ import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import CloseIcon from '@mui/icons-material/Close'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { font } from '@/shared/styles/typography'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { useOnboardingProgress } from './hooks/useOnboardingProgress'; import { hasAnyAgentCompleted } from './steps/skipPredicates'; @@ -425,7 +426,7 @@ const StepCardBody: React.FC = ({ fontWeight: 600, color: c.text.primary, mb: 0.4, - fontFamily: '"Charter", Georgia, serif', + fontFamily: font.heading, }} > {step.title} @@ -669,7 +670,7 @@ const CelebrationView: React.FC = ({ step, accent }) => { fontSize: 16, fontWeight: 600, color: c.text.primary, - fontFamily: '"Charter", Georgia, serif', + fontFamily: font.heading, position: 'relative', display: 'inline-block', }} @@ -723,7 +724,7 @@ const AllDoneView: React.FC<{ accent: string; tokens: ReturnType You're all set up diff --git a/frontend/src/app/components/Onboarding/OnboardingRoadmapModal.tsx b/frontend/src/app/components/Onboarding/OnboardingRoadmapModal.tsx index 1c52a8d4..82ecd760 100644 --- a/frontend/src/app/components/Onboarding/OnboardingRoadmapModal.tsx +++ b/frontend/src/app/components/Onboarding/OnboardingRoadmapModal.tsx @@ -8,6 +8,7 @@ import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import LockIcon from '@mui/icons-material/Lock'; import CloseIcon from '@mui/icons-material/Close'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { font } from '@/shared/styles/typography'; import { useOnboardingProgress } from './hooks/useOnboardingProgress'; import { STAGE_GROUPS, STEPS, findStepById } from './steps'; import { useUnlockedStepIds, unlockHintFor } from './steps/stepUnlock'; @@ -101,7 +102,7 @@ const OnboardingRoadmapModal: React.FC = () => { sx={{ fontSize: 16, fontWeight: 600, - fontFamily: '"Charter", Georgia, serif', + fontFamily: font.heading, }} > Your roadmap diff --git a/frontend/src/app/components/overlays/GlobalSearchPalette.tsx b/frontend/src/app/components/overlays/GlobalSearchPalette.tsx index 4b1ab44f..afd9623b 100644 --- a/frontend/src/app/components/overlays/GlobalSearchPalette.tsx +++ b/frontend/src/app/components/overlays/GlobalSearchPalette.tsx @@ -51,9 +51,8 @@ const ACTIONS: ActionResult[] = [ { kind: 'action', id: 'new-dashboard', name: 'New dashboard', keywords: 'create board canvas workspace' }, { kind: 'action', id: 'settings', name: 'Open Settings', keywords: 'preferences general theme options' }, { kind: 'action', id: 'settings-models', name: 'Connect a model', keywords: 'settings models api key provider subscription' }, - { kind: 'action', id: 'go-skills', name: 'Go to Skills', keywords: 'customize skills' }, - { kind: 'action', id: 'go-actions', name: 'Go to Actions', keywords: 'customize tools actions mcp' }, - { kind: 'action', id: 'go-modes', name: 'Go to Modes', keywords: 'customize modes' }, + { kind: 'action', id: 'go-skills', name: 'Go to Skills', keywords: 'customize skills settings' }, + { kind: 'action', id: 'go-actions', name: 'Go to Tools', keywords: 'customize tools actions mcp settings' }, { kind: 'action', id: 'go-apps', name: 'Go to Apps', keywords: 'apps mini app' }, { kind: 'action', id: 'all-dashboards', name: 'All dashboards', keywords: 'overview picker browse boards' }, ]; @@ -150,9 +149,8 @@ const GlobalSearchPalette: React.FC = ({ open, onClose }) => { break; case 'settings': dispatch(openSettingsModal()); break; case 'settings-models': dispatch(openSettingsModal('models')); break; - case 'go-skills': navigate('/skills'); break; - case 'go-actions': navigate('/actions'); break; - case 'go-modes': navigate('/modes'); break; + case 'go-skills': dispatch(openSettingsModal('skills')); break; + case 'go-actions': dispatch(openSettingsModal('tools')); break; case 'go-apps': navigate('/apps'); break; case 'all-dashboards': navigate('/'); break; } diff --git a/frontend/src/app/components/overlays/SignInDialog.tsx b/frontend/src/app/components/overlays/SignInDialog.tsx index d2cc88b1..d56c56c7 100644 --- a/frontend/src/app/components/overlays/SignInDialog.tsx +++ b/frontend/src/app/components/overlays/SignInDialog.tsx @@ -17,6 +17,7 @@ import CloseIcon from '@mui/icons-material/Close'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { activateSignin, fetchSettings } from '@/shared/state/settingsSlice'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { font } from '@/shared/styles/typography'; import { OPENSWARM_DEFAULT_PROXY_URL } from '@/shared/config'; import { report } from '@/shared/serviceClient'; @@ -187,7 +188,7 @@ export default function SignInDialog({ onClose }: { onClose: () => void }): JSX. <> Check your inbox @@ -272,7 +273,7 @@ export default function SignInDialog({ onClose }: { onClose: () => void }): JSX. <> Sign in to OpenSwarm diff --git a/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx b/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx index e6473364..6e01c324 100644 --- a/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx +++ b/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx @@ -1168,7 +1168,7 @@ const MessageBubble: React.FC = React.memo(({ message, editing = false, o {message.images && message.images.length > 0 && ( )} - + {renderUserTextWithPills(displayText, c)} @@ -1180,9 +1180,11 @@ const MessageBubble: React.FC = React.memo(({ message, editing = false, o sx={{ color: c.text.secondary, fontSize: '0.875rem', + fontFamily: c.font.serif, lineHeight: 1.7, overflowWrap: 'anywhere', wordBreak: 'break-word', + '& h1, & h2, & h3, & h4, & h5, & h6': { fontFamily: c.font.sans }, '& p': { m: 0, mb: 1, '&:last-child': { mb: 0 } }, '& pre': { bgcolor: c.bg.secondary, diff --git a/frontend/src/app/pages/AgentChat/tool-bubbles/AgentResponseBody.tsx b/frontend/src/app/pages/AgentChat/tool-bubbles/AgentResponseBody.tsx index a2740e2c..1e3eb7b6 100644 --- a/frontend/src/app/pages/AgentChat/tool-bubbles/AgentResponseBody.tsx +++ b/frontend/src/app/pages/AgentChat/tool-bubbles/AgentResponseBody.tsx @@ -18,7 +18,7 @@ export const AgentResponseBody: React.FC<{ open: boolean; markdown: string }> = overflowY: 'auto', overflowX: 'hidden', color: c.text.secondary, - fontFamily: c.font.sans, + fontFamily: c.font.serif, fontSize: '0.78rem', lineHeight: 1.65, overflowWrap: 'anywhere', diff --git a/frontend/src/app/pages/Dashboard/canvas/DashboardEmptyState.tsx b/frontend/src/app/pages/Dashboard/canvas/DashboardEmptyState.tsx index 6d544a83..22df537a 100644 --- a/frontend/src/app/pages/Dashboard/canvas/DashboardEmptyState.tsx +++ b/frontend/src/app/pages/Dashboard/canvas/DashboardEmptyState.tsx @@ -4,6 +4,7 @@ import Typography from '@mui/material/Typography'; import { motion, AnimatePresence } from 'framer-motion'; import { ArrowLeft } from 'lucide-react'; import type { ClaudeTokens } from '@/shared/styles/claudeTokens'; +import { font } from '@/shared/styles/typography'; import { useAppSelector } from '@/shared/hooks'; import { hasModelConnected, @@ -54,7 +55,7 @@ const DashboardEmptyState: React.FC<{ pointerEvents: 'none', }} > - + What do you want done? @@ -70,9 +71,6 @@ const DashboardEmptyState: React.FC<{ transition={{ duration: 0.2 }} style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }} > - - pick one, or just tell me below - {STARTER_CATEGORIES.map((cat) => ( { ) : dashboards.length === 0 ? ( - + {search ? 'No dashboards match your search' : 'No dashboards yet'} diff --git a/frontend/src/app/pages/Skills/Skills.tsx b/frontend/src/app/pages/Skills/Skills.tsx index 8399a2a9..fdae3e9f 100644 --- a/frontend/src/app/pages/Skills/Skills.tsx +++ b/frontend/src/app/pages/Skills/Skills.tsx @@ -35,6 +35,7 @@ import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { font } from '@/shared/styles/typography'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { fetchSkills, @@ -288,9 +289,9 @@ const Skills: React.FC = () => { overflow: 'auto', '&::-webkit-scrollbar': { width: 5 }, '&::-webkit-scrollbar-thumb': { background: c.border.medium, borderRadius: 3 }, - '& h1': { fontSize: '1.3rem', fontWeight: 700, color: c.text.primary, mt: 0, mb: 1.5, fontFamily: c.font.sans }, - '& h2': { fontSize: '1.1rem', fontWeight: 600, color: c.text.primary, mt: 2, mb: 1, fontFamily: c.font.sans }, - '& h3': { fontSize: '0.95rem', fontWeight: 600, color: c.text.primary, mt: 1.5, mb: 0.75, fontFamily: c.font.sans }, + '& h1': { fontSize: '1.3rem', fontWeight: 700, color: c.text.primary, mt: 0, mb: 1.5, fontFamily: font.heading }, + '& h2': { fontSize: '1.1rem', fontWeight: 600, color: c.text.primary, mt: 2, mb: 1, fontFamily: font.heading }, + '& h3': { fontSize: '0.95rem', fontWeight: 600, color: c.text.primary, mt: 1.5, mb: 0.75, fontFamily: font.heading }, '& p': { fontSize: '0.88rem', color: c.text.secondary, lineHeight: 1.7, mb: 1.5 }, '& ul, & ol': { pl: 2.5, mb: 1.5, '& li': { fontSize: '0.88rem', color: c.text.secondary, lineHeight: 1.7, mb: 0.5 } }, '& code': { fontFamily: c.font.mono, fontSize: '0.82rem', bgcolor: 'rgba(0,0,0,0.04)', px: 0.5, py: 0.15, borderRadius: `${c.radius.xs}px` }, @@ -532,7 +533,7 @@ const Skills: React.FC = () => { - + {builderPreview.name || 'Untitled Skill'} { ) : selectedReg ? ( - + {selectedReg.name} @@ -658,7 +659,7 @@ const Skills: React.FC = () => { - + {selectedLocal.name} {selectedLocal.built_in && ( @@ -752,7 +753,7 @@ const Skills: React.FC = () => { sx: { bgcolor: c.bg.surface, backgroundImage: 'none', borderRadius: `${c.radius.lg}px`, border: `${c.border.width} solid ${c.border.subtle}`, boxShadow: c.shadow.lg }, }} > - + {editingId ? 'Edit Skill' : 'New Skill'} diff --git a/frontend/src/app/pages/Views/ViewEditor.tsx b/frontend/src/app/pages/Views/ViewEditor.tsx index 02621bea..79d62c62 100644 --- a/frontend/src/app/pages/Views/ViewEditor.tsx +++ b/frontend/src/app/pages/Views/ViewEditor.tsx @@ -4,6 +4,7 @@ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import CircularProgress from '@mui/material/CircularProgress'; import PixelBlast from '@/app/components/feedback/PixelBlast'; +import { font } from '@/shared/styles/typography'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import TextField from '@mui/material/TextField'; @@ -70,7 +71,7 @@ const InstallPlaceholder: React.FC = () => { > { color: c.text.muted, }} > - + No apps yet diff --git a/frontend/src/app/pages/Workflows/app/CalendarView.tsx b/frontend/src/app/pages/Workflows/app/CalendarView.tsx index 3a7a4282..fb08c6cb 100644 --- a/frontend/src/app/pages/Workflows/app/CalendarView.tsx +++ b/frontend/src/app/pages/Workflows/app/CalendarView.tsx @@ -4,7 +4,7 @@ import type { CSSProperties } from 'react'; import { useAppSelector } from '@/shared/hooks'; import { startOfWeek, startOfMonthGrid, addDays, sameDay } from '@/app/pages/Workflows/scheduleUtils'; import { useCalendarOccurrences } from './useCalendarOccurrences'; -import { colorForWorkflow, useWC, type WCPalette } from './uiKit'; +import { colorForWorkflow, useWC, FONT_SERIF, type WCPalette } from './uiKit'; import type { AppNav } from './types'; interface Occ { wfId: string; title: string; at: Date; color: string; } @@ -102,7 +102,7 @@ const CalendarView: React.FC<{ nav: AppNav }> = ({ nav }) => { -

{title}

+

{title}

@@ -117,7 +117,7 @@ const CalendarView: React.FC<{ nav: AppNav }> = ({ nav }) => { {dayPop && createPortal(
setDayPop(null)} style={{ position: 'fixed', inset: 0, zIndex: 2147483600 }}>
e.stopPropagation()} style={{ position: 'fixed', left: dayPop.x, top: dayPop.y, width: POP_W, maxHeight: 320, overflowY: 'auto', background: WC.paper, border: `1px solid ${WC.line2}`, borderRadius: WC.radius.lg, boxShadow: WC.shadow.lg, padding: 12 }}> -
{dayPop.title}
+
{dayPop.title}
{dayPop.runs.map((r, i) => (
selectFromPop(r.wfId)} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '8px 7px', borderRadius: 8, cursor: 'pointer' }}> @@ -259,7 +259,7 @@ const WeekGrid: React.FC = ({ ref0, now, occByDay, dayKey, onSelect,
{DOW[d.getDay()]}
- {d.getDate()} + {d.getDate()}
); diff --git a/frontend/src/app/pages/Workflows/app/ComposeView.tsx b/frontend/src/app/pages/Workflows/app/ComposeView.tsx index f38e7a6b..3a678f84 100644 --- a/frontend/src/app/pages/Workflows/app/ComposeView.tsx +++ b/frontend/src/app/pages/Workflows/app/ComposeView.tsx @@ -90,7 +90,7 @@ const ComposeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
- Setting up your workflow… + Setting up your workflow…
); @@ -105,11 +105,11 @@ const ComposeView: React.FC<{ nav: AppNav }> = ({ nav }) => { value={workflow.title || ''} onCommit={(t) => patch(workflow, { title: t, auto_named: false })} placeholder="Untitled workflow" - sx={{ flex: 1, minWidth: 0, fontFamily: "'Newsreader',serif", fontSize: 21, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }} + sx={{ flex: 1, minWidth: 0, fontFamily: FONT_SERIF, fontSize: 21, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }} > {(t) => ( - {t} + {t} )} diff --git a/frontend/src/app/pages/Workflows/app/DetailView.tsx b/frontend/src/app/pages/Workflows/app/DetailView.tsx index cf0e290b..121b0ad4 100644 --- a/frontend/src/app/pages/Workflows/app/DetailView.tsx +++ b/frontend/src/app/pages/Workflows/app/DetailView.tsx @@ -7,7 +7,7 @@ import { askRun } from './api'; import AgentChat from '@/app/pages/AgentChat/AgentChat'; import InlineEditableTitle from '@/app/components/InlineEditableTitle'; import { Typewriter } from '@/app/components/feedback/Animated'; -import { useWC, colorForWorkflow, statusChip } from './uiKit'; +import { useWC, colorForWorkflow, statusChip, FONT_SERIF } from './uiKit'; import { isRunning, runContextChip } from './model'; import { useEditAgentSession } from './useEditAgentSession'; import { useWorkflowPatch } from './useWorkflowPatch'; @@ -63,11 +63,11 @@ const DetailView: React.FC<{ workflowId: string; nav: AppNav }> = ({ workflowId value={workflow.title || ''} onCommit={(t) => patch(workflow, { title: t, auto_named: false })} placeholder="Untitled workflow" - sx={{ flex: 1, minWidth: 0, fontFamily: "'Newsreader',serif", fontSize: 25, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }} + sx={{ flex: 1, minWidth: 0, fontFamily: FONT_SERIF, fontSize: 25, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }} > {(t) => ( - {t} + {t} )} diff --git a/frontend/src/app/pages/Workflows/app/HomeView.tsx b/frontend/src/app/pages/Workflows/app/HomeView.tsx index 0dad456b..fcf3f20b 100644 --- a/frontend/src/app/pages/Workflows/app/HomeView.tsx +++ b/frontend/src/app/pages/Workflows/app/HomeView.tsx @@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { runMissedRuns, dismissMissedRuns } from '@/shared/state/missedRunsSlice'; import { openWorkflowMonitor } from '@/shared/state/dashboardLayoutSlice'; import { useCalendarOccurrences } from './useCalendarOccurrences'; -import { colorForWorkflow, useWC, statusChip, statusDot } from './uiKit'; +import { colorForWorkflow, useWC, statusChip, statusDot, FONT_SERIF } from './uiKit'; import { clockOf, whenText } from './model'; import WorkflowTitle from './WorkflowTitle'; import type { AppNav } from './types'; @@ -98,7 +98,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => { {ongoing.length > 0 && (
- Ongoing runs + Ongoing runs
@@ -131,7 +131,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => { {missed.length > 0 && (
- Missed + Missed {missed.length}
@@ -164,7 +164,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => { )}
-
Coming up
+
Coming up
{comingGroups.length === 0 && (
Nothing scheduled in the next 7 days.
)} @@ -172,7 +172,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => { {comingGroups.map((g) => (
-
{g.dayNum}
+
{g.dayNum}
{g.dow}
{g.countLabel}
@@ -201,7 +201,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
-
Recents
+
Recents
{recents.length === 0 &&
No runs yet.
}
{recents.map((r) => ( diff --git a/frontend/src/app/pages/Workflows/app/TrashView.tsx b/frontend/src/app/pages/Workflows/app/TrashView.tsx index cb0e3d9c..e4b93eb3 100644 --- a/frontend/src/app/pages/Workflows/app/TrashView.tsx +++ b/frontend/src/app/pages/Workflows/app/TrashView.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { fetchDeletedWorkflows, restoreWorkflow, purgeWorkflow } from '@/shared/state/workflowsSlice'; -import { colorForWorkflow, useWC } from './uiKit'; +import { colorForWorkflow, useWC, FONT_SERIF } from './uiKit'; import { whenText } from './model'; const TrashView: React.FC = () => { @@ -23,7 +23,7 @@ const TrashView: React.FC = () => {
Deleted workflows
-

Trash

+

Trash

@@ -47,7 +47,7 @@ const TrashView: React.FC = () => {
-
Trash is empty
+
Trash is empty
Deleted workflows appear here. Restore them or remove them permanently.
) diff --git a/frontend/src/app/pages/Workflows/app/uiKit.ts b/frontend/src/app/pages/Workflows/app/uiKit.ts index e24c538a..6864112b 100644 --- a/frontend/src/app/pages/Workflows/app/uiKit.ts +++ b/frontend/src/app/pages/Workflows/app/uiKit.ts @@ -104,9 +104,7 @@ export function useWC(): WCPalette { }; } -export const FONT_SERIF = "'Newsreader', Georgia, serif"; -export const FONT_SANS = "'Hanken Grotesk', system-ui, sans-serif"; -export const FONT_MONO = "'JetBrains Mono', ui-monospace, monospace"; +export { FONT_SERIF, FONT_SANS, FONT_MONO } from '@/shared/styles/typography'; // Stable per-workflow color: the backend has no color field, so derive a vivid-but-deterministic swatch from the id. Same id always lands the same hue, so dots/bars stay consistent across panes without persistence. export const WORKFLOW_PALETTE = [ diff --git a/frontend/src/shared/styles/claudeTokens.ts b/frontend/src/shared/styles/claudeTokens.ts index 21daff46..82c978ac 100644 --- a/frontend/src/shared/styles/claudeTokens.ts +++ b/frontend/src/shared/styles/claudeTokens.ts @@ -1,3 +1,5 @@ +import { FONT_SANS, FONT_SERIF, FONT_MONO } from './typography'; + export interface ClaudeTokens { bg: { page: string; surface: string; elevated: string; secondary: string; inverse: string }; text: { primary: string; secondary: string; tertiary: string; muted: string; inverse: string; ghost: string }; @@ -7,7 +9,7 @@ export interface ClaudeTokens { radius: { xs: number; sm: number; md: number; lg: number; xl: number; full: number }; status: { success: string; successBg: string; warning: string; warningBg: string; error: string; errorBg: string; info: string; infoBg: string }; user: { bubble: string }; - font: { sans: string; mono: string }; + font: { sans: string; serif: string; mono: string }; transition: string; } @@ -56,8 +58,9 @@ export const lightTokens: ClaudeTokens = { }, user: { bubble: '#DDD9CE' }, font: { - sans: '"Anthropic Sans", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif', - mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace', + sans: FONT_SANS, + serif: FONT_SERIF, + mono: FONT_MONO, }, transition: 'all 150ms cubic-bezier(0.165, 0.85, 0.45, 1)', }; @@ -107,8 +110,9 @@ export const darkTokens: ClaudeTokens = { }, user: { bubble: '#393937' }, font: { - sans: '"Anthropic Sans", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif', - mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace', + sans: FONT_SANS, + serif: FONT_SERIF, + mono: FONT_MONO, }, transition: 'all 150ms cubic-bezier(0.165, 0.85, 0.45, 1)', }; diff --git a/frontend/src/shared/styles/text.module.scss b/frontend/src/shared/styles/text.module.scss deleted file mode 100644 index 8e218db1..00000000 --- a/frontend/src/shared/styles/text.module.scss +++ /dev/null @@ -1,63 +0,0 @@ -@use '@/shared/styles/color.module.scss' as g-color; -@use '@/shared/styles/utils.module.scss' as utils; - - -$font-map: ( - 'default': 'Inter', - 'secondary': 'Times New Roman' -); -@function font($mode: 'default') { - @return utils.get-style( - $function-map: $font-map, - $mode: $mode - ); -} - -$size-map: ( - 'small': 12px, - 'small-medium': 14px, - 'medium': 16px, - 'large': 20px, - 'title': 30px, -); -@function size($mode: 'default') { - @return utils.get-style( - $function-map: $size-map, - $mode: $mode - ); -} - -$weight-map: ( - 'small': 400, - 'medium': 500, - 'large': 600, - 'title': 700, -); -@function weight($mode: 'default') { - @return utils.get-style( - $function-map: $weight-map, - $mode: $mode - ); -} - -$text-map: ( - 'default': ( - font-family: 'Inter', - font-size: 16px, - font-weight: 400, - color: g-color.text('light-1'), - ), - 'title': ( - font-family: 'Inter', - font-size: 40px, - font-weight: 700, - color: g-color.accent('blue-1'), - line-height: 100%, - ) -); -@mixin text($mode: 'default') { - @include utils.apply-style-map( - $mixin-map: $text-map, - $mode: $mode - ); -} \ No newline at end of file diff --git a/frontend/src/shared/styles/typography.ts b/frontend/src/shared/styles/typography.ts new file mode 100644 index 00000000..fd81e12d --- /dev/null +++ b/frontend/src/shared/styles/typography.ts @@ -0,0 +1,22 @@ +// The single source of truth for font families. If a family string lives anywhere +// else in the app, that's a bug waiting to drift; point it here instead. +export const FONT_SANS = "'Hanken Grotesk', Arial, sans-serif"; +export const FONT_SERIF = "'Newsreader', Georgia, serif"; +export const FONT_MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace"; + +// Ask for text by ROLE, not by family. Sizes and weights stay at the call site. +export const font: { + display: string; + detail: string; + paragraph: string; + heading: string; + body: string; + mono: string; +} = { + display: FONT_SANS, + detail: FONT_SANS, + paragraph: FONT_SERIF, + heading: FONT_SANS, + body: FONT_SANS, + mono: FONT_MONO, +};