diff --git a/docs/data/joy/customization/approaches/approaches.md b/docs/data/joy/customization/approaches/approaches.md index a8fc388097f10f..08b2ffd02142df 100644 --- a/docs/data/joy/customization/approaches/approaches.md +++ b/docs/data/joy/customization/approaches/approaches.md @@ -25,7 +25,7 @@ Here are some examples that reproduce popular designs (only the light mode, thou ### Customizing theme tokens -Theme tokens refer to both [_low-level_ and _global variant_ design tokens](/joy-ui/customization/theme-tokens/). +Theme tokens refer to both _low-level_ and _global variant_ design tokens. For example, instead of assigning the same hex code every time you want to change a given component's background color, you assign a theme token instead. If, at any point, you want to change that, you'd change in one place only, ensuring you consistency across all the components that use that theme token. diff --git a/docs/data/joy/customization/default-theme-viewer/JoyDefaultTheme.js b/docs/data/joy/customization/default-theme-viewer/JoyDefaultTheme.js new file mode 100644 index 00000000000000..7b83d51ed7c326 --- /dev/null +++ b/docs/data/joy/customization/default-theme-viewer/JoyDefaultTheme.js @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { extendTheme } from '@mui/joy/styles'; +import Box from '@mui/joy/Box'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; +import ThemeViewer, { + useNodeIdsLazy, +} from 'docs/src/modules/components/ThemeViewer'; + +const defaultTheme = extendTheme(); + +export default function JoyDefaultTheme() { + const [expandPaths, setExpandPaths] = React.useState(null); + + React.useEffect(() => { + let expandPath; + decodeURI(document.location.search.slice(1)) + .split('&') + .forEach((param) => { + const [name, value] = param.split('='); + if (name === 'expand-path') { + expandPath = value; + } + }); + + if (!expandPath) { + return; + } + + setExpandPaths( + expandPath + .replace('$.', '') + .split('.') + .reduce((acc, path) => { + const last = acc.length > 0 ? `${acc[acc.length - 1]}.` : ''; + acc.push(last + path); + return acc; + }, []), + ); + }, []); + + const data = defaultTheme; + const allNodeIds = useNodeIdsLazy(data); + React.useDebugValue(allNodeIds); + + return ( + + createTheme()}> + + + + ); +} diff --git a/docs/data/joy/customization/default-theme-viewer/default-theme-viewer.md b/docs/data/joy/customization/default-theme-viewer/default-theme-viewer.md new file mode 100644 index 00000000000000..73e3bff69c90d8 --- /dev/null +++ b/docs/data/joy/customization/default-theme-viewer/default-theme-viewer.md @@ -0,0 +1,15 @@ +# Default theme viewer + +

Here's what the theme object looks like with the default values.

+ +:::warning +This is a work in progress. We're still iterating on Joy UI's default theme. +::: + +## Explore + +Explore the default theme: + +{{"demo": "JoyDefaultTheme.js", "hideToolbar": true, "bg": "inline"}} + +To create your own theme, starts with customizing the [theme colors](/joy-ui/customization/theme-colors/). diff --git a/docs/data/joy/customization/default-theme/default-theme-pt.md b/docs/data/joy/customization/default-theme/default-theme-pt.md deleted file mode 100644 index b2c7f16bbd37c5..00000000000000 --- a/docs/data/joy/customization/default-theme/default-theme-pt.md +++ /dev/null @@ -1,100 +0,0 @@ -# Default Theme - -

Here's what the theme object looks like with the default values.

- -:::warning -**⚠️ Work in progress:** we're still iterating on the whole Joy UI default theme. -::: - -**Typography-related:** - -```js -extendTheme({ - fontSize: { - xs: '0.75rem', - sm: '0.875rem', - md: '1rem', - lg: '1.25rem', - xl: '1.5rem', - xl2: '1.875rem', - xl3: '2.25rem', - xl4: '3rem', - xl5: '3.75rem', - xl6: '4.5rem', - }, -}); -``` - -```js -extendTheme({ - fontFamily: { - body: '"Public Sans", var(--joy-fontFamily-fallback)', - display: '"Public Sans", var(--joy-fontFamily-fallback)', - code: 'Source Code Pro,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace', - fallback: - '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', - }, -}); -``` - -```js -extendTheme({ - fontWeight: { - xs: 200, - sm: 300, - md: 500, - lg: 700, - xl: 800, - }, -}); -``` - -```js -extendTheme({ - lineHeight: { - sm: 1.25, - md: 1.5, - lg: 1.7, - }, -}); -``` - -```js -extendTheme({ - letterSpacing: { - sm: '-0.01em', - md: '0.083em', - lg: '0.125em', - }, -}); -``` - -```js -extendTheme({ - radius: { - xs: '4px', - sm: '8px', - md: '12px', - lg: '16px', - xl: '20px', - }, -}); -} -``` - -**Box shadows:** - -All the shadows are listed [here](https://github.com/mui/material-ui/blob/master/packages/mui-joy/src/styles/types/shadow.ts). We recommend to use `var(--joy-shadowRing)` and `var(--joy-shadowChannel)` for creating the shadows because you can customize the shadow color on the component. - -```js -extendTheme({ - shadows: { - // default tokens - xs: 'var(--joy-shadowRing), 0 1px 2px 0 rgba(var(--joy-shadowChannel) / 0.12)', - sm: '...', - md: '...', - lg: '...', - xl: '...', - }, -}); -``` diff --git a/docs/data/joy/customization/default-theme/default-theme-zh.md b/docs/data/joy/customization/default-theme/default-theme-zh.md deleted file mode 100644 index b2c7f16bbd37c5..00000000000000 --- a/docs/data/joy/customization/default-theme/default-theme-zh.md +++ /dev/null @@ -1,100 +0,0 @@ -# Default Theme - -

Here's what the theme object looks like with the default values.

- -:::warning -**⚠️ Work in progress:** we're still iterating on the whole Joy UI default theme. -::: - -**Typography-related:** - -```js -extendTheme({ - fontSize: { - xs: '0.75rem', - sm: '0.875rem', - md: '1rem', - lg: '1.25rem', - xl: '1.5rem', - xl2: '1.875rem', - xl3: '2.25rem', - xl4: '3rem', - xl5: '3.75rem', - xl6: '4.5rem', - }, -}); -``` - -```js -extendTheme({ - fontFamily: { - body: '"Public Sans", var(--joy-fontFamily-fallback)', - display: '"Public Sans", var(--joy-fontFamily-fallback)', - code: 'Source Code Pro,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace', - fallback: - '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', - }, -}); -``` - -```js -extendTheme({ - fontWeight: { - xs: 200, - sm: 300, - md: 500, - lg: 700, - xl: 800, - }, -}); -``` - -```js -extendTheme({ - lineHeight: { - sm: 1.25, - md: 1.5, - lg: 1.7, - }, -}); -``` - -```js -extendTheme({ - letterSpacing: { - sm: '-0.01em', - md: '0.083em', - lg: '0.125em', - }, -}); -``` - -```js -extendTheme({ - radius: { - xs: '4px', - sm: '8px', - md: '12px', - lg: '16px', - xl: '20px', - }, -}); -} -``` - -**Box shadows:** - -All the shadows are listed [here](https://github.com/mui/material-ui/blob/master/packages/mui-joy/src/styles/types/shadow.ts). We recommend to use `var(--joy-shadowRing)` and `var(--joy-shadowChannel)` for creating the shadows because you can customize the shadow color on the component. - -```js -extendTheme({ - shadows: { - // default tokens - xs: 'var(--joy-shadowRing), 0 1px 2px 0 rgba(var(--joy-shadowChannel) / 0.12)', - sm: '...', - md: '...', - lg: '...', - xl: '...', - }, -}); -``` diff --git a/docs/data/joy/customization/default-theme/default-theme.md b/docs/data/joy/customization/default-theme/default-theme.md deleted file mode 100644 index 574b97e7610123..00000000000000 --- a/docs/data/joy/customization/default-theme/default-theme.md +++ /dev/null @@ -1,101 +0,0 @@ -# Default theme - -

Here's what the theme object looks like with the default values.

- -:::warning -This is a work in progress. We're still iterating on Joy UI's default theme. -::: - -**Typography-related:** - -```js -extendTheme({ - fontSize: { - xs: '0.75rem', - sm: '0.875rem', - md: '1rem', - lg: '1.25rem', - xl: '1.5rem', - xl2: '1.875rem', - xl3: '2.25rem', - xl4: '3rem', - xl5: '3.75rem', - xl6: '4.5rem', - }, -}); -``` - -```js -extendTheme({ - fontFamily: { - body: '"Public Sans", var(--joy-fontFamily-fallback)', - display: '"Public Sans", var(--joy-fontFamily-fallback)', - code: 'Source Code Pro,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace', - fallback: - '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', - }, -}); -``` - -```js -extendTheme({ - fontWeight: { - xs: 200, - sm: 300, - md: 500, - lg: 700, - xl: 800, - }, -}); -``` - -```js -extendTheme({ - lineHeight: { - sm: 1.25, - md: 1.5, - lg: 1.7, - }, -}); -``` - -```js -extendTheme({ - letterSpacing: { - sm: '-0.01em', - md: '0.083em', - lg: '0.125em', - }, -}); -``` - -```js -extendTheme({ - radius: { - xs: '4px', - sm: '8px', - md: '12px', - lg: '16px', - xl: '20px', - }, -}); -} -``` - -**Box shadows:** - -All the shadows are listed [here](https://github.com/mui/material-ui/blob/master/packages/mui-joy/src/styles/types/shadow.ts). -We recommend to use `var(--joy-shadowRing)` and `var(--joy-shadowChannel)` for creating the shadows because you can customize the shadow color on the component. - -```js -extendTheme({ - shadows: { - // default tokens - xs: 'var(--joy-shadowRing), 0 1px 2px 0 rgba(var(--joy-shadowChannel) / 0.12)', - sm: '...', - md: '...', - lg: '...', - xl: '...', - }, -}); -``` diff --git a/docs/data/joy/customization/theme-tokens/BootstrapVariantTokens.js b/docs/data/joy/customization/theme-colors/BootstrapVariantTokens.js similarity index 100% rename from docs/data/joy/customization/theme-tokens/BootstrapVariantTokens.js rename to docs/data/joy/customization/theme-colors/BootstrapVariantTokens.js diff --git a/docs/data/joy/customization/theme-colors/PaletteThemeViewer.js b/docs/data/joy/customization/theme-colors/PaletteThemeViewer.js new file mode 100644 index 00000000000000..0cc6fab730583d --- /dev/null +++ b/docs/data/joy/customization/theme-colors/PaletteThemeViewer.js @@ -0,0 +1,236 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import * as React from 'react'; +import useClipboardCopy from 'docs/src/modules/utils/useClipboardCopy'; +import { extendTheme, styled } from '@mui/joy/styles'; +import Box from '@mui/joy/Box'; +import Link from '@mui/joy/Link'; +import Tooltip from '@mui/joy/Tooltip'; +import Typography from '@mui/joy/Typography'; +import Sheet from '@mui/joy/Sheet'; +import LightMode from '@mui/icons-material/LightModeOutlined'; +import DarkMode from '@mui/icons-material/DarkModeOutlined'; +import InfoOutlined from '@mui/icons-material/InfoOutlined'; +import Check from '@mui/icons-material/Check'; + +const defaultTheme = extendTheme(); + +const traverseObject = (palette) => { + const result = {}; + const traverse = (object, parts = []) => { + if (object && typeof object === 'object') { + // eslint-disable-next-line no-restricted-syntax + for (const key of Object.keys(object)) { + traverse(object[key], [...parts, key]); + } + } else { + result[parts.join('.')] = object; + } + }; + traverse(palette); + return result; +}; + +// https://stackoverflow.com/a/38641281/559913 +const collator = new Intl.Collator(undefined, { + numeric: true, + sensitivity: 'base', +}); + +const Table = styled('table')(({ theme }) => ({ + border: '1px solid', + borderColor: theme.vars.palette.divider, + borderRadius: theme.vars.radius.xs, + borderCollapse: 'separate', + borderSpacing: 0, + display: 'block', + height: 500, + overflowY: 'scroll', + th: { + textAlign: 'left', + padding: '8px 6px', + position: 'sticky', + top: 0, + zIndex: 1, + ...theme.variants.soft.neutral, + }, + td: { + verticalAlign: 'top', + padding: '3px 6px', + }, + tr: { + '&:hover': { + backgroundColor: theme.vars.palette.background.level1, + }, + '&:first-of-type': { + '& td': { paddingTop: 6 }, + }, + }, +})); + +export default function PaletteThemeViewer() { + const { copy, isCopied } = useClipboardCopy(); + const light = traverseObject(defaultTheme.colorSchemes.light.palette); + const dark = traverseObject(defaultTheme.colorSchemes.dark.palette); + const paletteTokens = Array.from( + new Set([...Object.keys(dark), ...Object.keys(light)]), + ).sort(collator.compare); + const renderSwatch = (colorScheme, token) => ( + + ); + + return ( + + + }> + Copied + + + + + + + + + + + + {paletteTokens + .filter((token) => token !== 'mode') + .map((token) => ( + + + + + + ))} + +
+ + Token + + + } + textColor="inherit" + > + Light + + + } + textColor="inherit" + > + Dark + +
+ copy(token)} + endDecorator={ + light[token].match(/^[0-9]+\s[0-9]+\s[0-9]+$/) ? ( + + Translucent color usage:
+ + rgba(var(--joy-palette-{token.replace('.', '-')}) / + 0.6) + + + } + sx={{ pointerEvents: 'none' }} + > + +
+ ) : null + } + sx={{ cursor: 'copy' }} + > + {token} + +
+ copy(light[token])} + > + {light[token]} + + + copy(dark[token])} + > + {dark[token]} + +
+
+ ); +} diff --git a/docs/data/joy/customization/theme-colors/PaletteThemeViewer.tsx b/docs/data/joy/customization/theme-colors/PaletteThemeViewer.tsx new file mode 100644 index 00000000000000..623ccb0de59d37 --- /dev/null +++ b/docs/data/joy/customization/theme-colors/PaletteThemeViewer.tsx @@ -0,0 +1,235 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import * as React from 'react'; +import useClipboardCopy from 'docs/src/modules/utils/useClipboardCopy'; +import { extendTheme, Palette, styled } from '@mui/joy/styles'; +import Box from '@mui/joy/Box'; +import Link from '@mui/joy/Link'; +import Tooltip from '@mui/joy/Tooltip'; +import Typography from '@mui/joy/Typography'; +import Sheet from '@mui/joy/Sheet'; +import LightMode from '@mui/icons-material/LightModeOutlined'; +import DarkMode from '@mui/icons-material/DarkModeOutlined'; +import InfoOutlined from '@mui/icons-material/InfoOutlined'; +import Check from '@mui/icons-material/Check'; + +const defaultTheme = extendTheme(); + +const traverseObject = (palette: Palette) => { + const result: Record = {}; + const traverse = (object: any, parts: string[] = []) => { + if (object && typeof object === 'object') { + // eslint-disable-next-line no-restricted-syntax + for (const key of Object.keys(object)) { + traverse(object[key], [...parts, key]); + } + } else { + result[parts.join('.')] = object; + } + }; + traverse(palette); + return result; +}; + +// https://stackoverflow.com/a/38641281/559913 +const collator = new Intl.Collator(undefined, { + numeric: true, + sensitivity: 'base', +}); + +const Table = styled('table')(({ theme }) => ({ + border: '1px solid', + borderColor: theme.vars.palette.divider, + borderRadius: theme.vars.radius.xs, + borderCollapse: 'separate', + borderSpacing: 0, + display: 'block', + height: 500, + overflowY: 'scroll', + th: { + textAlign: 'left', + padding: '8px 6px', + position: 'sticky', + top: 0, + zIndex: 1, + ...theme.variants.soft.neutral, + }, + td: { + verticalAlign: 'top', + padding: '3px 6px', + }, + tr: { + '&:hover': { + backgroundColor: theme.vars.palette.background.level1, + }, + '&:first-of-type': { + '& td': { paddingTop: 6 }, + }, + }, +})); + +export default function PaletteThemeViewer() { + const { copy, isCopied } = useClipboardCopy(); + const light = traverseObject(defaultTheme.colorSchemes.light.palette); + const dark = traverseObject(defaultTheme.colorSchemes.dark.palette); + const paletteTokens = Array.from( + new Set([...Object.keys(dark), ...Object.keys(light)]), + ).sort(collator.compare); + const renderSwatch = (colorScheme: 'light' | 'dark', token: string) => ( + + ); + return ( + + + }> + Copied + + + + + + + + + + + + {paletteTokens + .filter((token) => token !== 'mode') + .map((token) => ( + + + + + + ))} + +
+ + Token + + + } + textColor="inherit" + > + Light + + + } + textColor="inherit" + > + Dark + +
+ copy(token)} + endDecorator={ + light[token].match(/^[0-9]+\s[0-9]+\s[0-9]+$/) ? ( + + Translucent color usage:
+ + rgba(var(--joy-palette-{token.replace('.', '-')}) / + 0.6) + + + } + sx={{ pointerEvents: 'none' }} + > + +
+ ) : null + } + sx={{ cursor: 'copy' }} + > + {token} + +
+ copy(light[token])} + > + {light[token]} + + + copy(dark[token])} + > + {dark[token]} + +
+
+ ); +} diff --git a/docs/data/joy/customization/theme-tokens/RemoveActiveTokens.js b/docs/data/joy/customization/theme-colors/RemoveActiveTokens.js similarity index 100% rename from docs/data/joy/customization/theme-tokens/RemoveActiveTokens.js rename to docs/data/joy/customization/theme-colors/RemoveActiveTokens.js diff --git a/docs/data/joy/customization/theme-colors/theme-colors.md b/docs/data/joy/customization/theme-colors/theme-colors.md new file mode 100644 index 00000000000000..0413fb059dd664 --- /dev/null +++ b/docs/data/joy/customization/theme-colors/theme-colors.md @@ -0,0 +1,284 @@ +# Theme colors + +

