From 87301834b34fa5c6ee65c6e89b1d912e43c8b771 Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 9 Aug 2023 10:55:42 -0400 Subject: [PATCH] chore: add light/dark mode theme detection to image component example (#53760) This PR adds documentation for light/dark mode detection with `next/image`. In the future, we could also document the picture solution once https://github.com/vercel/next.js/pull/51205 goes stable (although some of the preloading would not be possible). * x-ref: https://twitter.com/victorbayas/status/1688596439704780822 --- .../02-api-reference/01-components/image.mdx | 60 +++++++++++++++++++ examples/image-component/pages/index.tsx | 3 + examples/image-component/pages/theme.tsx | 38 ++++++++++++ examples/image-component/styles.module.css | 14 +++++ 4 files changed, 115 insertions(+) create mode 100644 examples/image-component/pages/theme.tsx diff --git a/docs/02-app/02-api-reference/01-components/image.mdx b/docs/02-app/02-api-reference/01-components/image.mdx index b4dfa279d0e5..5e5c5df10fe9 100644 --- a/docs/02-app/02-api-reference/01-components/image.mdx +++ b/docs/02-app/02-api-reference/01-components/image.mdx @@ -725,6 +725,66 @@ Try it out: - [Demo the `fill` prop](https://image-component.nextjs.gallery/fill) +## Theme Detection + +If you want to display a different image for light and dark mode, you can create a new component that wraps two `` components and reveals the correct one based on a CSS media query. + +```css filename="components/theme-image.module.css" +.imgDark { + display: none; +} + +@media (prefers-color-scheme: dark) { + .imgLight { + display: none; + } + .imgDark { + display: unset; + } +} +``` + +```tsx filename="components/theme-image.tsx" switcher +import styles from './theme-image.module.css' +import Image, { ImageProps } from 'next/image' + +type Props = Omit & { + srcLight: string + srcDark: string +} + +const ThemeImage = (props: Props) => { + const { srcLight, srcDark, ...rest } = props + + return ( + <> + + + + ) +} +``` + +```jsx filename="components/theme-image.js" switcher +import styles from './theme-image.module.css' +import Image from 'next/image' + +const ThemeImage = (props) => { + const { srcLight, srcDark, ...rest } = props + + return ( + <> + + + + ) +} +``` + +> **Good to know**: The default behavior of `loading="lazy"` ensures that only the correct image is loaded. You cannot use `priority` or `loading="eager"` because that would cause both images to load. Instead, you can use [`fetchPriority="high"`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority). + +- [Demo light/dark mode theme detection](https://image-component.nextjs.gallery/theme) + ## Known Browser Bugs This `next/image` component uses browser native [lazy loading](https://caniuse.com/loading-lazy-attr), which may fallback to eager loading for older browsers before Safari 15.4. When using the blur-up placeholder, older browsers before Safari 12 will fallback to empty placeholder. When using styles with `width`/`height` of `auto`, it is possible to cause [Layout Shift](https://web.dev/cls/) on older browsers before Safari 15 that don't [preserve the aspect ratio](https://caniuse.com/mdn-html_elements_img_aspect_ratio_computed_from_attributes). For more details, see [this MDN video](https://www.youtube.com/watch?v=4-d_SoCHeWE). diff --git a/examples/image-component/pages/index.tsx b/examples/image-component/pages/index.tsx index 0b044a1da40a..6e5ff47fac00 100644 --- a/examples/image-component/pages/index.tsx +++ b/examples/image-component/pages/index.tsx @@ -50,6 +50,9 @@ const Index = () => (
  • Color placeholder
  • +
  • + Light/Dark mode theme detection +
  • Text on background image
  • diff --git a/examples/image-component/pages/theme.tsx b/examples/image-component/pages/theme.tsx new file mode 100644 index 000000000000..5b67f277cb9f --- /dev/null +++ b/examples/image-component/pages/theme.tsx @@ -0,0 +1,38 @@ +import Image, { ImageProps } from 'next/image' +import ViewSource from '../components/view-source' +import styles from '../styles.module.css' + +// Note: we cannot use `priority` or `loading="eager" +// because we depend on the default `loading="lazy"` +// behavior to wait for CSS to reveal the proper image. +type Props = Omit & { + srcLight: string + srcDark: string +} + +const ThemeImage = (props: Props) => { + const { srcLight, srcDark, ...rest } = props + + return ( + <> + + + + ) +} + +const Page = () => ( +
    + +

    Image With Light/Dark Theme Detection

    + +
    +) + +export default Page diff --git a/examples/image-component/styles.module.css b/examples/image-component/styles.module.css index 2e3e92d886a7..9a6e1fb1c4d5 100644 --- a/examples/image-component/styles.module.css +++ b/examples/image-component/styles.module.css @@ -48,3 +48,17 @@ padding-top: 40vh; text-shadow: 1px 1px 1px #3c5c5e; } + +.imgDark { + display: none; +} + +@media (prefers-color-scheme: dark) { + .imgLight { + display: none; + } + + .imgDark { + display: unset; + } +}