Type safe CSS-in-JS API heavily inspired by react-jss
—
TSS-React provides specialized integration for Material-UI applications with built-in theme support, style overrides compatibility, and seamless migration from @material-ui/core v4. The integration includes pre-configured instances and plugins for MUI theme systems.
Ready-to-use TSS, makeStyles, and withStyles instances with MUI theme integration.
/**
* Pre-configured TSS instance with MUI theme context and plugin support
*/
const tss: Tss<{ theme: Theme }, {}, never, MuiThemeStyleOverridesPluginParams, never>;
/**
* Pre-configured makeStyles function with MUI theme
*/
const makeStyles: <Params = void, RuleNameSubsetReferencableInNestedSelectors extends string = never>(
params?: { name?: string | Record<string, unknown>; uniqId?: string }
) => MakeStylesHook<Theme, Params, RuleNameSubsetReferencableInNestedSelectors>;
/**
* Pre-configured withStyles HOC with MUI theme
*/
const withStyles: <Component, Props, CssObjectByRuleName>(
Component: Component,
cssObjectByRuleNameOrGetCssObjectByRuleName:
| CssObjectByRuleName
| ((theme: Theme, props: Props, classes: Record<string, string>) => CssObjectByRuleName)
) => ComponentType<Props>;
/**
* Pre-configured useStyles hook with MUI theme context
*/
const useStyles: UseStyles<{ theme: Theme }, {}, string, MuiThemeStyleOverridesPluginParams>;
interface Theme {
palette: any;
typography: any;
spacing: (...args: any[]) => any;
breakpoints: any;
shadows: any;
shape: any;
transitions: any;
zIndex: any;
[key: string]: any;
}Usage Examples:
import { tss, makeStyles, withStyles, useStyles } from "tss-react/mui";
// Using pre-configured TSS instance
const useComponentStyles = tss.create(({ theme }) => ({
root: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius
},
title: {
color: theme.palette.primary.main,
fontSize: theme.typography.h5.fontSize,
fontWeight: theme.typography.fontWeightBold
}
}));
// Using pre-configured makeStyles
const useMakeStyles = makeStyles()(theme => ({
container: {
maxWidth: theme.breakpoints.values.md,
margin: "0 auto",
padding: theme.spacing(0, 2)
}
}));
// Using pre-configured withStyles
const StyledButton = withStyles(
({ children, classes }: { children: React.ReactNode; classes?: { root?: string } }) => (
<button className={classes?.root}>{children}</button>
),
theme => ({
root: {
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
padding: theme.spacing(1, 2),
border: "none",
borderRadius: theme.shape.borderRadius,
"&:hover": {
backgroundColor: theme.palette.primary.dark
}
}
})
);
function MyMuiComponent() {
const { classes } = useComponentStyles();
const { classes: layoutClasses } = useMakeStyles();
return (
<div className={layoutClasses.container}>
<div className={classes.root}>
<h1 className={classes.title}>MUI Integration Example</h1>
<StyledButton>Click me</StyledButton>
</div>
</div>
);
}Plugin system for integrating with MUI's theme style overrides mechanism, enabling component customization through theme configuration.
/**
* Plugin for MUI theme style overrides integration
* @param params - Plugin parameters including classes, theme, and MUI-specific options
* @returns Processed classes with theme overrides applied
*/
const useMuiThemeStyleOverridesPlugin: UsePlugin<
{ theme: MuiThemeLike },
MuiThemeStyleOverridesPluginParams
> = (params: {
classes: Record<string, string>;
theme: MuiThemeLike;
muiStyleOverridesParams?: MuiThemeStyleOverridesPluginParams;
css: Css;
cx: Cx;
name?: string;
}) => Record<string, string>;
interface MuiThemeStyleOverridesPluginParams {
muiStyleOverridesParams?: {
props: Record<string, unknown>;
ownerState?: Record<string, unknown>;
};
}
interface MuiThemeLike {
components?: Record<string, {
styleOverrides?: Record<string, any>;
}>;
}Usage Examples:
import { createTss } from "tss-react";
import { useTheme } from "@mui/material/styles";
import { useMuiThemeStyleOverridesPlugin } from "tss-react/mui";
// Manual setup with plugin
const { tss } = createTss({
useContext: () => {
const theme = useTheme();
return { theme };
},
usePlugin: useMuiThemeStyleOverridesPlugin
});
// Component with theme overrides support
const useCardStyles = tss
.withName("MyCard")
.withParams<{ variant: "outlined" | "elevated" }>()
.create(({ theme }, { variant }) => ({
root: {
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
padding: theme.spacing(2),
...(variant === "outlined" && {
border: `1px solid ${theme.palette.divider}`
}),
...(variant === "elevated" && {
boxShadow: theme.shadows[4]
})
},
header: {
borderBottom: `1px solid ${theme.palette.divider}`,
paddingBottom: theme.spacing(1),
marginBottom: theme.spacing(2)
},
content: {
color: theme.palette.text.secondary
}
}));
// Theme configuration with style overrides
const theme = createTheme({
components: {
MyCard: {
styleOverrides: {
root: {
// These styles will be merged with component styles
transition: "all 0.3s ease",
"&:hover": {
transform: "translateY(-2px)"
}
},
header: {
fontWeight: 600,
textTransform: "uppercase"
}
}
}
}
});
function MyCard({
variant,
title,
children
}: {
variant: "outlined" | "elevated";
title: string;
children: React.ReactNode;
}) {
const { classes } = useCardStyles({ variant });
return (
<div className={classes.root}>
<div className={classes.header}>{title}</div>
<div className={classes.content}>{children}</div>
</div>
);
}import { tss } from "tss-react/mui";
const useResponsiveStyles = tss.create(({ theme }) => ({
container: {
padding: theme.spacing(1),
[theme.breakpoints.up("sm")]: {
padding: theme.spacing(2)
},
[theme.breakpoints.up("md")]: {
padding: theme.spacing(3),
maxWidth: theme.breakpoints.values.lg,
margin: "0 auto"
}
},
grid: {
display: "grid",
gap: theme.spacing(1),
gridTemplateColumns: "1fr",
[theme.breakpoints.up("sm")]: {
gridTemplateColumns: "repeat(2, 1fr)",
gap: theme.spacing(2)
},
[theme.breakpoints.up("lg")]: {
gridTemplateColumns: "repeat(3, 1fr)",
gap: theme.spacing(3)
}
}
}));import { tss } from "tss-react/mui";
const useThemedStyles = tss
.withParams<{
severity: "info" | "warning" | "error" | "success";
size: "small" | "medium" | "large";
}>()
.create(({ theme }, { severity, size }) => {
const palette = theme.palette[severity];
const typography = {
small: theme.typography.body2,
medium: theme.typography.body1,
large: theme.typography.h6
}[size];
return {
alert: {
backgroundColor: palette.light,
color: palette.contrastText,
border: `1px solid ${palette.main}`,
borderRadius: theme.shape.borderRadius,
padding: theme.spacing(1, 2),
fontSize: typography.fontSize,
fontWeight: typography.fontWeight,
lineHeight: typography.lineHeight
},
icon: {
marginRight: theme.spacing(1),
color: palette.main
}
};
});import { tss } from "tss-react/mui";
const useAnimatedStyles = tss
.withParams<{ expanded: boolean; loading: boolean }>()
.create(({ theme }, { expanded, loading }) => ({
expandableCard: {
backgroundColor: theme.palette.background.paper,
borderRadius: theme.shape.borderRadius,
overflow: "hidden",
transition: theme.transitions.create(
["height", "box-shadow"],
{
duration: theme.transitions.duration.standard,
easing: theme.transitions.easing.easeInOut
}
),
height: expanded ? "auto" : 60,
boxShadow: expanded ? theme.shadows[8] : theme.shadows[2]
},
loadingSpinner: {
animation: loading ? `$spin 1s linear infinite` : "none",
color: theme.palette.primary.main
},
"@keyframes spin": {
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" }
}
}));// Before (Material-UI v4)
import { makeStyles, withStyles } from "@material-ui/core/styles";
const useStyles = makeStyles(theme => ({
root: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(2)
}
}));
const StyledComponent = withStyles(theme => ({
root: {
color: theme.palette.primary.main
}
}))(({ classes }) => <div className={classes.root}>Content</div>);
// After (TSS-React MUI)
import { makeStyles, withStyles } from "tss-react/mui";
const useStyles = makeStyles()(theme => ({
root: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(2)
}
}));
const StyledComponent = withStyles(
({ classes }: { classes?: { root?: string } }) => (
<div className={classes?.root}>Content</div>
),
theme => ({
root: {
color: theme.palette.primary.main
}
})
);import { ThemeProvider, createTheme } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
const theme = createTheme({
palette: {
mode: "light",
primary: {
main: "#1976d2"
},
secondary: {
main: "#dc004e"
}
},
components: {
// Style overrides work automatically with TSS-React
MuiButton: {
styleOverrides: {
root: {
textTransform: "none"
}
}
}
}
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<MyStyledComponents />
</ThemeProvider>
);
}declare module "@mui/material/styles" {
interface Theme {
custom: {
gradients: {
primary: string;
secondary: string;
};
};
}
interface ThemeOptions {
custom?: {
gradients?: {
primary?: string;
secondary?: string;
};
};
}
}
const theme = createTheme({
custom: {
gradients: {
primary: "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)",
secondary: "linear-gradient(45deg, #2196F3 30%, #21CBF3 90%)"
}
}
});
const useCustomThemeStyles = tss.create(({ theme }) => ({
gradientButton: {
background: theme.custom.gradients.primary,
color: "white",
border: "none",
borderRadius: theme.shape.borderRadius,
padding: theme.spacing(1, 3),
cursor: "pointer",
transition: theme.transitions.create("transform"),
"&:hover": {
transform: "scale(1.05)"
}
}
}));import { Button, Card, CardContent, Typography } from "@mui/material";
import { tss } from "tss-react/mui";
const useIntegratedStyles = tss.create(({ theme }) => ({
styledCard: {
// Enhancing MUI Card component
background: `linear-gradient(135deg, ${theme.palette.primary.light} 0%, ${theme.palette.primary.main} 100%)`,
color: theme.palette.primary.contrastText,
"& .MuiCardContent-root": {
padding: theme.spacing(3)
}
},
customButton: {
// Custom styling that complements MUI Button
marginTop: theme.spacing(2),
borderRadius: theme.spacing(3),
textTransform: "none",
fontWeight: theme.typography.fontWeightBold
}
}));
function IntegratedComponent() {
const { classes } = useIntegratedStyles();
return (
<Card className={classes.styledCard}>
<CardContent>
<Typography variant="h5" component="h2">
Custom Styled Card
</Typography>
<Typography variant="body2">
This card combines MUI components with TSS-React custom styling.
</Typography>
<Button
variant="contained"
color="secondary"
className={classes.customButton}
>
Learn More
</Button>
</CardContent>
</Card>
);
}Install with Tessl CLI
npx tessl i tessl/npm-tss-react