Learn about the theme's default colors and how to customize them.

+ +## Default tokens + +The table below lists all the default tokens and their values in light and dark color schemes. +Some tokens reuse values from other tokens using the [`var(--*)`](https://developer.mozilla.org/en-US/docs/Web/CSS/var) syntax. + +{{"demo": "PaletteThemeViewer.js", "bg": "inline"}} + +### Channel tokens + +The default tokens ending with `Channel` are automatically generated for each palette. +These tokens are useful for creating translucent colors (`rgba`). + +- `lightChannel`: is generated from the palette's `200` token. +- `mainChannel`: is generated from the palette's `500` token. +- `darkChannel`: is generated from the palette's `800` token. + +The example usage is: + +```js +import Typography from '@mui/joy/Typography'; + + ({ + color: `rgba(${theme.vars.palette.primary.mainChannel} / 0.72)`, + })} +> +``` + +### Global variant tokens + +By default, Joy UI has four built-in [global variants](/joy-ui/main-features/global-variants/) tokens: `plain`, `outlined`, `soft`, and `solid`. + +The global variant token is composed of three parts, in the format of **variant type | state | CSS property**. + +For example: + +- `solidBg` refers to the solid variant's background color in its initial state. +- `outlinedHoverBorder` refers to the outlined variant's border color in its hover state. + +There are six palettes (`primary`, `neutral`, `danger`, `info`, `success`, and `warning`) that contain the global variant tokens as listed in the [table above](#default-tokens). + +## Customizing the default palette + +For each color scheme, the default colors are grouped within the `palette` node. + +For example, the snippet below customizes the primary palette in dark mode: + +```js +import { extendTheme } from '@mui/joy/styles'; + +const theme = extendTheme({ + colorSchemes: { + dark: { + palette: { + primary: { + 50: '#C0CCD9', + 100: '#A5B8CF', + 200: '#6A96CA', + 300: '#4886D0', + 400: '#2178DD', + 500: '#096BDE', + 600: '#1B62B5', + 700: '#265995', + 800: '#2F4968', + 900: '#2F3C4C', + }, + }, + }, + }, +}); + +// Then, pass it to ``. +``` + +## Customizing global variant tokens + +We recommend using the [Button](/joy-ui/react-button/) component as a jumping-off point when customizing the global variants, because it gives you access to more of the interactive variants available than some other components. + +As an example, let's customize Joy UI's Button so to match the style of [Bootstrap](https://getbootstrap.com/docs/5.2/components/buttons/#examples): + +- Bootstrap's default buttons are comparable to Joy UI's `solid` variant. +- Bootstrap's `secondary` variant uses a grey color, similar to Joy UI's `neutral`. +- Bootstrap's `btn-light` is similar to Joy UI's button using the `soft` variant and `neutral` color palette. +- Joy UI's defaults don't include anything similar to Bootstrap's `btn-dark`. + - We can recreate it using one of the three main customization approaches. + +{{"demo": "BootstrapVariantTokens.js"}} + +:::info +Customizing the global variant tokens affects all Joy UI components that support the `variant` prop. +::: + +## Removing the default tokens + +To remove any default token, use `undefined` as a value. +This removes it from the `theme` object and prevents the corresponding CSS variable from being generated. + +For example, all default global variant tokens comes with styles for the `:active` pseudo class. +Here's how you'd remove it from the solid variant. + +```jsx +// ⚠️ If the value is `undefined`, it should be `undefined` for all color schemes. +const theme = extendTheme({ + colorSchemes: { + light: { + palette: { + primary: { + solidActiveBg: undefined, + }, + }, + }, + dark: { + palette: { + primary: { + solidActiveBg: undefined, + }, + }, + }, + }, +}); +``` + +{{"demo": "RemoveActiveTokens.js"}} + +## Adding more colors + +Any custom tokens that you add to theme are available for use with both the `styled` and `sx` APIs. + +```js +extendTheme({ + colorSchemes: { + light: { + palette: { + // Example of new color tokens. + // We recommend to limit them to 3 levels deep-in this case: + // `palette.gradient.primary`. + gradient: { + primary: 'linear-gradient(to top, var(--joy-palette-primary-main), #000)', + }, + }, + }, + }, +}); + +// `sx` prop usage example: + + ); +} diff --git a/docs/data/joy/customization/theme-shadow/CustomShadowOnElement.tsx b/docs/data/joy/customization/theme-shadow/CustomShadowOnElement.tsx new file mode 100644 index 00000000000000..831e154568b7df --- /dev/null +++ b/docs/data/joy/customization/theme-shadow/CustomShadowOnElement.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import Button from '@mui/joy/Button'; + +export default function CustomShadowOnElement() { + return ( + + ); +} diff --git a/docs/data/joy/customization/theme-shadow/ShadowThemeViewer.js b/docs/data/joy/customization/theme-shadow/ShadowThemeViewer.js new file mode 100644 index 00000000000000..977f6e7a821717 --- /dev/null +++ b/docs/data/joy/customization/theme-shadow/ShadowThemeViewer.js @@ -0,0 +1,154 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import * as React from 'react'; +import useClipboardCopy from 'docs/src/modules/utils/useClipboardCopy'; +import { styled, extendTheme } from '@mui/joy/styles'; +import Box from '@mui/joy/Box'; +import Link from '@mui/joy/Link'; +import Typography from '@mui/joy/Typography'; +import Sheet from '@mui/joy/Sheet'; +import LightMode from '@mui/icons-material/LightModeOutlined'; +import DarkMode from '@mui/icons-material/DarkModeOutlined'; +import Check from '@mui/icons-material/CheckCircle'; + +const Table = styled('table')(({ theme }) => ({ + border: '1px solid', + borderColor: theme.vars.palette.divider, + borderRadius: theme.vars.radius.xs, + borderCollapse: 'separate', + borderSpacing: 0, + width: '100%', + overflowY: 'scroll', + th: { + textAlign: 'left', + padding: 12, + position: 'sticky', + top: 0, + zIndex: 1, + ...theme.variants.soft.neutral, + }, + td: { + verticalAlign: 'top', + padding: '8px 12px', + }, + tr: { + '&:hover': { + backgroundColor: theme.vars.palette.background.level1, + }, + '&:first-of-type': { + '& td': { paddingTop: 6 }, + }, + }, +})); +const defaultTheme = extendTheme(); + +export default function ShadowThemeViewer() { + const { copy, isCopied } = useClipboardCopy(); + const tokens = Object.keys(defaultTheme.shadow); + const formatShadowLayers = (shadow) => + React.Children.toArray( + shadow + .split(', ') + .reduce( + (result, curr, index, array) => + array.length - 1 !== index + ? [...result, `${curr},`,
] + : [...result, curr], + [], + ), + ); + + return ( + + + }> + Copied + + + + + + + + + + + + + {tokens.map((token) => ( + + + + + + + ))} + +
+ Token + + Value + + }> + Light + + + }> + Dark + +
+ {token} + + copy(token)} + > + {formatShadowLayers(defaultTheme.shadow[token])} + + + theme.shadow[token], + borderRadius: 'xs', + mr: 2, + }} + /> + + theme.shadow[token], + borderRadius: 'xs', + }} + /> +
+
+ ); +} diff --git a/docs/data/joy/customization/theme-shadow/ShadowThemeViewer.tsx b/docs/data/joy/customization/theme-shadow/ShadowThemeViewer.tsx new file mode 100644 index 00000000000000..7adba3da08e703 --- /dev/null +++ b/docs/data/joy/customization/theme-shadow/ShadowThemeViewer.tsx @@ -0,0 +1,153 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import * as React from 'react'; +import useClipboardCopy from 'docs/src/modules/utils/useClipboardCopy'; +import { styled, extendTheme, Shadow } from '@mui/joy/styles'; +import Box from '@mui/joy/Box'; +import Link from '@mui/joy/Link'; +import Typography from '@mui/joy/Typography'; +import Sheet from '@mui/joy/Sheet'; +import LightMode from '@mui/icons-material/LightModeOutlined'; +import DarkMode from '@mui/icons-material/DarkModeOutlined'; +import Check from '@mui/icons-material/CheckCircle'; + +const Table = styled('table')(({ theme }) => ({ + border: '1px solid', + borderColor: theme.vars.palette.divider, + borderRadius: theme.vars.radius.xs, + borderCollapse: 'separate', + borderSpacing: 0, + width: '100%', + overflowY: 'scroll', + th: { + textAlign: 'left', + padding: 12, + position: 'sticky', + top: 0, + zIndex: 1, + ...theme.variants.soft.neutral, + }, + td: { + verticalAlign: 'top', + padding: '8px 12px', + }, + tr: { + '&:hover': { + backgroundColor: theme.vars.palette.background.level1, + }, + '&:first-of-type': { + '& td': { paddingTop: 6 }, + }, + }, +})); +const defaultTheme = extendTheme(); + +export default function ShadowThemeViewer() { + const { copy, isCopied } = useClipboardCopy(); + const tokens = Object.keys(defaultTheme.shadow) as Array; + const formatShadowLayers = (shadow: string) => + React.Children.toArray( + shadow + .split(', ') + .reduce>( + (result, curr, index, array) => + array.length - 1 !== index + ? [...result, `${curr},`,
] + : [...result, curr], + [], + ), + ); + return ( + + + }> + Copied + + + + + + + + + + + + + {tokens.map((token) => ( + + + + + + + ))} + +
+ Token + + Value + + }> + Light + + + }> + Dark + +
+ {token} + + copy(token)} + > + {formatShadowLayers(defaultTheme.shadow[token])} + + + theme.shadow[token], + borderRadius: 'xs', + mr: 2, + }} + /> + + theme.shadow[token], + borderRadius: 'xs', + }} + /> +
+
+ ); +} diff --git a/docs/data/joy/customization/theme-shadow/theme-shadow.md b/docs/data/joy/customization/theme-shadow/theme-shadow.md new file mode 100644 index 00000000000000..99c03c08c49591 --- /dev/null +++ b/docs/data/joy/customization/theme-shadow/theme-shadow.md @@ -0,0 +1,158 @@ +# Theme shadow + +

