Skip to content

Commit

Permalink
[docs] Add a guide for setting dark mode by default (mui#34839)
Browse files Browse the repository at this point in the history
  • Loading branch information
siriwatknp authored and Daniel Rabe committed Nov 29, 2022
1 parent 269ef3c commit 8f61b1b
Show file tree
Hide file tree
Showing 17 changed files with 331 additions and 19 deletions.
37 changes: 37 additions & 0 deletions docs/data/joy/customization/dark-mode/DarkModeByDefault.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react';
import { CssVarsProvider } from '@mui/joy/styles';
import Sheet from '@mui/joy/Sheet';
import Chip from '@mui/joy/Chip';
import Typography from '@mui/joy/Typography';

export default function DarkModeByDefault() {
return (
<CssVarsProvider
defaultMode="dark"
// the props below are specific to this demo,
// you might not need them in your app.
//
// the selector to apply CSS theme variables stylesheet.
colorSchemeSelector="#demo_dark-mode-by-default"
//
// the local storage key to use
modeStorageKey="demo_dark-mode-by-default"
>
<div id="demo_dark-mode-by-default">
<Sheet sx={{ px: 3, py: 1.5, borderRadius: 'sm' }}>
<Typography
component="div"
endDecorator={
<Chip variant="outlined" color="primary" size="sm">
Default
</Chip>
}
fontSize="lg"
>
Dark mode
</Typography>
</Sheet>
</div>
</CssVarsProvider>
);
}
37 changes: 37 additions & 0 deletions docs/data/joy/customization/dark-mode/DarkModeByDefault.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react';
import { CssVarsProvider } from '@mui/joy/styles';
import Sheet from '@mui/joy/Sheet';
import Chip from '@mui/joy/Chip';
import Typography from '@mui/joy/Typography';

export default function DarkModeByDefault() {
return (
<CssVarsProvider
defaultMode="dark"
// the props below are specific to this demo,
// you might not need them in your app.
//
// the selector to apply CSS theme variables stylesheet.
colorSchemeSelector="#demo_dark-mode-by-default"
//
// the local storage key to use
modeStorageKey="demo_dark-mode-by-default"
>
<div id="demo_dark-mode-by-default">
<Sheet sx={{ px: 3, py: 1.5, borderRadius: 'sm' }}>
<Typography
component="div"
endDecorator={
<Chip variant="outlined" color="primary" size="sm">
Default
</Chip>
}
fontSize="lg"
>
Dark mode
</Typography>
</Sheet>
</div>
</CssVarsProvider>
);
}
51 changes: 51 additions & 0 deletions docs/data/joy/customization/dark-mode/IdentifySystemMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
import Typography from '@mui/joy/Typography';

const Identifier = () => {
const { systemMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<Typography component="div" fontSize="lg" sx={{ opacity: 0 }}>
Calculating…
</Typography>
);
}
return (
<Typography component="div" fontSize="lg">
Your system is in{' '}
<Typography
variant="outlined"
fontSize="md"
sx={{
boxShadow: 'sm',
py: 0.25,
fontFamily: 'code',
bgcolor: 'background.level1',
}}
>
{systemMode}
</Typography>{' '}
mode.
</Typography>
);
};

export default function IdentifySystemMode() {
return (
<CssVarsProvider
defaultMode="system"
// The props below are specific to this demo,
// you might not need them in your app.
//
// the local storage key to use.
modeStorageKey="demo_identify-system-mode"
>
<Identifier />
</CssVarsProvider>
);
}
51 changes: 51 additions & 0 deletions docs/data/joy/customization/dark-mode/IdentifySystemMode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
import Typography from '@mui/joy/Typography';

const Identifier = () => {
const { systemMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<Typography component="div" fontSize="lg" sx={{ opacity: 0 }}>
Calculating…
</Typography>
);
}
return (
<Typography component="div" fontSize="lg">
Your system is in{' '}
<Typography
variant="outlined"
fontSize="md"
sx={{
boxShadow: 'sm',
py: 0.25,
fontFamily: 'code',
bgcolor: 'background.level1',
}}
>
{systemMode}
</Typography>{' '}
mode.
</Typography>
);
};

