Skip to main content

leptos_use/
use_prefers_reduced_motion.rs

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