Learn about the theme's default shadow and how to customize it.

+ +## Default tokens + +Joy UI uses a T-shirt scale (sm, md, lg, etc.) for defining shadows used by components such as [Card](/joy-ui/react-card/), [Menu](/joy-ui/react-menu/), and more. + +These tokens are grouped inside the `theme.shadow` node: + +{{"demo": "ShadowThemeViewer.js", "bg": "inline"}} + +## Customizing the default shadow + +Provide key-values to the `shadow` node to override the default shadows: + +```js +import { extendTheme } from '@mui/joy/styles'; + +const theme = extendTheme({ + shadow: { + xs: '{CSS box-shadow}', + sm: '{CSS box-shadow}', + md: '{CSS box-shadow}', + lg: '{CSS box-shadow}', + xl: '{CSS box-shadow}', + }, +}); + +// Then, pass it to ``. +``` + +:::success +We recommend using `var(--joy-shadowRing)` and `var(--joy-shadowChannel)` for shadow values, similar to the [default token value](#default-tokens). +::: + +## Adding new shadows + +You can add any custom keys to the `shadow` node: + +```js +import { extendTheme } from '@mui/joy/styles'; + +const theme = extendTheme({ + shadow: { + subtle: '{CSS box-shadow}', + strong: '{CSS box-shadow}', + }, +}); + +// Then, pass it to ``. +``` + +### TypeScript + +When working in TypeScript, you need to augment the theme's `Shadow` interface with the new keys: + +```ts +// You can put this to any file that's included in your tsconfig +declare module '@mui/joy/styles' { + interface Shadow { + subtle: string; + strong: string; + } +} +``` + +## Shadow ring + +The shadow ring can be configured for both light and dark color schemes. +To create a shadow ring, provide a valid CSS box-shadow value to the `shadowRing` node: + +```js +import { extendTheme } from '@mui/joy/styles'; + +const theme = extendTheme({ + colorSchemes: { + light: { + // This creates a 1px box-shadow. + shadowRing: '0 0 0 1px rgba(0 0 0 / 0.1)', + }, + dark: { + shadowChannel: '0 0 0 1px rgba(255 255 255 / 0.1)', + }, + }, +}); + +// Then, pass it to ``. +``` + +:::warning +Customizing the theme's shadow ring will affect all Joy UI components that consume the theme's shadows. + +If you want to create a shadow ring for a specific element, see [Customizing shadows on an element](#customizing-shadows-on-an-element). +::: + +## Shadow colors + +The color of the shadow comes from the theme token named `var(--joy-shadowChannel)`. +You can customize the value for both light and dark color schemes: + +```js +import { extendTheme } from '@mui/joy/styles'; + +const theme = extendTheme({ + colorSchemes: { + light: { + shadowChannel: '12 12 12', + }, + dark: { + shadowChannel: '0 0 0', + }, + }, +}); + +// Then, pass it to ``. +``` + +:::warning +The `shadowChannel` value must be rgb channels, e.g. `187 187 187`. +::: + +## Customizing shadows on an element + +To customize a shadow color or shadow ring on a specific instance, use the raw value from the `theme.shadow.*`. + +:::warning +**Don't** use shadows from `theme.vars` or the shorthand syntax `{ shadow: '{key}' }` because the value points to the global CSS variable which does not work with the custom `shadowChannel` and `shadowRing` on the instance. +::: + +```js +// ✅ + - - The shadow applied - -
- - - - No custom shadow - - -
- - ); -} diff --git a/docs/data/joy/customization/theme-tokens/theme-tokens-pt.md b/docs/data/joy/customization/theme-tokens/theme-tokens-pt.md deleted file mode 100644 index a9352acfdae468..00000000000000 --- a/docs/data/joy/customization/theme-tokens/theme-tokens-pt.md +++ /dev/null @@ -1,355 +0,0 @@ -# Theme tokens - -

