@nexusui/theme
v5.0.0
Published
This is a simple MUI theme drop in for your React application, which aims to be the source of truth for the NexusUI Design system.
Downloads
2,254
Readme
NexusUI MUI Theme
This is a simple MUI theme drop in for your React application, which aims to be the source of truth for the NexusUI Design system.
NOTE: The NexusUI v5+ theme enables the
cssVariables
feature and allows wrapping your app with aThemeProvider
. After rendering, you will see the CSS variables in the:root
stylesheet of the HTML document. By default, these variables are flattened and prefixed with--mui
.
Installation
# With yarn:
yarn add @nexusui/theme
# With npm:
npm install --save @nexusui/theme
Peer Dependencies
Our theme is built to be used with MUI, so you'll need to have all of the MUI peer dependencies installed in your project:
# With yarn:
yarn add @mui/material @emotion/react @emotion/styled @mui/icons-material
# With npm:
npm install --save @mui/material @emotion/react @emotion/styled
Usage
Basic theme & Fonts Import
In your main React application entry point, wrap your whole app with the ThemeProvider and pass in the NexusUI theme to the theme
prop. You should also include the CssBaseline
component from MUI — this component includes a CSS reset to standardize default styles across browsers for a more consistent experience. Additionally, you should import the fonts file, which provides a self-hosted version of the Open Sans font through Fontsource.
NOTE: The fonts file from the NexusUI theme includes the 400 (regular), 600 (semi-bold), 700 (bold), and 800 (extra-bold) weights. If you need 300 (light) or 500 (medium) for your application, you can include them separately (see below).
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';
import theme from '@nexusui/theme';
import '@nexusui/theme/fonts';
// The theme variables type is not enabled by default. You need to import the module augmentation to enable the typings:
import type {} from '@mui/material/themeCssVarsAugmentation';
// include these only if you need Open Sans light or medium weight in your application
// import '@fontsource/open-sans/300.css;
// import '@fontsource/open-sans/500.css;
...
<ThemeProvider theme={theme}>
<CssBaseline />
{/* Your Application Contents Here */}
</ThemeProvider>
Supporting Light & Dark Theme
The NexusUI theme includes both light and dark colorSchemes / themes. You can toggle between light and dark mode using the useColorScheme
hook from MUI.
import CssBaseline from '@mui/material/CssBaseline';
import {
ThemeProvider,
useColorScheme,
} from '@mui/material/styles'
import theme from "@nexusui/theme";
import '@nexusui/theme/fonts';
// The theme variables type is not enabled by default. You need to import the module augmentation to enable the typings:
import type {} from '@mui/material/themeCssVarsAugmentation';
const defaultMode = localStorage.getItem("theme") || 'light'; // Or read from redux
const { mode, setMode } = useColorScheme();
<ThemeProvider theme={theme} defaultMode={defaultMode}>
<CssBaseline />
{/* Your Application Contents Here */}
<Button
onClick={() => {
setMode(mode === 'light' ? 'dark' : 'light');
}}
>
{mode === 'light' ? 'Turn dark' : 'Turn light'}
</Button>
</ThemeProvider>
Supporting localization
If you also want to support both MUI
and Nexus UI
components localization switching in your app, you can use useThemeWithLocale
to simplify this step:
import {
ThemeProvider
} from '@mui/material/styles'
import CssBaseline from '@mui/material/CssBaseline';
import { LanguageType, useThemeWithLocale, nexusThemeConfig } from "@nexusui/theme";
import '@nexusui/theme/fonts';
// The theme variables type is not enabled by default. You need to import the module augmentation to enable the typings:
import type {} from '@mui/material/themeCssVarsAugmentation';
// Or read theme mode from redux
const defaultMode = localStorage.getItem("theme") || 'light';
// Or read language from redux
const language = localStorage.getItem("language") || 'en-US'; // Or read from redux
const themeWithLocale = useThemeWithLocale(language as LanguageType, nexusThemeConfig);
<ThemeProvider theme={themeWithLocale} defaultMode={defaultMode}>
<CssBaseline />
{/* Your Application Contents Here */}
</ThemeProvider>
With MUI X Data Grid or MUI X Date Pickers
If you want to add localization for either of these supplemental MUI libraries, you can import their localization configurations and merge them into the theme (you can find instructions here and here).
import { ThemeProvider, extendTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { LanguageType, useThemeWithLocale, nexusThemeConfig } from "@nexusui/theme";
import '@nexusui/theme/fonts';
// The theme variables type is not enabled by default. You need to import the module augmentation to enable the typings:
import type {} from '@mui/material/themeCssVarsAugmentation';
// import the localization configurations for any languages you support
import * as gridLocales from '@mui/x-data-grid/locales';
import * as dateLocales from '@mui/x-date-pickers/locales';
// Or read theme mode from redux
const defaultMode = localStorage.getItem("theme") || 'light';
// Or read language from redux
const language = localStorage.getItem("language") || 'en-US'; // Or read from redux
const themeWithLocale = useThemeWithLocale(language as LanguageType, nexusThemeConfig, gridLocales[language], dateLocales[language]);
<ThemeProvider theme={themeWithLocale} defaultMode={defaultMode}>
<CssBaseline />
{/* Your Application Contents Here */}
</ThemeProvider>
Migration Guide
4.X.X => 5.0.0
Version 5 eliminates custom theme extensions to help you prepare for the migration to the new Nova theme. All custom theme properties have been eliminated. The theme has also been migrated to the new CSS variables structure from MUI. Refer to the following to help you map any existing styles using these custom properties to a suitable standard theme value:
Typescript & Jest
The theme variables type is not enabled by default. You need to import the module augmentation to enable the typings.
// App.tsx
import type {} from '@mui/material/themeCssVarsAugmentation';
- You may face "matchMedia" issue after upgrading, please simulate it in jest setup file.
// jest.setup.ts
window.matchMedia = (query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
});
Highlight Palette
Highlight colors have been removed from the theme object, but they are still available through the colors subfolder:
// before
sx={{ color: (theme) => theme.highlight.XXX }}
// after
import { highlight } from '@nexusui/theme/colors';
sx={{ color: highlight.XXX }}
Shades Palette
The shades palette has been removed, but you can use the rgba
construction to recreate them where needed:
// before
sx={{ color: (theme) => theme.shades.primary['4p'] }} // 4p, 8p, 12p, 30p, 50p
sx={{ color: (theme) => theme.shades.secondary['4p'] }} // 4p, 8p, 12p, 30p, 50p
// after w/ CSS Vars (preferred)
import { alpha } from '@mui/material/styles';
sx={{ color: (theme) => `rgba(${theme.vars.palette.primary.mainChannel} / 0.04)` }} // 0.04, 0.08, 0.12, 0.30, 0.50
sx={{ color: (theme) => `rgba(${theme.vars.palette.secondary.mainChannel} / 0.04)` }} // 0.04, 0.08, 0.12, 0.30, 0.50
// after with alpha
import { alpha } from '@mui/material/styles';
sx={{ color: (theme) => alpha(theme.palette.primary.main, 0.04) }} // 0.04, 0.08, 0.12, 0.30, 0.50
sx={{ color: (theme) => alpha(theme.palette.secondary.main, 0.04) }} // 0.04, 0.08, 0.12, 0.30, 0.50
Alternate Background
Alternate background color has been removed — switch to using default or paper, whichever is closer for your use case.
// before
sx={{ backgroundColor: 'background.alternate' }}
// after
sx={{ backgroundColor: 'background.default' }} // or 'background.paper'
Typography Variants
Custom typography variants have been removed — replace them with the nearest standard variant:
| Custom Variant | Standard Variant | | -------- | -------- | | display | h1 | | bodyL | body1 | | body | body2 | | bodyS | body2 |
Alternate Component Colors
Previously, some components supported an 'alternate' color option (Button, Checkbox, Chip, Fab, IconButton, Radio, Slider, Switch, ToggleButton, ToggleButtonGroup), which gave the components a white base for use on darker colored backgrounds. This option has been removed, but you can recreate the styles if needed as follows:
Button
// before
<Button color={'alternate'} />
<Button color={'alternate'} variant={'outlined'} />
<Button color={'alternate'} variant={'contained'} />
// after
<Button color={'inherit'} sx={{ color: 'common.white' }} />
<Button color={'inherit'} variant={'outlined'} sx={{ color: 'common.white', borderColor: 'common.white' }} />
<Button
color={'inherit'}
variant={'contained'}
sx={(theme) => ({
backgroundColor: 'common.white',
color: 'primary.main',
...theme.applyStyles("dark", {
color: 'primary.dark'
})
})}
/>
IconButton
// before
<IconButton color={'alternate'} />
// after
<IconButton color={'inherit'} sx={{ color: 'common.white' }} />
Checkbox
// before
<Checkbox color={'alternate'} />
// after
<Checkbox color={'default'} sx={{ color: 'primary.200' }} />
Fab
// before
<Fab color={'alternate'} />
// after
<Fab color={'inherit'} sx={{ backgroundColor: 'common.white', color: 'primary.main' }} />
Radio
// before
<Radio color={'alternate'} />
// after
<Radio color={'default'} sx={{ color: 'primary.200' }} />
Slider
// before
<Slider color={'alternate'} />
// after (for dark theme, you can ignore the sx and just use the primary color on its own)
<Slider
color={'primary'}
sx={{
color: 'primary.400',
'& .MuiSlider-rail': {
backgroundColor: 'background.paper',
opacity: 1
},
'& .MuiSlider-markLabel': {
color: 'common.white'
},
'& .MuiSlider-valueLabel': {
backgroundColor: 'background.paper',
color: 'primary.main'
}
}}
/>
Switch
// before
<Switch color={'alternate'} />
// after (for dark theme, you can use primary or info variant on its own without custom styles)
<Switch
sx={{
'& .MuiSwitch-switchBase': {
'&.Mui-disabled': {
color: 'grey.300',
'+ .MuiSwitch-track': {
backgroundColor: 'grey.400',
opacity: 1,
},
},
'&.Mui-checked': {
'+ .MuiSwitch-track': {
backgroundColor: 'primary.300',
opacity: 1,
},
'&.Mui-disabled': {
color: 'primary.200',
'+ .MuiSwitch-track': {
backgroundColor: 'primary.400',
opacity: 1,
},
},
},
},
'& .MuiSwitch-track': {
backgroundColor: (theme) => alpha(theme.palette.common.white, 0.38),
opacity: 1,
},
}}
ToggleButton / ToggleButtonGroup
// before
<ToggleButtonGroup color={'alternate'} />
// after
<ToggleButtonGroup
color={'info'}
sx={{
'& .MuiToggleButton-root':{
color: 'inherit'
}
}}
/>
Chip
// before
<Chip color={'alternate'} variant={'filled'} />
<Chip color={'alternate'} variant={'outlined'} />
// after
<Chip
color={'default'}
variant={'filled'}
sx={{
backgroundColor: 'common.white',
'& .MuiChip-deleteIcon': {
color: 'grey.400',
'&:hover': {
color: 'grey.500'
}
},
'& .MuiChip-avatar': {
backgroundColor: 'grey.400',
color: 'common.white'
}
}}
/>
<Chip
color={'default'}
variant={'outlined'}
sx={{
my: 2,
borderColor: 'common.white',
color: 'common.white',
'& .MuiChip-deleteIcon': {
color: (theme) => alpha(theme.palette.common.white, 0.7),
'&:hover': {
color: 'common.white'
}
},
'& .MuiChip-avatar': {
backgroundColor: 'common.white',
color: 'primary.main'
},
'&.MuiChip-clickable:hover': {
backgroundColor: (theme) => alpha(theme.palette.common.white, 0.15)
}
}}
/>
Localization
Previously, the Nexus UI theme (themeWithLocale
) included built-in localization for MUI X DataGrid and Date/Time Pickers. To reduce peer dependency requirements, these built in localizations have been removed. If you are using any of these components in your application, you can add the localizations yourself and merge them with the Nexus UI theme (instructions for DataGrid and Date Pickers — also see the example in the usage instructions above).
Css Variables utility
The getThemePaletteCssVariables
has been removed. If you were using this previously, you can now access CSS variables for theme values directly from the theme object.
// before
import { getThemePaletteCssVariables } from '@nexusui/theme';
const theme = localStorage.getItem('theme') === 'dark' ? 'dark' : 'light';
const cssVariables = getThemePaletteCssVariables(theme);
const html = document.getElementsByTagName('html')[0];
Object.keys(cssVariables).forEach((property) => {
html.style.setProperty(property, cssVariables[property]);
});
export default function CssVariableUsage() {
const style = { display: 'flex', flex: 1, padding: '8px', marginTop: '4px', marginRight: '4px' };
return (
<div style={{ ...style, margin: 0, padding: 0 }}>
<div style={{ backgroundColor: 'var(--mui-primary-main)', color: 'var(--mui-primary-contrastText)', ...style }}>primary.main</div>
<div style={{ backgroundColor: 'var(--mui-secondary-main)', color: 'var(--mui-secondary-contrastText)', ...style }}>secondary.main</div>
<div style={{ backgroundColor: 'var(--mui-error-main)', color: 'var(--mui-error-contrastText)', ...style }}>error.main</div>
</div>
);
}
// after
import { useTheme } from '@mui/material/styles';
const theme = useTheme();
<div style={{backgroundColor: theme.vars.palette.primary.main}} />
3.X.X => 4.0.0
Version 4.0.0 of @nexusui/theme
includes a single breaking change with dependencies. When upgrading, you'll need to add the following dependencies:
- @mui/x-tree-view
You can also remove your @mui/lab dependency if you were only using it for the TreeView component, which was migrated out of the lab.
2.X.X -> 3.0.0
Version 3.0.0 of @nexusui/theme
includes a single breaking change with dependencies. When upgrading, you'll need to update your dependencies to the following minimum versions:
- @mui/x-data-grid to version 6.5.0 or greater.
- @mui/x-date-pickers to version 6.7.0 or greater.
1.X.X -> 2.0.0
Version 2.0.0 of @nexusui/theme
includes a few minor breaking changes to improve consistency and compatibility and also introduces our new dark theme. When upgrading from version 1.x, take note of the following breaking changes.
1) Open Sans fonts no longer automatically included
We previously bundled the Open Sans font dependencies along with our theme automatically to simplify the integration for users. However, including these CSS files automatically in our NPM package caused the theme to no longer be compatible with NextJS applications. We have removed this automatic import in version 2.0.0, so when upgrading, you will need to manually add the following import to your top-level index file:
import '@nexusui/theme/fonts';
2) onDark
palette renamed to alternate
We previously had a supplemental palette called 'onDark' (theme.palette.onDark
) which was used sparingly for components that were rendered on a dark blue background. We've renamed this palette to be more general in order to make sense for the dark theme. You should be able to use a simple find & replace strategy to replace all instances of onDark
with alternate
throughout your application.
3) background.blue
palette renamed to background.alternate
Similar to the onDark palette, we have renamed the 'background.blue' palette (theme.palette.background.blue
) to 'background.alternate' for compatibility with the dark theme (the alternate background is not a shade of blue in the dark theme). This is another case where you can use a find & replace strategy to update the name throughout your application.
0.X.X -> 1.0.0
Version 1.0.0 of @nexusui/theme
is a major refactor to better align with Material Design styles and the Material UI theming mechanism. This update also improves alignment with the Nexus design assets currently accessible through Figma.
In general, we recommend trying to reduce your custom style/theme overrides that you may have in place for MUI elements in your application (you can gradually add them back in if they are, in fact, necessary). We've put a lot of effort into making these components look correct for NexusUI applications out-of-the-box with little to no styling required. Any overrides that you currently have in place will likely interfere and prevent you from reaping the full benefits of the updated theme.
When updating to this version, there are some key changes that you will need to be aware of to make the process as smooth as possible.
1) New Peer Dependencies
We converted the theme package to TypeScript. To add custom theming for MUI-X components, we needed to add new dependencies on their type definitions. You will need to install these packages to avoid type errors when using the theme.
# With yarn:
yarn add @mui/x-data-grid @mui/x-date-pickers
# With npm:
npm install --save @mui/x-data-grid @mui/x-date-pickers
2) Color Palette Imports
Individual color palettes are no longer available from a nested colors folder. Instead, import them from the package root:
// before
import { black, blue, green, grey, orange, red } from '@nexusui/theme/colors';
// after
import { black, blue, green, grey, orange, red } from '@nexusui/theme';
3) Updated Color Palette Shades
- Individual color palettes have been updated for compatibility with the MUI theme mechanism. These now include shades for
50, 100, 200, 300, 400, 500, 600, 700, 800, 900
. Note that the0
and1000
shades have been removed. - All palettes previously included pure white as their lightest shade and pure black as their darkest shade. The lightest/darkest shade in each palette is now a version of the base color. If you need pure white/black, you can access them from the
common
palette or using the hex code.
// before
import { blue } from '@nexusui/theme/colors';
const myWhite = blue[50];
const myBlack = blue[1000];
// after
import { blue } from '@nexusui/theme';
const myDarkColor = blue[900];
const myLightColor = blue[50];
const myWhite = theme.palette.common.white; // or '#fff'
const myBlack = theme.palette.common.black; // or '#000'
Some palettes previously did not have all shades defined, so some shades (e.g., 500 and 700) might have been exactly the same. Every variant in each palette is now a unique shade that evenly progresses from light to dark (this is most notable in the range > 500 — there is a wider variety of darker shades now for most colors).
The
blue
palette was also overhauled (it's actually a combination of the previous blue and secondary blue palettes — we now haveblue
anddarkBlue
). Because the base color shifted, there is no direct 1-1 mapping of old shades to new shades, but a reasonable mapping would be:
oldBlue[0] -> theme.palette.common.white
oldBlue[50] -> grey[200]
oldBlue[100] -> blue[50]
oldBlue[200] -> blue[100]
oldBlue[300] -> blue[200]
oldBlue[400] -> blue[600]
oldBlue[500] -> blue[700]
oldBlue[600] -> darkBlue[700]
oldBlue[700] -> darkBlue[700]
oldBlue[800] -> darkBlue[800]
oldBlue[900] -> darkBlue[900]
oldBlue[1000] -> theme.palette.common.black
4) Removed Tertiary and Dark Palettes
We previously extended the default MUI theme structure to include tertiary
and dark
palettes. Additionally, the primary
and secondary
palettes were both based on the same blue
palette.
To better align with how MUI theming works, we have removed the tertiary
palette and moved those definitions to secondary
to provide a better accent color (instead of blue and blue).
// before
const myBlueColor = theme.palette.secondary;
const myGreenColor = theme.palette.tertiary;
// after
const myBlueColor = theme.palette.primary;
const myGreenColor = theme.palette.secondary;
The dark
palette was previously used to handle variations of components that were displayed on a dark colored background. The spirit of this palette has been maintained, but renamed to alternate
. This palette is not meant to be used like the other palettes (it does have all shades defined, but they are all pure white) — it's exclusively for use with the MUI components that support a 'white' version for use on dark backgrounds (Button, IconButton, CheckBox, Fab, Radio, Slider, Switch, ToggleButtonGroup, ToggleButton, Chip) by passing it to the color
prop.
// before
<Button color={'dark'}/>
// after
<Button color={'alternate'}/>
5) Updated Typography Variants
The Typography
variants have been slightly modified and updated to define the styles for previously undefined defaults from MUI. Notable changes include:
- added a
display
variant — this is the biggest and boldest style we offer - added a definition for
h6
,subtitle
, andsubtitle2
- custom "body2" styles have been shifted down (subtitle styles take the place of the larger sizes):
bodyXL
has been removed ->bodyL
bodyL
->body
body
->bodyS
- removed additional custom variants in favor of default MUI variants:
label
->bodyS
strong
-><Typography fontWeight={700}>
footer
->caption
user
-><Typography variant={"body2"} fontWeight={700}>
6) Default Roundness (Border Radius)
The theme default roundness
property in the theme (affects the roundness of corners on various MUI elements) has been changed from 8px to 0.25rem (4px). If you were basing some of your custom styles on the theme roundness, you may need to multiply
your values by 2 to maintain the same style as before.