mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[aidan] ux/font-stack: unify font families via shared typography module (#116)
This commit is contained in:
@@ -38,6 +38,9 @@
|
||||
<link rel="dns-prefetch" href="https://api.openswarm.com" />
|
||||
<link rel="dns-prefetch" href="https://api.github.com" />
|
||||
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Newsreader:opsz,wght@6..72,400;6..72,500;6..72,600;6..72,700&display=swap" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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 = () => {
|
||||
<Typography
|
||||
sx={{
|
||||
color: c.text.secondary,
|
||||
fontFamily: font.detail,
|
||||
fontSize: '0.9rem',
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.2,
|
||||
letterSpacing: 0,
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -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<StepCardProps> = ({
|
||||
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<CelebrationProps> = ({ 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<typeof useClaud
|
||||
fontSize: 16,
|
||||
fontWeight: 600,
|
||||
color: c.text.primary,
|
||||
fontFamily: '"Charter", Georgia, serif',
|
||||
fontFamily: font.heading,
|
||||
}}
|
||||
>
|
||||
You're all set up
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Props> = ({ 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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
<>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ fontFamily: '"Charter", Georgia, serif', fontWeight: 500, mb: 1 }}
|
||||
sx={{ fontFamily: font.heading, fontWeight: 500, mb: 1 }}
|
||||
>
|
||||
Check your inbox
|
||||
</Typography>
|
||||
@@ -272,7 +273,7 @@ export default function SignInDialog({ onClose }: { onClose: () => void }): JSX.
|
||||
<>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ fontFamily: '"Charter", Georgia, serif', fontWeight: 500, mb: 1 }}
|
||||
sx={{ fontFamily: font.heading, fontWeight: 500, mb: 1 }}
|
||||
>
|
||||
Sign in to OpenSwarm
|
||||
</Typography>
|
||||
|
||||
@@ -1168,7 +1168,7 @@ const MessageBubble: React.FC<Props> = React.memo(({ message, editing = false, o
|
||||
{message.images && message.images.length > 0 && (
|
||||
<MessageImageThumbnails images={message.images} c={c} />
|
||||
)}
|
||||
<Typography sx={{ color: c.text.primary, fontSize: '0.875rem', lineHeight: 1.6, overflowWrap: 'anywhere', wordBreak: 'break-word', whiteSpace: 'pre-wrap' }}>
|
||||
<Typography sx={{ color: c.text.primary, fontSize: '0.875rem', fontFamily: c.font.serif, lineHeight: 1.6, overflowWrap: 'anywhere', wordBreak: 'break-word', whiteSpace: 'pre-wrap' }}>
|
||||
{renderUserTextWithPills(displayText, c)}
|
||||
</Typography>
|
||||
<AttachedContextSection elements={selectedElements} message={message} c={c} />
|
||||
@@ -1180,9 +1180,11 @@ const MessageBubble: React.FC<Props> = 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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ color: c.text.secondary, fontSize: '1.3rem', fontWeight: 500, mb: 2.2, textAlign: 'center' }}>
|
||||
<Typography sx={{ color: c.text.primary, fontFamily: font.display, fontSize: '1.5rem', fontWeight: 500, lineHeight: 1.1, letterSpacing: 0, mb: 2.5, textAlign: 'center' }}>
|
||||
What do you want done?
|
||||
</Typography>
|
||||
|
||||
@@ -70,9 +71,6 @@ const DashboardEmptyState: React.FC<{
|
||||
transition={{ duration: 0.2 }}
|
||||
style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }}
|
||||
>
|
||||
<Typography sx={{ color: c.text.ghost, fontSize: '0.9rem', mb: 1.4 }}>
|
||||
pick one, or just tell me below
|
||||
</Typography>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 1.2, width: '100%', maxWidth: 460 }}>
|
||||
{STARTER_CATEGORIES.map((cat) => (
|
||||
<Box
|
||||
@@ -85,9 +83,9 @@ const DashboardEmptyState: React.FC<{
|
||||
borderRadius: 2.5,
|
||||
border: `1px solid ${c.border.medium}`,
|
||||
background: c.bg.surface,
|
||||
color: c.text.secondary,
|
||||
fontSize: '0.98rem', fontWeight: 500,
|
||||
cursor: 'pointer', fontFamily: 'inherit',
|
||||
color: c.text.primary,
|
||||
fontSize: '1.05rem', fontWeight: 400, lineHeight: 1.25,
|
||||
cursor: 'pointer', fontFamily: font.paragraph,
|
||||
transition: 'background 150ms, border-color 150ms',
|
||||
'&:hover': { background: c.bg.elevated, borderColor: c.border.strong },
|
||||
}}
|
||||
@@ -134,11 +132,13 @@ const DashboardEmptyState: React.FC<{
|
||||
borderRadius: 2,
|
||||
border: `1px solid ${c.border.medium}`,
|
||||
background: c.bg.surface,
|
||||
color: c.text.secondary,
|
||||
fontSize: '0.95rem',
|
||||
color: c.text.primary,
|
||||
fontSize: '1.02rem',
|
||||
fontWeight: 400,
|
||||
lineHeight: 1.35,
|
||||
cursor: launching ? 'default' : 'pointer',
|
||||
opacity: launching ? 0.5 : 1,
|
||||
fontFamily: 'inherit',
|
||||
fontFamily: font.paragraph,
|
||||
transition: 'background 150ms, border-color 150ms',
|
||||
'&:hover': launching ? {} : { background: c.bg.elevated, borderColor: c.border.strong },
|
||||
}}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
Dashboard,
|
||||
} from '@/shared/state/dashboardsSlice';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { font } from '@/shared/styles/typography';
|
||||
import { byPreviewRecency } from '@/shared/previewOrder';
|
||||
|
||||
// Module-scope: auto-enter fires once per app boot; revisiting "/" later shows the picker normally.
|
||||
@@ -212,7 +213,7 @@ const DashboardSelection: React.FC = () => {
|
||||
</Box>
|
||||
) : dashboards.length === 0 ? (
|
||||
<Box sx={{ textAlign: 'center', py: 10, color: c.text.muted }}>
|
||||
<Typography sx={{ fontSize: '1.1rem', mb: 1 }}>
|
||||
<Typography sx={{ fontFamily: font.heading, fontSize: '1.1rem', mb: 1 }}>
|
||||
{search ? 'No dashboards match your search' : 'No dashboards yet'}
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: '0.85rem', color: c.text.tertiary }}>
|
||||
|
||||
@@ -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 = () => {
|
||||
<Box sx={{ p: 4, pb: 3, maxWidth: 1100, display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 0.5, flexShrink: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Typography sx={{ fontSize: '1.4rem', fontWeight: 700, color: c.text.primary, fontFamily: c.font.sans }}>
|
||||
<Typography sx={{ fontSize: '1.4rem', fontWeight: 700, color: c.text.primary, fontFamily: font.heading }}>
|
||||
{builderPreview.name || 'Untitled Skill'}
|
||||
</Typography>
|
||||
<Chip
|
||||
@@ -593,7 +594,7 @@ const Skills: React.FC = () => {
|
||||
) : selectedReg ? (
|
||||
<Box sx={{ p: 4, pb: 3, maxWidth: 1100, display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 0.5, flexShrink: 0 }}>
|
||||
<Typography sx={{ fontSize: '1.4rem', fontWeight: 700, color: c.text.primary, fontFamily: c.font.sans }}>
|
||||
<Typography sx={{ fontSize: '1.4rem', fontWeight: 700, color: c.text.primary, fontFamily: font.heading }}>
|
||||
{selectedReg.name}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
@@ -658,7 +659,7 @@ const Skills: React.FC = () => {
|
||||
<Box sx={{ p: 4, pb: 3, maxWidth: 1100, display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 0.5, flexShrink: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography sx={{ fontSize: '1.4rem', fontWeight: 700, color: c.text.primary, fontFamily: c.font.sans }}>
|
||||
<Typography sx={{ fontSize: '1.4rem', fontWeight: 700, color: c.text.primary, fontFamily: font.heading }}>
|
||||
{selectedLocal.name}
|
||||
</Typography>
|
||||
{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 },
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ color: c.text.primary, fontWeight: 600, fontFamily: c.font.sans }}>
|
||||
<DialogTitle sx={{ color: c.text.primary, fontWeight: 600, fontFamily: font.heading }}>
|
||||
{editingId ? 'Edit Skill' : 'New Skill'}
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2, pt: '8px !important' }}>
|
||||
|
||||
@@ -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 = () => {
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Charter, Georgia, serif',
|
||||
fontFamily: font.heading,
|
||||
fontSize: '2rem',
|
||||
fontWeight: 500,
|
||||
color: '#f5f5f5',
|
||||
|
||||
@@ -10,6 +10,7 @@ import AddIcon from '@mui/icons-material/Add';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { fetchOutputs, deleteOutput, Output } from '@/shared/state/outputsSlice';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { font } from '@/shared/styles/typography';
|
||||
import { byPreviewRecency } from '@/shared/previewOrder';
|
||||
import ViewCard from './ViewCard';
|
||||
import { Skeleton } from '@/app/components/feedback/Loading';
|
||||
@@ -143,7 +144,7 @@ const Views: React.FC = () => {
|
||||
color: c.text.muted,
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: '1.1rem', mb: 1 }}>
|
||||
<Typography sx={{ fontFamily: font.heading, fontSize: '1.1rem', mb: 1 }}>
|
||||
No apps yet
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: '0.85rem', color: c.text.tertiary }}>
|
||||
|
||||
@@ -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 }) => {
|
||||
<button onClick={() => step(-1)} style={{ width: 30, height: 30, borderRadius: 8, border: `1px solid rgba(${WC.inkRGB},0.12)`, background: WC.paper, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: WC.ink3 }}><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M15 6l-6 6 6 6" /></svg></button>
|
||||
<button onClick={() => step(1)} style={{ width: 30, height: 30, borderRadius: 8, border: `1px solid rgba(${WC.inkRGB},0.12)`, background: WC.paper, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: WC.ink3 }}><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M9 6l6 6-6 6" /></svg></button>
|
||||
</div>
|
||||
<h1 style={{ margin: 0, fontFamily: "'Newsreader',serif", fontSize: 22, fontWeight: 600, color: WC.ink, letterSpacing: '-0.01em' }}>{title}</h1>
|
||||
<h1 style={{ margin: 0, fontFamily: FONT_SERIF, fontSize: 22, fontWeight: 600, color: WC.ink, letterSpacing: '-0.01em' }}>{title}</h1>
|
||||
<div style={{ flex: 1 }} />
|
||||
<div style={{ display: 'flex', background: WC.inset, border: `1px solid ${WC.line}`, borderRadius: 9, padding: 3, gap: 2 }}>
|
||||
<button onClick={() => nav.setCalView('week')} style={tabBtn(nav.calView === 'week', WC)}>Week</button>
|
||||
@@ -117,7 +117,7 @@ const CalendarView: React.FC<{ nav: AppNav }> = ({ nav }) => {
|
||||
{dayPop && createPortal(
|
||||
<div onClick={() => setDayPop(null)} style={{ position: 'fixed', inset: 0, zIndex: 2147483600 }}>
|
||||
<div onClick={(e) => 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 }}>
|
||||
<div style={{ fontFamily: "'Newsreader',serif", fontSize: 15, fontWeight: 500, color: WC.ink, marginBottom: 10, padding: '0 2px' }}>{dayPop.title}</div>
|
||||
<div style={{ fontFamily: FONT_SERIF, fontSize: 15, fontWeight: 500, color: WC.ink, marginBottom: 10, padding: '0 2px' }}>{dayPop.title}</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{dayPop.runs.map((r, i) => (
|
||||
<div key={i} onClick={() => 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<GridProps> = ({ ref0, now, occByDay, dayKey, onSelect,
|
||||
<div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '8px 0 7px', borderLeft: `1px solid rgba(${WC.inkRGB},0.06)` }}>
|
||||
<span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, letterSpacing: '0.04em', color: WC.muted2 }}>{DOW[d.getDay()]}</span>
|
||||
<div style={{ width: 30, height: 30, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', marginTop: 3, flex: 'none', background: isToday ? WC.accent : 'transparent' }}>
|
||||
<span style={{ fontFamily: "'Newsreader',serif", fontSize: 18, fontWeight: 500, lineHeight: 1, color: isToday ? '#fff' : WC.ink }}>{d.getDate()}</span>
|
||||
<span style={{ fontFamily: FONT_SERIF, fontSize: 18, fontWeight: 500, lineHeight: 1, color: isToday ? '#fff' : WC.ink }}>{d.getDate()}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -90,7 +90,7 @@ const ComposeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', background: WC.page }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<div style={{ width: 14, height: 14, borderRadius: '50%', border: `2px solid rgba(${WC.inkRGB},0.15)`, borderTopColor: WC.accent, animation: 'os-spin 0.7s linear infinite' }} />
|
||||
<span style={{ fontFamily: "'Newsreader',serif", fontStyle: 'italic', fontSize: 14, color: WC.ink4 }}>Setting up your workflow…</span>
|
||||
<span style={{ fontFamily: FONT_SERIF, fontStyle: 'italic', fontSize: 14, color: WC.ink4 }}>Setting up your workflow…</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -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' }}
|
||||
>
|
||||
<Typewriter value={workflow.title || 'Untitled workflow'} enabled={workflow.auto_named !== false}>
|
||||
{(t) => (
|
||||
<span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontFamily: "'Newsreader',serif", fontSize: 21, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }}>{t}</span>
|
||||
<span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontFamily: FONT_SERIF, fontSize: 21, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }}>{t}</span>
|
||||
)}
|
||||
</Typewriter>
|
||||
</InlineEditableTitle>
|
||||
|
||||
@@ -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' }}
|
||||
>
|
||||
<Typewriter value={workflow.title || 'Untitled workflow'} enabled={workflow.auto_named !== false}>
|
||||
{(t) => (
|
||||
<span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontFamily: "'Newsreader',serif", fontSize: 25, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }}>{t}</span>
|
||||
<span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontFamily: FONT_SERIF, fontSize: 25, fontWeight: 500, color: WC.ink, letterSpacing: '-0.01em' }}>{t}</span>
|
||||
)}
|
||||
</Typewriter>
|
||||
</InlineEditableTitle>
|
||||
|
||||
@@ -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 && (
|
||||
<div style={{ marginTop: 22 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 11 }}>
|
||||
<span style={{ fontFamily: "'Newsreader',serif", fontSize: 18, fontWeight: 500, color: WC.ink }}>Ongoing runs</span>
|
||||
<span style={{ fontFamily: FONT_SERIF, fontSize: 18, fontWeight: 500, color: WC.ink }}>Ongoing runs</span>
|
||||
<div style={{ width: 7, height: 7, borderRadius: '50%', background: WC.accent, animation: 'os-pulse 1.1s ease-in-out infinite' }} />
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
@@ -131,7 +131,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
|
||||
{missed.length > 0 && (
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 11 }}>
|
||||
<span style={{ fontFamily: "'Newsreader',serif", fontSize: 18, fontWeight: 500, color: WC.ink }}>Missed</span>
|
||||
<span style={{ fontFamily: FONT_SERIF, fontSize: 18, fontWeight: 500, color: WC.ink }}>Missed</span>
|
||||
<span style={{ fontSize: 11, fontWeight: 600, color: WC.danger, background: WC.dangerBg, padding: '2px 8px', borderRadius: 999 }}>{missed.length}</span>
|
||||
<div style={{ flex: 1 }} />
|
||||
<button onClick={reRunAll} style={{ background: 'transparent', border: `1px solid rgba(${WC.inkRGB},0.14)`, borderRadius: 8, padding: '6px 12px', fontSize: 12, fontWeight: 600, color: WC.ink3, cursor: 'pointer' }}>Re-run all</button>
|
||||
@@ -164,7 +164,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
|
||||
)}
|
||||
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontFamily: "'Newsreader',serif", fontSize: 18, fontWeight: 500, color: WC.ink, marginBottom: 12 }}>Coming up</div>
|
||||
<div style={{ fontFamily: FONT_SERIF, fontSize: 18, fontWeight: 500, color: WC.ink, marginBottom: 12 }}>Coming up</div>
|
||||
{comingGroups.length === 0 && (
|
||||
<div style={{ fontSize: 13, color: WC.muted }}>Nothing scheduled in the next 7 days.</div>
|
||||
)}
|
||||
@@ -172,7 +172,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
|
||||
{comingGroups.map((g) => (
|
||||
<div key={g.key} style={{ display: 'flex', gap: 18, padding: '4px 0 16px' }}>
|
||||
<div style={{ width: 62, flex: 'none', textAlign: 'right', paddingTop: 2 }}>
|
||||
<div style={{ fontFamily: "'Newsreader',serif", fontSize: 26, fontWeight: 500, color: WC.ink, lineHeight: 1 }}>{g.dayNum}</div>
|
||||
<div style={{ fontFamily: FONT_SERIF, fontSize: 26, fontWeight: 500, color: WC.ink, lineHeight: 1 }}>{g.dayNum}</div>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10.5, letterSpacing: '0.04em', textTransform: 'uppercase', color: WC.muted2, marginTop: 4 }}>{g.dow}</div>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: WC.faint, marginTop: 3 }}>{g.countLabel}</div>
|
||||
</div>
|
||||
@@ -201,7 +201,7 @@ const HomeView: React.FC<{ nav: AppNav }> = ({ nav }) => {
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 18 }}>
|
||||
<div style={{ fontFamily: "'Newsreader',serif", fontSize: 18, fontWeight: 500, color: WC.ink, marginBottom: 12 }}>Recents</div>
|
||||
<div style={{ fontFamily: FONT_SERIF, fontSize: 18, fontWeight: 500, color: WC.ink, marginBottom: 12 }}>Recents</div>
|
||||
{recents.length === 0 && <div style={{ fontSize: 13, color: WC.muted }}>No runs yet.</div>}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
|
||||
{recents.map((r) => (
|
||||
|
||||
@@ -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 = () => {
|
||||
<div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', background: WC.paper }}>
|
||||
<div style={{ flex: 'none', padding: '22px 30px 14px', borderBottom: `1px solid ${WC.line}` }}>
|
||||
<div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, letterSpacing: '0.06em', textTransform: 'uppercase', color: WC.muted2, marginBottom: 5 }}>Deleted workflows</div>
|
||||
<h1 style={{ margin: 0, fontFamily: "'Newsreader',serif", fontSize: 29, fontWeight: 500, color: WC.ink, letterSpacing: '-0.015em' }}>Trash</h1>
|
||||
<h1 style={{ margin: 0, fontFamily: FONT_SERIF, fontSize: 29, fontWeight: 500, color: WC.ink, letterSpacing: '-0.015em' }}>Trash</h1>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, overflowY: 'auto', minHeight: 0, padding: '18px 30px 32px' }}>
|
||||
@@ -47,7 +47,7 @@ const TrashView: React.FC = () => {
|
||||
<div style={{ width: 46, height: 46, borderRadius: 12, background: WC.inset, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 14 }}>
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke={WC.faint} strokeWidth="1.7"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13" /></svg>
|
||||
</div>
|
||||
<div style={{ fontFamily: "'Newsreader',serif", fontSize: 18, color: WC.ink, marginBottom: 4 }}>Trash is empty</div>
|
||||
<div style={{ fontFamily: FONT_SERIF, fontSize: 18, color: WC.ink, marginBottom: 4 }}>Trash is empty</div>
|
||||
<div style={{ fontSize: 13, color: WC.muted, maxWidth: 300, lineHeight: 1.5 }}>Deleted workflows appear here. Restore them or remove them permanently.</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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)',
|
||||
};
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
Reference in New Issue
Block a user