app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 0fd5440ae09738080c9b4851a7a1946dcc71e83a
parent a6f3c8dee48ab9adf52c067975593228ea80a21b
Author: triesap <triesap@radroots.dev>
Date:   Wed, 21 Jan 2026 19:57:34 +0000

ui: add motion and contrast preference helpers

- add preference helpers with explicit error types

- use matchMedia for wasm preference queries

- add unit tests for preference helpers

- enable web-sys media query support

Diffstat:
Mcrates/ui-core/Cargo.toml | 1+
Mcrates/ui-core/src/lib.rs | 7+++++++
Acrates/ui-core/src/preference.rs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 66 insertions(+), 0 deletions(-)

diff --git a/crates/ui-core/Cargo.toml b/crates/ui-core/Cargo.toml @@ -19,6 +19,7 @@ web-sys = { workspace = true, features = [ "EventTarget", "HtmlElement", "KeyboardEvent", + "MediaQueryList", "PointerEvent", "Window" ] } diff --git a/crates/ui-core/src/lib.rs b/crates/ui-core/src/lib.rs @@ -6,6 +6,7 @@ extern crate alloc; mod id; mod event; mod input; +mod preference; pub use event::{ radroots_app_ui_compose_event_handlers, @@ -19,3 +20,9 @@ pub use input::{ RadrootsAppUiInputModality, RadrootsAppUiInputModalityError, }; +pub use preference::{ + radroots_app_ui_prefers_contrast_more, + radroots_app_ui_prefers_reduced_motion, + RadrootsAppUiPreferenceError, + RadrootsAppUiPreferenceResult, +}; diff --git a/crates/ui-core/src/preference.rs b/crates/ui-core/src/preference.rs @@ -0,0 +1,58 @@ +use core::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RadrootsAppUiPreferenceError { + WindowMissing, + MatchMediaFailed, +} + +impl fmt::Display for RadrootsAppUiPreferenceError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RadrootsAppUiPreferenceError::WindowMissing => { + write!(f, "preference_window_missing") + } + RadrootsAppUiPreferenceError::MatchMediaFailed => { + write!(f, "preference_match_media_failed") + } + } + } +} + +pub type RadrootsAppUiPreferenceResult<T> = Result<T, RadrootsAppUiPreferenceError>; + +#[cfg(target_arch = "wasm32")] +fn radroots_app_ui_prefers(query: &str) -> RadrootsAppUiPreferenceResult<bool> { + let window = web_sys::window().ok_or(RadrootsAppUiPreferenceError::WindowMissing)?; + let media = window + .match_media(query) + .map_err(|_| RadrootsAppUiPreferenceError::MatchMediaFailed)?; + Ok(media.map(|list| list.matches()).unwrap_or(false)) +} + +#[cfg(not(target_arch = "wasm32"))] +fn radroots_app_ui_prefers(_query: &str) -> RadrootsAppUiPreferenceResult<bool> { + Ok(false) +} + +pub fn radroots_app_ui_prefers_reduced_motion() -> RadrootsAppUiPreferenceResult<bool> { + radroots_app_ui_prefers("(prefers-reduced-motion: reduce)") +} + +pub fn radroots_app_ui_prefers_contrast_more() -> RadrootsAppUiPreferenceResult<bool> { + radroots_app_ui_prefers("(prefers-contrast: more)") +} + +#[cfg(test)] +mod tests { + use super::{ + radroots_app_ui_prefers_contrast_more, + radroots_app_ui_prefers_reduced_motion, + }; + + #[test] + fn prefers_helpers_return_result() { + let _ = radroots_app_ui_prefers_reduced_motion().expect("reduced motion"); + let _ = radroots_app_ui_prefers_contrast_more().expect("contrast more"); + } +}