1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
//! # Theming
//!
//! Freya has built-in support for Theming.
//!
//! <div class="warning">⚠️ As of 2023-12-19, extending the base theme is not supported.</div>
//!
//! ### Accessing the current theme
//!
//! You can access the current theme via the `use_get_theme` hook.
//!
//! ```rust, no_run
//! # use freya::prelude::*;
//!
//! fn app(cx: Scope) -> Element {
//! render!(
//! ThemeProvider {
//! Component { }
//! }
//! )
//! }
//!
//! #[allow(non_snake_case)]
//! fn Component(cx: Scope) -> Element {
//! let theme = use_get_theme(cx);
//!
//! let button_theme = &theme.button;
//!
//! render!(
//! rect {
//! background: "{button_theme.background}",
//! }
//! )
//! }
//! ```
//!
//! ## Custom default theme
//!
//! By default, the selected theme is `LIGHT_THEME`. You can use the alternative, `DARK_THEME`.
//!
//! ```rust, no_run
//! # use freya::prelude::*;
//!
//! fn app(cx: Scope) -> Element {
//! render!(
//! ThemeProvider {
//! theme: LIGHT_THEME,
//! Component { }
//! }
//! )
//! }
//!
//! #[allow(non_snake_case)]
//! fn Component(cx: Scope) -> Element {
//! let theme = use_get_theme(cx);
//!
//! let button_theme = &theme.button;
//!
//! render!(
//! rect {
//! background: "{button_theme.background}",
//! }
//! )
//! }
//! ```
//!
//! ## Change the theme
//!
//! Changing the selected theme at runtime is possible by using the `use_theme` hook.
//!
//! ```rust, no_run
//! # use freya::prelude::*;
//!
//! fn app(cx: Scope) -> Element {
//! render!(
//! ThemeProvider {
//! Component { }
//! }
//! )
//! }
//!
//! #[allow(non_snake_case)]
//! fn Component(cx: Scope) -> Element {
//! let theme = use_theme(cx);
//!
//! let onclick = |_| {
//! *theme.write() = LIGHT_THEME;
//! };
//!
//! render!(
//! Button {
//! onclick: onclick,
//! label {
//! "Use Light theme"
//! }
//! }
//! )
//! }
//! ```
//!
//! ## Change theme for an individual component
//!
//! Most built-in components have their own theme "override."
//! You can specify values to override like this:
//!
//! ```rust,no_run
//! # use freya::prelude::*;
//!
//! fn app(cx: Scope) -> Element {
//! render! {
//! Button {
//! theme: ButtonThemeWith {
//! background: Some("blue".into()),
//! font_theme: Some(FontThemeWith {
//! color: Some("white".into()),
//! ..Default::default()
//! }),
//! ..Default::default()
//! },
//! label { "I'm blue now" }
//! }
//! }
//! }
//! ```
//!
//! You need to use a different "type" of theme.
//! In the "ThemeWith" structs, each field is optional, so that the component knows what to override and
//! what to keep.
//! Also, you need to spread `..Default::default`, to make all the other fields `None`.
//!
//! To make this less verbose, you can use the `theme_with!` macro:
//!
//! ```rust,no_run
//! # use freya::prelude::*;
//!
//! fn app(cx: Scope) -> Element {
//! render! {
//! Button {
//! theme: theme_with!(ButtonTheme {
//! background: "blue".into(),
//! font_theme: theme_with!(FontTheme {
//! color: "white".into(),
//! }),
//! }),
//! label { "I'm blue now" }
//! }
//! }
//! }
//! ```
//!
//! <div class="warning">⚠️ The comma after the last field in the `theme_with!` macro is required.</div>
//!
//! As you can see, it removes the need for the "With" suffix, because that is already in the macro name.
//! More importantly, though, it wraps each field in a `Some`, and adds the spread.
//!
//! ## Custom theme
//!
//! You can build themes from scratch or extended from others, like here with `LIGHT_THEME`:
//!
//! ```rust, no_run
//! # use freya::prelude::*;
//!
//! const CUSTOM_THEME: Theme = Theme {
//! button: ButtonTheme {
//! background: Cow::Borrowed("rgb(230, 0, 0)"),
//! hover_background: Cow::Borrowed("rgb(150, 0, 0)"),
//! font_theme: FontTheme {
//! color: Cow::Borrowed("white"),
//! },
//! ..LIGHT_THEME.button
//! },
//! ..LIGHT_THEME
//! };
//!
//! fn app(cx: Scope) -> Element {
//! render!(
//! ThemeProvider {
//! theme: CUSTOM_THEME,
//! rect {
//! width: "100%",
//! height: "100%",
//! Button {
//! label {
//! "Report"
//! }
//! }
//! }
//! }
//! )
//! }
//! ```