.claude/skills/ui-engineer/SKILL.md
MUI component styling and implementation rules — sx prop patterns, theme usage, dark mode, spacing, accessibility, form best practices, chart config, and component-specific gotchas. Use whenever building or modifying MUI components, reviewing MUI code, or implementing designs with Material UI. Triggers on any task involving MUI component creation, styling, theming, or mockup implementation.
npx skillsauth add siriwatknp/mui-treasury ui-engineerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Staff Design Engineer with comprehensive MUI expertise and pixel-perfect implementation skills.
theme.applyStyles('dark', styles) exclusivelyonClear when onChange(null) suffices)gap at least 1 unless design explicitly says otherwise<Box component="img" /> with empty src and proper alt, style via sx with proper aspectRatiohttps://placehold.co/600x400 (no query params). Use correct aspect ratio (e.g., 3:4 → https://placehold.co/600x400, square → https://placehold.co/400)maxWidth/width — let them flow naturally; control width from the preview pageSemantic text (error, success, info, warning): use <palette>.text token for better contrast
<Typography sx={{ color: "error.text" }}>Error</Typography>
<Box sx={theme => ({
color: (theme.vars || theme).palette.success.text,
})}>
High contrast background → Button with custom borderRadius (IconButton doesn't support variant)
<Button variant="contained" sx={{ borderRadius: 99 }}>
<AddIcon />
</Button>
IconButton only for secondary actions or lists of same-size icon-only buttons
No textTransform: "none" needed — built-in theme already handles it
Don't customize buttons with grey tokens — use primary color
Always start with zero margin/axis, then adjust:
import { BarChart } from "@mui/x-charts/BarChart";
<BarChart
margin={{ left: 0, right: 0, top: 0, bottom: 0 }}
xAxis={[{ height: 0, position: "none" }]} // min 28 to display label
yAxis={[{ width: 0, position: "none" }]} // min 28 to display label
/>;
slotProps.legend.sx.display = "none"valueFormatter: (params) => \${params.value}%``colors prop with string arraymargin pattern as above<Chip variant="filled" color="success|error|info|warning|secondary">@mui/icons-material. Fallback: lucide-react<Box sx={{ display: 'inline-block', width: size, height: size, bgcolor: 'text.icon', borderRadius: '50%' }} />sx={{ alignItems: 'flex-start' }} — NOT the alignItems prop
ListItemAvatar for alignment (unless ListItemText is not used)disablePadding when secondaryAction is present (removes padding-right)secondaryAction: ensure padding-right accommodates the action contentslotProps.secondary.component to "div" if secondary is a React element (avoids <p> nesting)h5/h6 variants — lowest heading is h4label prop — not separate TypographyslotProps — not deprecated InputProps/InputLabelProps
slotProps.input, slotProps.inputLabel, slotProps.htmlInputrequired, error, helperText for validation and a11y// ✅ CORRECT
<TextField
fullWidth
required
label="Card Number"
placeholder="1234 5678 9012 3456"
variant="outlined"
value={formData.cardNumber}
onChange={handleInputChange("cardNumber")}
error={!!errors.cardNumber}
helperText={errors.cardNumber || "Enter 16-digit card number"}
/>
// ❌ INCORRECT
<Box>
<Typography variant="body2">CARD NUMBER</Typography>
<TextField
fullWidth
placeholder="1234..."
InputProps={{ /* deprecated */ }}
/>
</Box>
sx Prop Rulesheight — let padding/line-height determine it- sx={theme => ({ borderRadius: (theme.vars || theme).shape.borderRadius * 3 })}
+ sx={{ borderRadius: 3 }}
- sx={theme => ({ color: (theme.vars || theme).palette.primary.main })}
+ sx={{ color: "primary.main" }}
Use callback as value or array item. NEVER spread callback in object:
// ✅ Callback as value
sx={theme => ({
color: (theme.vars || theme).palette.primary.main,
})}
// ✅ Callback as array item
sx={[
{ borderRadius: 2 },
theme => ({
color: (theme.vars || theme).palette.primary.main,
})
]}
// ❌ NEVER — callback spread in object
sx={{
borderRadius: 2,
...theme => ({
color: (theme.vars || theme).palette.primary.main,
})
}}
sx propsAlways use array syntax:
function MyButton({ sx, ...props }: MyButtonProps) {
return (
<IconButton
sx={[
{ color: "text.secondary", "&:hover": { color: "text.primary" } },
...(Array.isArray(sx) ? sx : [sx]),
]}
{...props}
/>
);
}
Wrap in @media (hover: hover):
sx={theme => ({
bgcolor: "background.paper",
"@media (hover: hover)": {
"&:hover": { bgcolor: "action.hover" },
},
})}
sx={{ width: { xs: "100%", md: "50%" } }}theme.breakpoints.up("md")sx={theme => ({
width: "100%",
[theme.breakpoints.up("md")]: { width: "50%" },
})}
sx={theme => ({
[theme.containerQueries?.up("sm") || "@container (min-width: 600px)"]: {
gridColumn: "span 6",
},
[theme.containerQueries?.up("md") || "@container (min-width: 900px)"]: {
gridColumn: "span 7",
},
})}
Both container + media queries with class selectors:
sx={theme => ({
[theme.containerQueries?.up("md") || "@container (min-width: 900px)"]: {
width: "50%",
},
".responsive-media &": {
[theme.breakpoints.up("md")]: { width: "50%" },
},
})}
(theme.vars || theme).palette.* for palette/shape accesstheme.typography directly (NOT theme.vars.typography)// ✅ CORRECT
sx={{
borderRadius: 3,
color: "primary.main",
p: 2,
...theme.applyStyles('dark', { bgcolor: "grey.900" })
}}
// ❌ INCORRECT
sx={{
borderRadius: "12px",
color: "#1976d2",
padding: "16px",
bgcolor: isDarkMode ? "grey.900" : "white"
}}
useTheme() + isDarkMode patterntheme.applyStyles('dark', styles):// ✅ Correct
sx={theme => ({
bgcolor: "background.paper",
...theme.applyStyles('dark', { bgcolor: "grey.900" }),
})}
// ❌ Incorrect — callback spread in object
sx={{
bgcolor: "background.paper",
...theme => theme.applyStyles('dark', { bgcolor: "grey.900" }),
}}
aria-describedby for forms, aria-live for dynamic content)IconButton needs aria-label, don't wrap disabled buttons in Tooltip::after for click area extensionaria-live)tools
Use this skill when writing meta file for MUI Treasury registry.
development
Always use this skill when integrating Base UI components `@base-ui-components/react` with Material UI `@mui/material`.
documentation
Get better understanding about the structure before writing documentation for a software or system. Invoke when asked/requested to plan on creating documentation.
development
Use this skill for any task involving app layouts — building new layouts, adding sidebars or navigation to existing apps, migrating an existing layout to mui-treasury components, or reviewing/improving layout structure. Trigger whenever the user mentions dashboard layout, sidebar navigation, app shell, header/footer/content structure, collapsible sidebar, drawer navigation, layout with sidebar, or asks to "build a layout", "add a sidebar", "set up the app layout", "review the layout". Also trigger when a mockup or design shows an app with sidebar + header + content regions, even if the user doesn't say "layout" explicitly.