Learn about the two categories of tokens within Joy UI's default theme and how to customize them.

- -The [W3C Community Group](https://github.com/design-tokens/community-group) defines design tokens as: _"...indivisible pieces of a design system such as colors, spacing, typography scale."_ Joy UI builds up on this concept to develop its theme, consisting of two categories: low-level and global variant tokens. - -## Low-level tokens - -Low-level tokens refer to the smallest units of style that defines the look and feel Joy UI has out-of-the-box. They're labeled as _low-level_ because they can be used to compose larger tokens, such as the typography scale. - -### Structure - -Joy UI's default theme has three main categories of low-level design tokens: - -1. Color -2. Typography -3. Shape-related - -#### Color - -The first theme node within the color category is `colorSchemes`. It houses the `light` and `dark` nodes, and inside each one of them, there is a `palette` node, containing the [global variant tokens](#global-variant-tokens) adjusted for both modes. - -```js -colorSchemes: { - light: { - palette: { - primary: { - plainColor: 'valid CSS color', - plainHoverBg: 'valid CSS color', - plainActiveBg: 'valid CSS color', - }, - neutral: {...}, - ... - }, - }, - dark: { - palette: { - primary: { - plainColor: 'valid CSS color', - plainHoverBg: 'valid CSS color', - plainActiveBg: 'valid CSS color', - }, - neutral: {...}, - ... - }, - }, -} -``` - -Visit the [ColorSystem interface](https://github.com/mui/material-ui/blob/master/packages/mui-joy/src/styles/types/colorSystem.ts#L142) to see all of the available interfaces. - -#### Channel tokens - -The tokens ended with `Channel` are automatically generated from the provided theme unless you explicitly specify them. These tokens are useful for creating translucent (alpha) colors. - -```js -import Typography from '@mui/joy/Typography'; - - ({ - color: `rgba(${theme.vars.palette.primary.mainChannel} / 0.72)`, - })} -> -``` - -#### Typography - -Within the typography-related tokens, there are first the ones that map out to common CSS typography properties: - -```js -fontSize: {...}, -fontFamily: {...}, -fontWeight: {...}, -lineHeight: {...}, -letterSpacing: {...}, -``` - -They're then used to build up Joy UI's typographic scale: - -```js -typography: { - h1: { - fontFamily: 'var(--joy-fontFamily-display)', - fontWeight: 'var(--joy-fontWeight-lg)' as CSSProperties['fontWeight'], - fontSize: 'var(--joy-fontSize-xl4)', - lineHeight: 'var(--joy-lineHeight-sm)', - letterSpacing: 'var(--joy-letterSpacing-sm)', - color: 'var(--joy-palette-text-primary)', - }, - h2: {...}, - h3: {...}, - ... -} -``` - -#### Shape - -The two main theme nodes related to shape elements are: - -```js -radius: {...}, -shadow: {...}, -``` - -### Overriding low-level tokens - -To customize the theme's low-level design tokens, use the `extendTheme` API to create a new theme and then pass it to the `CssVarsProvider`. The specified tokens will be deeply merged into the default values. - -```js -import { CssVarsProvider, extendTheme } from '@mui/joy/styles'; - -const theme = extendTheme({ - colorSchemes: { - light: { - palette: { - background: { - // palette.neutral.50 is the default token - body: 'var(--joy-palette-neutral-50)', - }, - }, - }, - }, -}); - -function App() { - return ...; -} -``` - -:::info -**Note**: Joy UI will add the prefix (default as `joy`) to all CSS variables. To change it, use ``. and the generated CSS variables will then be: - -```diff -- --joy-palette-primary-50: /* color */ ; -+ --myproduct-palette-primary-50: /* color */ ; -``` - -::: - -### Adding low-level tokens - -You can add any custom tokens to the theme and still be able to use them in APIs like `styled` and `sx` prop. - -```ts -extendTheme({ - colorSchemes: { - light: { - palette: { - // Example of new color tokens. - // We recommend to limit them to 3 levels deep-in this case `palette.brand.primary`. - brand: { - primary: 'green', - secondary: 'red', - }, - }, - }, - }, -}); -``` - -For **TypeScript**, you need to augment the theme structure to include the new tokens. - -```ts -import { CssVarsProvider, extendTheme } from '@mui/joy/styles'; - -declare module '@mui/joy/styles' { - interface Palette { - brand: { - primary: string; - secondary: string; - }; - } -} -``` - -After that, you can use those tokens in the `styled` function or the `sx` prop: - -```jsx -// sx prop -