ui: Make UI independent from settings crate (#52578)

Piotr Osiewicz and Zed Zippy created

This will allow us to use UI crate on the web

Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A

---------

Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>

Change summary

Cargo.lock                                   |  1 
crates/theme/src/theme.rs                    |  4 +
crates/theme/src/theme_settings_provider.rs  | 43 +++++++++++
crates/theme/src/ui_density.rs               | 65 +++++++++++++++++
crates/theme_settings/src/settings.rs        | 80 +--------------------
crates/theme_settings/src/theme_settings.rs  | 42 +++++++++--
crates/ui/Cargo.toml                         |  1 
crates/ui/src/components/context_menu.rs     |  4 
crates/ui/src/components/label/label_like.rs | 14 ---
crates/ui/src/components/list/list_header.rs |  7 -
crates/ui/src/components/tooltip.rs          |  7 -
crates/ui/src/styles/spacing.rs              |  5 
crates/ui/src/styles/typography.rs           | 26 +++---
crates/ui_macros/src/dynamic_spacing.rs      | 18 ++--
14 files changed, 185 insertions(+), 132 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -18840,7 +18840,6 @@ dependencies = [
  "story",
  "strum 0.27.2",
  "theme",
- "theme_settings",
  "ui_macros",
  "windows 0.61.3",
 ]

crates/theme/src/theme.rs 🔗

@@ -17,6 +17,8 @@ mod registry;
 mod scale;
 mod schema;
 mod styles;
+mod theme_settings_provider;
+mod ui_density;
 
 use std::sync::Arc;
 
@@ -37,6 +39,8 @@ pub use crate::registry::*;
 pub use crate::scale::*;
 pub use crate::schema::*;
 pub use crate::styles::*;
+pub use crate::theme_settings_provider::*;
+pub use crate::ui_density::*;
 
 /// The name of the default dark theme.
 pub const DEFAULT_DARK_THEME: &str = "One Dark";

crates/theme/src/theme_settings_provider.rs 🔗

@@ -0,0 +1,43 @@
+use gpui::{App, Font, Global, Pixels};
+
+use crate::UiDensity;
+
+/// Trait for providing theme-related settings (fonts, font sizes, UI density)
+/// without coupling to the concrete settings infrastructure.
+///
+/// A concrete implementation is registered as a global by the `theme_settings` crate.
+pub trait ThemeSettingsProvider: Send + Sync + 'static {
+    /// Returns the font used for UI elements.
+    fn ui_font<'a>(&'a self, cx: &'a App) -> &'a Font;
+
+    /// Returns the font used for buffers and the terminal.
+    fn buffer_font<'a>(&'a self, cx: &'a App) -> &'a Font;
+
+    /// Returns the UI font size in pixels.
+    fn ui_font_size(&self, cx: &App) -> Pixels;
+
+    /// Returns the buffer font size in pixels.
+    fn buffer_font_size(&self, cx: &App) -> Pixels;
+
+    /// Returns the current UI density setting.
+    fn ui_density(&self, cx: &App) -> UiDensity;
+}
+
+struct GlobalThemeSettingsProvider(Box<dyn ThemeSettingsProvider>);
+
+impl Global for GlobalThemeSettingsProvider {}
+
+/// Registers the global [`ThemeSettingsProvider`] implementation.
+///
+/// This should be called during application initialization by the crate
+/// that owns the concrete theme settings (e.g. `theme_settings`).
+pub fn set_theme_settings_provider(provider: Box<dyn ThemeSettingsProvider>, cx: &mut App) {
+    cx.set_global(GlobalThemeSettingsProvider(provider));
+}
+
+/// Returns the global [`ThemeSettingsProvider`].
+///
+/// Panics if no provider has been registered via [`set_theme_settings_provider`].
+pub fn theme_settings(cx: &App) -> &dyn ThemeSettingsProvider {
+    &*cx.global::<GlobalThemeSettingsProvider>().0
+}

crates/theme/src/ui_density.rs 🔗

