emotions / src /theme.ts
tfrere's picture
tfrere HF Staff
chore(deps): upgrade MUI to v9.0.1
a9721bc
/**
* 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');