export default function IdentifySystemMode() {
return (
<CssVarsProvider
defaultMode="system"
// The props below are specific to this demo,
// you might not need them in your app.
//
// the local storage key to use.
modeStorageKey="demo_identify-system-mode"
>
<Identifier />
</CssVarsProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<CssVarsProvider
defaultMode="system"
// The props below are specific to this demo,
// you might not need them in your app.
//
// the local storage key to use.
modeStorageKey="demo_identify-system-mode"
>
<Identifier />
</CssVarsProvider>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Button from '@mui/joy/Button';
const useEnhancedEffect =
typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;

function ModeToggle() {
function ModeSwitcher() {
const { mode, setMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);

Expand All @@ -28,19 +28,27 @@ function ModeToggle() {
);
}

export default function BootstrapVariantTokens() {
// the `node` is used for attaching CSS variables to this demo, you might not need it in your application.
export default function ModeToggle() {
// the `node` is used for attaching CSS variables to this demo,
// you might not need it in your application.
const [node, setNode] = React.useState(null);
useEnhancedEffect(() => {
setNode(document.getElementById('mode-toggle'));
}, []);

return (
<CssVarsProvider
// the props below are specific to this demo,
// you might not need them in your app.
//
// the element to apply [data-joy-color-scheme] attribute.
colorSchemeNode={node || null}
modeStorageKey="mode-toggle-demo"
colorSchemeStorageKey="mode-toggle-demo"
//
// the selector to apply the CSS theme variables stylesheet.
colorSchemeSelector="#mode-toggle"
//
// the local storage key to use.
modeStorageKey="mode-toggle-demo"
>
<Box
id="mode-toggle"
Expand All @@ -49,11 +57,11 @@ export default function BootstrapVariantTokens() {
flexGrow: 1,
p: 2,
m: -3,
borderRadius: 'sm',
borderRadius: [0, 'sm'],
bgcolor: 'background.body',
}}
>
<ModeToggle />
<ModeSwitcher />
</Box>
</CssVarsProvider>
);
Expand Down
68 changes: 68 additions & 0 deletions docs/data/joy/customization/dark-mode/ModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as React from 'react';
import { CssVarsProvider, useColorScheme } from '@mui/joy/styles';
import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';

const useEnhancedEffect =
typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;

function ModeSwitcher() {
const { mode, setMode } = useColorScheme();
const [mounted, setMounted] = React.useState(false);

React.useEffect(() => {
setMounted(true);
}, []);

if (!mounted) {
return null;
}
return (
<Button
variant="outlined"
color="neutral"
onClick={() => setMode(mode === 'dark' ? 'light' : 'dark')}
>
{mode === 'dark' ? 'Turn light' : 'Turn dark'}
</Button>
);
}

export default function ModeToggle() {
// the `node` is used for attaching CSS variables to this demo,
// you might not need it in your application.
const [node, setNode] = React.useState<HTMLElement | null>(null);
useEnhancedEffect(() => {
setNode(document.getElementById('mode-toggle'));
}, []);

return (
<CssVarsProvider
// the props below are specific to this demo,
// you might not need them in your app.
//
// the element to apply [data-joy-color-scheme] attribute.
colorSchemeNode={node || null}
//
// the selector to apply the CSS theme variables stylesheet.
colorSchemeSelector="#mode-toggle"
//
// the local storage key to use.
modeStorageKey="mode-toggle-demo"
>
<Box
id="mode-toggle"
sx={{
textAlign: 'center',
flexGrow: 1,
p: 2,
m: -3,
borderRadius: [0, 'sm'],
bgcolor: 'background.body',
}}
>
<ModeSwitcher />
</Box>
</CssVarsProvider>
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,58 @@
# Applying dark mode
# Dark mode

<p class="description">A how-to-guide for applying dark mode to your application with Joy UI.</p>
<p class="description">Learn about the different methods for applying dark mode to a Joy UI app.</p>

## The mode-toggle component
## Set as default

To set dark mode as the default for your app, add `defaultMode: 'dark'` to your `<CssVarsProvider>` wrapper component:

{{"demo": "DarkModeByDefault.js"}}

For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `getInitColorSchemeScript` function:

```js
getInitColorSchemeScript({ defaultMode: 'dark' });
```

## Matching device's preference

Use `defaultMode: 'system'` to set your app's default mode to match the user's chosen preference on their device.

```jsx
import { CssVarsProvider } from '@mui/joy/styles';

<CssVarsProvider defaultMode="system">...</CssVarsProvider>;
```

For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `getInitColorSchemeScript` function:

```js
getInitColorSchemeScript({ defaultMode: 'system' });
```

### Identify the system mode

Use the `useColorScheme` React hook to check if the user's preference is in light or dark mode:

```js
import { useColorScheme } from '@mui/joy/styles';

function SomeComponent() {
const { mode, systemMode } = useColorScheme();
console.log(mode); // "system"
console.log(systemMode); // "light" | "dark" based on the user's preference.
}
```

{{"demo": "IdentifySystemMode.js"}}

:::warning
The `useColorScheme()` hook only works with components nested inside of `<CssVarsProvider>`—otherwise it will throw an error.
:::

## Creating a mode-toggle component

You can create a toggle component to give users the option to select between modes.

In the example below, we're using a `Button` component that calls `setMode` from the `useColorSchemes()` hook to handle the mode toggling.

Expand All @@ -27,10 +77,10 @@ function ModeToggle() {
{{"demo": "ModeToggle.js"}}

:::warning
**Note:** Make sure to use `useColorScheme()` in a component that's inside `<CssVarsProvider>`, otherwise it will throw an error.
The `useColorScheme()` hook only works with components nested inside of `<CssVarsProvider>`otherwise it will throw an error.
:::

## Server-side rendering
## Server-side rendering notes

### Avoid hydration mismatch

Expand Down
2 changes: 1 addition & 1 deletion docs/data/joy/getting-started/tutorial/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ Here are some of the major features introduced:

- [global variants](/joy-ui/main-features/global-variants/)
- [the `sx` prop](/system/getting-started/the-sx-prop/)
- [dark mode](/joy-ui/guides/applying-dark-mode/)
- [dark mode](/joy-ui/customization/dark-mode/)

## Next steps

Expand Down

0 comments on commit 8f61b1b

Please sign in to comment.