MUI Patterns Skill
Purpose
Master Material-UI v6 component library usage, theming, and styling patterns specific to the music-app project. This skill covers dark theme implementation, responsive design, and Emotion-based styling.
Scope
- •MUI v6 component usage
- •Dark theme configuration
- •Emotion styling system (sx prop, styled components)
- •Responsive design with breakpoints
- •Icon integration
- •Component composition patterns
Key Concepts
MUI v6 Features
- •Emotion styling engine (replaces JSS)
- •
sxprop for inline styles (preferred) - •
styled()function for custom components - •Theme-aware styling
- •Responsive props and breakpoints
Project Theme
- •Dark theme by default
- •Configured in
src/theme/darkTheme.js - •Applied via
ThemeProviderin App.jsx - •All components must respect theme colors
Code Examples
Basic Component with MUI
javascript
import { Box, Typography, Button } from '@mui/material';
import { useTheme } from '@mui/material/styles';
function ExampleComponent() {
const theme = useTheme();
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
gap: 2, // theme.spacing(2)
p: 3, // padding: theme.spacing(3)
bgcolor: 'background.paper', // theme.palette.background.paper
borderRadius: 1
}}
>
<Typography variant="h4" color="text.primary">
Title
</Typography>
<Button variant="contained" color="primary">
Click Me
</Button>
</Box>
);
}
Responsive Design
javascript
<Box
sx={{
// Mobile first approach
display: 'flex',
flexDirection: 'column',
gap: 1,
// Tablet (sm: 600px+)
[theme.breakpoints.up('sm')]: {
flexDirection: 'row',
gap: 2
},
// Desktop (md: 900px+)
[theme.breakpoints.up('md')]: {
gap: 3,
p: 4
}
}}
>
{/* Content */}
</Box>
// Or use responsive object syntax
<Typography
variant="h4"
sx={{
fontSize: { xs: '1.5rem', sm: '2rem', md: '2.5rem' },
display: { xs: 'none', md: 'block' }
}}
>
Responsive Text
</Typography>
Dark Theme Configuration
javascript
// src/theme/darkTheme.js
import { createTheme } from '@mui/material/styles';
const darkTheme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#90caf9', // Light blue
},
secondary: {
main: '#f48fb1', // Pink
},
background: {
default: '#121212', // Dark background
paper: '#1e1e1e', // Elevated surface
},
text: {
primary: '#ffffff',
secondary: 'rgba(255, 255, 255, 0.7)',
},
},
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
},
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none', // Remove uppercase
},
},
},
},
});
export default darkTheme;
Styled Components (Reusable)
javascript
// src/components/shared/StyledPaper.jsx
import { styled } from '@mui/material/styles';
import { Paper } from '@mui/material';
const StyledPaper = styled(Paper)(({ theme }) => ({
padding: theme.spacing(3),
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shadows[3],
// Hover effect
'&:hover': {
boxShadow: theme.shadows[6],
},
// Responsive padding
[theme.breakpoints.down('sm')]: {
padding: theme.spacing(2),
},
}));
export default StyledPaper;
Icon Integration
javascript
import {
Favorite,
FavoriteBorder,
PlayArrow,
Pause,
Search,
Menu,
Close
} from '@mui/icons-material';
// Icon button
<IconButton
onClick={handleClick}
sx={{ color: 'primary.main' }}
>
<Favorite />
</IconButton>
// Icon with text
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<PlayArrow />
<Typography>Play Tune</Typography>
</Box>
Grid Layout
javascript
import { Grid, Box } from '@mui/material';
function TuneGrid({ tunes }) {
return (
<Grid container spacing={3}>
{tunes.map(tune => (
<Grid item xs={12} sm={6} md={4} key={tune.id}>
<TuneCard tune={tune} />
</Grid>
))}
</Grid>
);
}
Forms and Input
javascript
import { TextField, FormControl, InputLabel, Select, MenuItem } from '@mui/material';
function FilterForm() {
const [genre, setGenre] = useState('');
return (
<Box sx={{ display: 'flex', gap: 2, flexDirection: 'column' }}>
<TextField
fullWidth
label="Search"
variant="outlined"
sx={{
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: 'primary.main',
},
},
}}
/>
<FormControl fullWidth>
<InputLabel>Genre</InputLabel>
<Select
value={genre}
label="Genre"
onChange={(e) => setGenre(e.target.value)}
>
<MenuItem value="irish">Irish Traditional</MenuItem>
<MenuItem value="scottish">Scottish</MenuItem>
<MenuItem value="bluegrass">Bluegrass</MenuItem>
</Select>
</FormControl>
</Box>
);
}
Modal Pattern
javascript
import { Modal, Box, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
function CustomModal({ open, onClose, title, children }) {
return (
<Modal
open={open}
onClose={onClose}
sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
<Box
sx={{
bgcolor: 'background.paper',
boxShadow: 24,
p: 4,
borderRadius: 2,
maxWidth: '90vw',
maxHeight: '90vh',
overflow: 'auto',
position: 'relative'
}}
>
<IconButton
sx={{ position: 'absolute', right: 8, top: 8 }}
onClick={onClose}
>
<CloseIcon />
</IconButton>
<Typography variant="h5" mb={2}>{title}</Typography>
{children}
</Box>
</Modal>
);
}
Best Practices
✅ Do
- •Use
sxprop for inline styles
javascript
<Box sx={{ p: 2, bgcolor: 'background.paper' }} />
- •Use theme palette colors
javascript
<Typography sx={{ color: 'text.primary' }} />
- •Use spacing function
javascript
sx={{ gap: 2 }} // theme.spacing(2) = 16px
- •Responsive design
javascript
sx={{ display: { xs: 'none', md: 'block' } }}
- •Extract reusable styled components
javascript
const StyledCard = styled(Card)(({ theme }) => ({ ... }));
❌ Don't
- •Don't use inline styles
javascript
<Box style={{ padding: '16px' }} /> // Wrong!
- •Don't hardcode colors
javascript
<Box sx={{ color: '#fff' }} /> // Wrong!
- •Don't mix styling approaches
javascript
// Pick one: sx prop OR styled component, not both
- •Don't forget responsive design
javascript
<Box sx={{ width: '800px' }} /> // Wrong! Not responsive
- •Don't ignore theme
javascript
<Box sx={{ padding: '20px' }} /> // Wrong! Use spacing units
Common Patterns from Project
NavBar Pattern
javascript
<AppBar position="static" sx={{ bgcolor: 'background.paper' }}>
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
Music App
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
Card Pattern
javascript
<Card
sx={{
height: '100%',
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.2s',
'&:hover': {
transform: 'translateY(-4px)',
boxShadow: 3
}
}}
>
<CardContent sx={{ flexGrow: 1 }}>
<Typography variant="h6">{tune.title}</Typography>
</CardContent>
<CardActions>
<Button size="small">View</Button>
</CardActions>
</Card>
Integration Points
- •React Components: All UI components use MUI
- •Theme Provider: App.jsx wraps everything in ThemeProvider
- •Responsive Design: Mobile-first breakpoints throughout
- •Icons: @mui/icons-material for all icons
Common Issues
Issue: Colors not updating with theme
Solution: Use theme palette paths, not hardcoded colors
javascript
// Wrong
sx={{ color: '#fff' }}
// Right
sx={{ color: 'text.primary' }}
Issue: Spacing inconsistent
Solution: Use theme.spacing (via numeric sx values)
javascript
// Wrong
sx={{ padding: '16px' }}
// Right
sx={{ p: 2 }} // = theme.spacing(2) = 16px
Issue: Components not responsive
Solution: Use breakpoint syntax
javascript
sx={{
width: { xs: '100%', md: '50%' },
display: { xs: 'block', sm: 'flex' }
}}
Version Notes
- •MUI v6: Uses Emotion (not JSS)
- •Breaking changes from v5: Minimal, mostly internal
- •sx prop: Preferred over makeStyles (deprecated)
- •styled(): For complex, reusable components
Resources
- •MUI v6 Documentation: https://mui.com/
- •Theme configuration: src/theme/darkTheme.js
- •Shared components: src/components/shared/
- •Project examples: src/components/, src/pages/