@@ -0,0 +1,65 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+
+/// Specifies the density of the UI.
+/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
+#[derive(
+    Debug,
+    Default,
+    PartialEq,
+    Eq,
+    PartialOrd,
+    Ord,
+    Hash,
+    Clone,
+    Copy,
+    Serialize,
+    Deserialize,
+    JsonSchema,
+)]
+#[serde(rename_all = "snake_case")]
+pub enum UiDensity {
+    /// A denser UI with tighter spacing and smaller elements.
+    #[serde(alias = "compact")]
+    Compact,
+    #[default]
+    #[serde(alias = "default")]
+    /// The default UI density.
+    Default,
+    #[serde(alias = "comfortable")]
+    /// A looser UI with more spacing and larger elements.
+    Comfortable,
+}
+
+impl UiDensity {
+    /// The spacing ratio of a given density.
+    /// TODO: Standardize usage throughout the app or remove
+    pub fn spacing_ratio(self) -> f32 {
+        match self {
+            UiDensity::Compact => 0.75,
+            UiDensity::Default => 1.0,
+            UiDensity::Comfortable => 1.25,
+        }
+    }
+}
+
+impl From<String> for UiDensity {
+    fn from(s: String) -> Self {
+        match s.as_str() {
+            "compact" => Self::Compact,
+            "default" => Self::Default,
+            "comfortable" => Self::Comfortable,
+            _ => Self::default(),
+        }
+    }
+}
+
+impl From<UiDensity> for String {
+    fn from(val: UiDensity) -> Self {
+        match val {
+            UiDensity::Compact => "compact".to_string(),
+            UiDensity::Default => "default".to_string(),
+            UiDensity::Comfortable => "comfortable".to_string(),
+        }
+    }
+}

crates/theme_settings/src/settings.rs 🔗

@@ -12,83 +12,17 @@ use serde::{Deserialize, Serialize};
 pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName};
 use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent};
 use std::sync::Arc;
-use theme::{Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme};
+use theme::{Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme, UiDensity};
 
 const MIN_FONT_SIZE: Pixels = px(6.0);
 const MAX_FONT_SIZE: Pixels = px(100.0);
 const MIN_LINE_HEIGHT: f32 = 1.0;
 
