/** * MUI theme - mirrors `reachy_mini_mobile_app/src/theme.ts` so the * emotions Space looks like a natural extension of the mobile shell * when embedded (same accent, radius, paper / canvas separation). * * Two prebuilt instances (light + dark) selected at runtime by * `App.tsx` based on the URL `?theme` query param, postMessage from a * host shell, or `prefers-color-scheme` when standalone. Keeping both * prebuilt avoids a visible flash when the user toggles their system * appearance. */ import { createTheme, type Theme } from '@mui/material/styles'; const ACCENT = '#FF9500'; const RADIUS = 12; const FONT_FAMILY = 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif'; function buildTheme(mode: 'light' | 'dark'): Theme { const isDark = mode === 'dark'; return createTheme({ palette: { mode, primary: { main: ACCENT }, background: { default: isDark ? '#101013' : '#fafafa', paper: isDark ? '#1a1a1a' : '#ffffff', }, text: { primary: isDark ? '#f5f5f5' : '#111111', secondary: isDark ? 'rgba(255,255,255,0.72)' : 'rgba(0,0,0,0.65)', }, divider: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)', }, typography: { fontFamily: FONT_FAMILY, button: { textTransform: 'none', fontWeight: 600 }, }, shape: { borderRadius: RADIUS }, components: { MuiCssBaseline: { styleOverrides: { html: { backgroundColor: isDark ? '#101013' : '#fafafa', minHeight: ['100vh', '100dvh'], }, body: { backgroundColor: isDark ? '#101013' : '#fafafa', minHeight: ['100vh', '100dvh'], margin: 0, // Emotions is a single-screen experience; lock body // scroll so a stray gesture on the joystick / video feed // never bounces the page. overscrollBehavior: 'none', WebkitTapHighlightColor: 'transparent', // App-style "unselectable" baseline: long-press shouldn't // pop iOS' text-selection / share callout, double-tap // shouldn't highlight a wedge label, etc. We're a control // surface, not a document. Form fields opt back in below // so future inputs keep their native copy / paste UX. userSelect: 'none', WebkitUserSelect: 'none', WebkitTouchCallout: 'none', '& input, & textarea, & [contenteditable="true"]': { userSelect: 'text', WebkitUserSelect: 'text', WebkitTouchCallout: 'default', }, }, '#root': { minHeight: ['100vh', '100dvh'], display: 'flex', flexDirection: 'column', }, }, }, MuiButton: { // App-wide default = outlined. Contained / filled buttons read // as a heavyweight "primary commitment" in MUI; in this app // even the main CTAs are mostly soft decisions (sign in, // retry, connect), so we lean on the lighter outlined chrome // and let the page composition do the hierarchy work via // size + position rather than fill weight. Call sites still // override (e.g. `variant="text"` for inline links) when they // need a quieter affordance. defaultProps: { disableElevation: true, variant: 'outlined' }, styleOverrides: { root: { borderRadius: RADIUS, paddingInline: 20, paddingBlock: 10 }, // Outlined buttons inherit the divider's neutral grey by // default, which makes a primary-coloured outlined button // look washed-out next to its text. Lift the border width // a touch and inherit the text colour so the outline // matches the label hue (primary CTA reads as primary). outlined: { borderWidth: 1.5 }, }, // MUI v9 removed variant-color combination slots like // `outlinedPrimary` from `styleOverrides`. The migration // path is the new `variants` API which matches on prop // combinations and works the same way at runtime. variants: [ { props: { variant: 'outlined', color: 'primary' }, style: ({ theme }) => ({ borderColor: theme.palette.primary.main, '&:hover': { borderWidth: 1.5, backgroundColor: `rgba(255, 149, 0, ${ theme.palette.mode === 'dark' ? 0.1 : 0.08 })`, }, }), }, ], }, MuiPaper: { styleOverrides: { root: { backgroundImage: 'none' }, }, }, MuiCard: { styleOverrides: { root: { borderRadius: RADIUS }, }, }, MuiTooltip: { styleOverrides: { tooltip: { fontSize: 12, paddingBlock: 6, paddingInline: 10, }, }, }, }, }); } export const lightTheme = buildTheme('light'); export const darkTheme = buildTheme('dark');