Skip to main content

leptos_use/
use_preferred_dark.rs

1use crate::utils::get_header;
2use default_struct_builder::DefaultBuilder;
3use leptos::prelude::*;
4use std::sync::Arc;
5
6/// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
7///
8/// ## Usage
9///
10/// ```
11/// # use leptos::prelude::*;
12/// # use leptos_use::use_preferred_dark;
13/// #
14/// # #[component]
15/// # fn Demo() -> impl IntoView {
16/// #
17/// let is_dark_preferred = use_preferred_dark();
18/// #
19/// #    view! { }
20/// # }
21/// ```
22///
23/// ## Server-Side Rendering
24///
25/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
26///
27/// On the server this will try to read the
28/// [`Sec-CH-Prefers-Color-Scheme` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
29/// to determine the color mode. If the header is not present it will return `ColorMode::Light`.
30/// Please have a look at the linked documentation above for that header to see browser support
31/// as well as potential server requirements.
32///
33/// > If you're using `axum` you have to enable the `"axum"` feature in your Cargo.toml.
34/// > In case it's `actix-web` enable the feature `"actix"`.
35///
36/// ### Bring your own header
37///
38/// In case you're neither using Axum nor Actix or the default implementation is not to your liking,
39/// you can provide your own way of reading the color scheme header value using the option
40/// [`crate::UsePreferredDarkOptions::ssr_color_header_getter`].
41///
42/// ## See also
43///
44/// * [`fn@crate::use_media_query`]
45/// * [`fn@crate::use_preferred_contrast`]
46/// * [`fn@crate::use_prefers_reduced_motion`]
47pub fn use_preferred_dark() -> Signal<bool> {
48    use_preferred_dark_with_options(Default::default())
49}
50
51/// Version of [`fn@crate::use_preferred_dark`] that accepts a `UsePreferredDarkOptions`.
52pub fn use_preferred_dark_with_options(options: UsePreferredDarkOptions) -> Signal<bool> {
53    #[cfg(not(feature = "ssr"))]
54    {
55        let _ = options;
56
57        crate::use_media_query("(prefers-color-scheme: dark)")
58    }
59
60    #[cfg(feature = "ssr")]
61    {
62        let color_header = (options.ssr_color_header_getter)();
63        Signal::derive(move || color_header == Some("dark".to_string()))
64    }
65}
66
67/// Options for [`fn@crate::use_preferred_dark_with_options`].
68#[derive(DefaultBuilder)]
69pub struct UsePreferredDarkOptions {
70    /// Getter function to return the string value of the
71    /// [`Sec-CH-Prefers-Color-Scheme`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
72    /// header.
73    /// When you use one of the features `"axum"` or `"actix"` there's a valid default
74    /// implementation provided.
75    #[allow(dead_code)]
76    pub(crate) ssr_color_header_getter: Arc<dyn Fn() -> Option<String> + Send + Sync>,
77}
78
79impl Default for UsePreferredDarkOptions {
80    fn default() -> Self {
81        Self {
82            ssr_color_header_getter: Arc::new(move || {
83                get_header!(
84                    HeaderName::from_static("sec-ch-prefers-color-scheme"),
85                    use_preferred_dark,
86                    ssr_color_header_getter
87                )
88            }),
89        }
90    }
91}