-#[derive(
-    Debug,
-    Default,
-    PartialEq,
-    Eq,
-    PartialOrd,
-    Ord,
-    Hash,
-    Clone,
-    Copy,
-    Serialize,
-    Deserialize,
-    JsonSchema,
-)]
-
-/// Specifies the density of the UI.
-/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
-#[serde(rename_all = "snake_case")]
-pub enum UiDensity {
-    /// A denser UI with tighter spacing and smaller elements.
-    #[serde(alias = "compact")]
-    Compact,
-    #[default]
-    #[serde(alias = "default")]
-    /// The default UI density.
-    Default,
-    #[serde(alias = "comfortable")]
-    /// A looser UI with more spacing and larger elements.
-    Comfortable,
-}
-
-impl UiDensity {
-    /// The spacing ratio of a given density.
-    /// TODO: Standardize usage throughout the app or remove
-    pub fn spacing_ratio(self) -> f32 {
-        match self {
-            UiDensity::Compact => 0.75,
-            UiDensity::Default => 1.0,
-            UiDensity::Comfortable => 1.25,
-        }
-    }
-}
-
-impl From<String> for UiDensity {
-    fn from(s: String) -> Self {
-        match s.as_str() {
-            "compact" => Self::Compact,
-            "default" => Self::Default,
-            "comfortable" => Self::Comfortable,
-            _ => Self::default(),
-        }
-    }
-}
-
-impl From<UiDensity> for String {
-    fn from(val: UiDensity) -> Self {
-        match val {
-            UiDensity::Compact => "compact".to_string(),
-            UiDensity::Default => "default".to_string(),
-            UiDensity::Comfortable => "comfortable".to_string(),
-        }
-    }
-}
-
-impl From<settings::UiDensity> for UiDensity {
-    fn from(val: settings::UiDensity) -> Self {
-        match val {
-            settings::UiDensity::Compact => Self::Compact,
-            settings::UiDensity::Default => Self::Default,
-            settings::UiDensity::Comfortable => Self::Comfortable,
-        }
+pub(crate) fn ui_density_from_settings(val: settings::UiDensity) -> UiDensity {
+    match val {
+        settings::UiDensity::Compact => UiDensity::Compact,
+        settings::UiDensity::Default => UiDensity::Default,
+        settings::UiDensity::Comfortable => UiDensity::Comfortable,
     }
 }
 
@@ -693,7 +627,7 @@ impl settings::Settings for ThemeSettings {
             experimental_theme_overrides: content.experimental_theme_overrides.clone(),
             theme_overrides: content.theme_overrides.clone(),
             icon_theme: icon_theme_selection,
-            ui_density: content.ui_density.unwrap_or_default().into(),
+            ui_density: ui_density_from_settings(content.ui_density.unwrap_or_default()),
             unnecessary_code_fade: content.unnecessary_code_fade.unwrap().0.clamp(0.0, 0.9),
         }
     }

crates/theme_settings/src/theme_settings.rs 🔗

@@ -12,13 +12,13 @@ use std::sync::Arc;
 
 use ::settings::{IntoGpui, Settings, SettingsStore};
 use anyhow::{Context as _, Result};
-use gpui::{App, HighlightStyle, Refineable};
+use gpui::{App, Font, HighlightStyle, Pixels, Refineable};
 use gpui_util::ResultExt;
 use theme::{
     AccentColors, Appearance, AppearanceContent, DEFAULT_DARK_THEME, DEFAULT_ICON_THEME_NAME,
     GlobalTheme, LoadThemes, PlayerColor, PlayerColors, StatusColors, SyntaxTheme,
-    SystemAppearance, SystemColors, Theme, ThemeColors, ThemeFamily, ThemeRegistry, ThemeStyles,
-    default_color_scales, try_parse_color,
+    SystemAppearance, SystemColors, Theme, ThemeColors, ThemeFamily, ThemeRegistry,
+    ThemeSettingsProvider, ThemeStyles, default_color_scales, try_parse_color,
 };
 
 pub use crate::schema::{
@@ -28,12 +28,37 @@ pub use crate::schema::{
 };
 pub use crate::settings::{
     AgentFontSize, BufferLineHeight, FontFamilyName, IconThemeName, IconThemeSelection,
-    ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings, UiDensity,
-    adjust_agent_buffer_font_size, adjust_agent_ui_font_size, adjust_buffer_font_size,
-    adjust_ui_font_size, adjusted_font_size, appearance_to_mode, clamp_font_size, default_theme,
-    observe_buffer_font_size_adjustment, reset_agent_buffer_font_size, reset_agent_ui_font_size,
-    reset_buffer_font_size, reset_ui_font_size, set_icon_theme, set_mode, set_theme, setup_ui_font,
+    ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings, adjust_agent_buffer_font_size,
+    adjust_agent_ui_font_size, adjust_buffer_font_size, adjust_ui_font_size, adjusted_font_size,
+    appearance_to_mode, clamp_font_size, default_theme, observe_buffer_font_size_adjustment,
+    reset_agent_buffer_font_size, reset_agent_ui_font_size, reset_buffer_font_size,
+    reset_ui_font_size, set_icon_theme, set_mode, set_theme, setup_ui_font,
 };
+pub use theme::UiDensity;
+
+struct ThemeSettingsProviderImpl;
+
+impl ThemeSettingsProvider for ThemeSettingsProviderImpl {
+    fn ui_font<'a>(&'a self, cx: &'a App) -> &'a Font {
+        &ThemeSettings::get_global(cx).ui_font
+    }
+
+    fn buffer_font<'a>(&'a self, cx: &'a App) -> &'a Font {
+        &ThemeSettings::get_global(cx).buffer_font
+    }
+
+    fn ui_font_size(&self, cx: &App) -> Pixels {
+        ThemeSettings::get_global(cx).ui_font_size(cx)
+    }
+
+    fn buffer_font_size(&self, cx: &App) -> Pixels {
+        ThemeSettings::get_global(cx).buffer_font_size(cx)
+    }
+
+    fn ui_density(&self, cx: &App) -> UiDensity {
+        ThemeSettings::get_global(cx).ui_density
+    }
+}
 
 /// Initialize the theme system with settings integration.
 ///
@@ -43,6 +68,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut App) {
     let load_user_themes = matches!(&themes_to_load, LoadThemes::All(_));
 
     theme::init(themes_to_load, cx);
+    theme::set_theme_settings_provider(Box::new(ThemeSettingsProviderImpl), cx);
 
     if load_user_themes {
         let registry = ThemeRegistry::global(cx);

crates/ui/Cargo.toml 🔗

@@ -28,7 +28,6 @@ smallvec.workspace = true
 story = { workspace = true, optional = true }
 strum.workspace = true
 theme.workspace = true
-theme_settings.workspace = true
 ui_macros.workspace = true
 gpui_util.workspace = true
 

crates/ui/src/components/context_menu.rs 🔗

@@ -8,14 +8,12 @@ use gpui::{
     Subscription, anchored, canvas, prelude::*, px,
 };
 use menu::{SelectChild, SelectFirst, SelectLast, SelectNext, SelectParent, SelectPrevious};
-use settings::Settings;
 use std::{
     cell::{Cell, RefCell},
     collections::HashMap,
     rc::Rc,
     time::{Duration, Instant},
 };
-use theme_settings::ThemeSettings;
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum SubmenuOpenTrigger {
@@ -2050,7 +2048,7 @@ impl ContextMenuItem {
 
 impl Render for ContextMenu {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
+        let ui_font_size = theme::theme_settings(cx).ui_font_size(cx);
         let window_size = window.viewport_size();
         let rem_size = window.rem_size();
         let is_wide_window = window_size.width / rem_size > rems_from_px(800.).0;

crates/ui/src/components/label/label_like.rs 🔗

@@ -1,8 +1,6 @@
 use crate::prelude::*;
 use gpui::{FontWeight, Rems, StyleRefinement, UnderlineStyle};
-use settings::Settings;
 use smallvec::SmallVec;
-use theme_settings::ThemeSettings;
 
 /// Sets the size of a label
 #[derive(Debug, PartialEq, Clone, Copy, Default)]
@@ -191,9 +189,7 @@ impl LabelCommon for LabelLike {
     }
 
     fn buffer_font(mut self, cx: &App) -> Self {
-        let font = theme_settings::ThemeSettings::get_global(cx)
-            .buffer_font
-            .clone();
+        let font = theme::theme_settings(cx).buffer_font(cx).clone();
         self.weight = Some(font.weight);
         self.base = self.base.font(font);
         self
@@ -202,11 +198,7 @@ impl LabelCommon for LabelLike {
     fn inline_code(mut self, cx: &App) -> Self {
         self.base = self
             .base
-            .font(
-                theme_settings::ThemeSettings::get_global(cx)
-                    .buffer_font
-                    .clone(),
-            )
+            .font(theme::theme_settings(cx).buffer_font(cx).clone())
             .bg(cx.theme().colors().element_background)
             .rounded_sm()
             .px_0p5();
@@ -264,7 +256,7 @@ impl RenderOnce for LabelLike {
             .text_color(color)
             .font_weight(
                 self.weight
-                    .unwrap_or(ThemeSettings::get_global(cx).ui_font.weight),
+                    .unwrap_or(theme::theme_settings(cx).ui_font(cx).weight),
             )
             .children(self.children)
     }

crates/ui/src/components/list/list_header.rs 🔗

@@ -3,8 +3,7 @@ use std::sync::Arc;
 use crate::{Disclosure, prelude::*};
 use component::{Component, ComponentScope, example_group_with_title, single_example};
 use gpui::{AnyElement, ClickEvent};
-use settings::Settings;
-use theme_settings::ThemeSettings;
+use theme::UiDensity;
 
 #[derive(IntoElement, RegisterComponent)]
 pub struct ListHeader {
@@ -81,7 +80,7 @@ impl Toggleable for ListHeader {
 
 impl RenderOnce for ListHeader {
     fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
-        let ui_density = ThemeSettings::get_global(cx).ui_density;
+        let ui_density = theme::theme_settings(cx).ui_density(cx);
 
         h_flex()
             .id(self.label.clone())
@@ -91,7 +90,7 @@ impl RenderOnce for ListHeader {
             .child(
                 div()
                     .map(|this| match ui_density {
-                        theme_settings::UiDensity::Comfortable => this.h_5(),
+                        UiDensity::Comfortable => this.h_5(),
                         _ => this.h_7(),
                     })
                     .when(self.inset, |this| this.px_2())

crates/ui/src/components/tooltip.rs 🔗

@@ -1,12 +1,9 @@
 use std::borrow::Borrow;
 use std::rc::Rc;
 
-use gpui::{Action, AnyElement, AnyView, AppContext, FocusHandle, IntoElement, Render};
-use settings::Settings;
-use theme_settings::ThemeSettings;
-
 use crate::prelude::*;
 use crate::{Color, KeyBinding, Label, LabelSize, StyledExt, h_flex, v_flex};
+use gpui::{Action, AnyElement, AnyView, AppContext, FocusHandle, IntoElement, Render};
 
 #[derive(RegisterComponent)]
 pub struct Tooltip {
@@ -221,7 +218,7 @@ where
     C: AppContext + Borrow<App>,
 {
     let app = (*cx).borrow();
-    let ui_font = ThemeSettings::get_global(app).ui_font.clone();
+    let ui_font = theme::theme_settings(app).ui_font(app).clone();
 
     // padding to avoid tooltip appearing right below the mouse cursor
     div().pl_2().pt_2p5().child(

crates/ui/src/styles/spacing.rs 🔗

@@ -1,6 +1,5 @@
 use gpui::{App, Pixels, Rems, px, rems};
-use settings::Settings;
-use theme_settings::{ThemeSettings, UiDensity};
+use theme::UiDensity;
 use ui_macros::derive_dynamic_spacing;
 
 // Derives [DynamicSpacing]. See [ui_macros::derive_dynamic_spacing].
@@ -51,5 +50,5 @@ derive_dynamic_spacing![
 ///
 /// Always use [DynamicSpacing] for spacing values.
 pub fn ui_density(cx: &mut App) -> UiDensity {
-    ThemeSettings::get_global(cx).ui_density
+    theme::theme_settings(cx).ui_density(cx)
 }

crates/ui/src/styles/typography.rs 🔗

@@ -3,9 +3,7 @@ use gpui::{
     AnyElement, App, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled, Window,
     div, rems,
 };
-use settings::Settings;
 use theme::ActiveTheme;
-use theme_settings::ThemeSettings;
 
 use crate::{Color, rems_from_px};
 
@@ -13,16 +11,16 @@ use crate::{Color, rems_from_px};
 pub trait StyledTypography: Styled + Sized {
     /// Sets the font family to the buffer font.
     fn font_buffer(self, cx: &App) -> Self {
-        let settings = ThemeSettings::get_global(cx);
-        let buffer_font_family = settings.buffer_font.family.clone();
+        let settings = theme::theme_settings(cx);
+        let buffer_font_family = settings.buffer_font(cx).family.clone();
 
         self.font_family(buffer_font_family)
     }
 
     /// Sets the font family to the UI font.
     fn font_ui(self, cx: &App) -> Self {
-        let settings = ThemeSettings::get_global(cx);
-        let ui_font_family = settings.ui_font.family.clone();
+        let settings = theme::theme_settings(cx);
+        let ui_font_family = settings.ui_font(cx).family.clone();
 
         self.font_family(ui_font_family)
     }
@@ -83,7 +81,7 @@ pub trait StyledTypography: Styled + Sized {
     /// This should only be used for text that is displayed in a buffer,
     /// or other places that text needs to match the user's buffer font size.
     fn text_buffer(self, cx: &App) -> Self {
-        let settings = ThemeSettings::get_global(cx);
+        let settings = theme::theme_settings(cx);
         self.text_size(settings.buffer_font_size(cx))
     }
 }
@@ -134,28 +132,28 @@ pub enum TextSize {
 impl TextSize {
     /// Returns the text size in rems.
     pub fn rems(self, cx: &App) -> Rems {
-        let theme_settings = ThemeSettings::get_global(cx);
+        let settings = theme::theme_settings(cx);
 
         match self {
             Self::Large => rems_from_px(16.),
             Self::Default => rems_from_px(14.),
             Self::Small => rems_from_px(12.),
             Self::XSmall => rems_from_px(10.),
-            Self::Ui => rems_from_px(theme_settings.ui_font_size(cx)),
-            Self::Editor => rems_from_px(theme_settings.buffer_font_size(cx)),
+            Self::Ui => rems_from_px(settings.ui_font_size(cx)),
+            Self::Editor => rems_from_px(settings.buffer_font_size(cx)),
         }
     }
 
     pub fn pixels(self, cx: &App) -> Pixels {
-        let theme_settings = ThemeSettings::get_global(cx);
+        let settings = theme::theme_settings(cx);
 
         match self {
             Self::Large => px(16.),
             Self::Default => px(14.),
             Self::Small => px(12.),
             Self::XSmall => px(10.),
-            Self::Ui => theme_settings.ui_font_size(cx),
-            Self::Editor => theme_settings.buffer_font_size(cx),
+            Self::Ui => settings.ui_font_size(cx),
+            Self::Editor => settings.buffer_font_size(cx),
         }
     }
 }
@@ -213,7 +211,7 @@ pub struct Headline {
 
 impl RenderOnce for Headline {
     fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
-        let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
+        let ui_font = theme::theme_settings(cx).ui_font(cx).clone();
 
         div()
             .font(ui_font)

crates/ui_macros/src/dynamic_spacing.rs 🔗

@@ -65,10 +65,10 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
                 DynamicSpacingValue::Single(n) => {
                     let n = n.base10_parse::<f32>().unwrap();
                     quote! {
-                        DynamicSpacing::#variant => match ThemeSettings::get_global(cx).ui_density {
-                            ::theme_settings::UiDensity::Compact => (#n - 4.0).max(0.0) / BASE_REM_SIZE_IN_PX,
-                            ::theme_settings::UiDensity::Default => #n / BASE_REM_SIZE_IN_PX,
-                            ::theme_settings::UiDensity::Comfortable => (#n + 4.0) / BASE_REM_SIZE_IN_PX,
+                        DynamicSpacing::#variant => match ::theme::theme_settings(cx).ui_density(cx) {
+                            ::theme::UiDensity::Compact => (#n - 4.0).max(0.0) / BASE_REM_SIZE_IN_PX,
+                            ::theme::UiDensity::Default => #n / BASE_REM_SIZE_IN_PX,
+                            ::theme::UiDensity::Comfortable => (#n + 4.0) / BASE_REM_SIZE_IN_PX,
                         }
                     }
                 }
@@ -77,10 +77,10 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
                     let b = b.base10_parse::<f32>().unwrap();
                     let c = c.base10_parse::<f32>().unwrap();
                     quote! {
-                        DynamicSpacing::#variant => match ThemeSettings::get_global(cx).ui_density {
-                            ::theme_settings::UiDensity::Compact => #a / BASE_REM_SIZE_IN_PX,
-                            ::theme_settings::UiDensity::Default => #b / BASE_REM_SIZE_IN_PX,
-                            ::theme_settings::UiDensity::Comfortable => #c / BASE_REM_SIZE_IN_PX,
+                        DynamicSpacing::#variant => match ::theme::theme_settings(cx).ui_density(cx) {
+                            ::theme::UiDensity::Compact => #a / BASE_REM_SIZE_IN_PX,
+                            ::theme::UiDensity::Default => #b / BASE_REM_SIZE_IN_PX,
+                            ::theme::UiDensity::Comfortable => #c / BASE_REM_SIZE_IN_PX,
                         }
                     }
                 }
@@ -157,7 +157,7 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
 
             /// Returns the spacing value in pixels.
             pub fn px(&self, cx: &App) -> Pixels {
-                let ui_font_size_f32: f32 = ThemeSettings::get_global(cx).ui_font_size(cx).into();
+                let ui_font_size_f32: f32 = ::theme::theme_settings(cx).ui_font_size(cx).into();
                 px(ui_font_size_f32 * self.spacing_ratio(cx))
             }
         }