Browse Source

[REF-1743] Implement radix-native color mode switch and button (#2526)

Masen Furer 1 year ago
parent
commit
de6244483d

+ 1 - 1
reflex/.templates/apps/blank/code/blank.py

@@ -15,7 +15,7 @@ class State(rx.State):
 
 def index() -> rx.Component:
     return rx.fragment(
-        rx.color_mode_button(rx.color_mode_icon(), float="right"),
+        rx.color_mode.button(rx.color_mode.icon(), float="right"),
         rx.vstack(
             rx.heading("Welcome to Reflex!", font_size="2em"),
             rx.box("Get started by editing ", rx.code(filename, font_size="1em")),

+ 3 - 3
reflex/__init__.py

@@ -20,7 +20,7 @@ _ALL_COMPONENTS = [
     "foreach",
     "html",
     "match",
-    # "color_mode_cond",
+    "color_mode_cond",
     "connection_banner",
     "connection_modal",
     "debounce_input",
@@ -121,7 +121,7 @@ _MAPPING = {
     "reflex.components.el": ["el"],
     "reflex.components.lucide": ["lucide"],
     "reflex.components.next": ["next"],
-    "reflex.components.radix": ["radix"],
+    "reflex.components.radix": ["radix", "color_mode"],
     "reflex.components.recharts": ["recharts"],
     "reflex.components.moment.moment": ["MomentDelta"],
     "reflex.config": ["config", "Config", "DBConfig"],
@@ -150,7 +150,7 @@ _MAPPING = {
     "reflex.page": ["page"],
     "reflex.route": ["route"],
     "reflex.state": ["state", "var", "Cookie", "LocalStorage", "State"],
-    "reflex.style": ["style", "color_mode", "toggle_color_mode"],
+    "reflex.style": ["style", "toggle_color_mode"],
     "reflex.testing": ["testing"],
     "reflex.utils": ["utils"],
     "reflex.vars": ["vars", "cached_var", "Var"],

+ 2 - 1
reflex/__init__.pyi

@@ -12,6 +12,7 @@ from reflex.components import cond as cond
 from reflex.components import foreach as foreach
 from reflex.components import html as html
 from reflex.components import match as match
+from reflex.components import color_mode_cond as color_mode_cond
 from reflex.components import connection_banner as connection_banner
 from reflex.components import connection_modal as connection_modal
 from reflex.components import debounce_input as debounce_input
@@ -97,6 +98,7 @@ from reflex.components import el as el
 from reflex.components import lucide as lucide
 from reflex.components import next as next
 from reflex.components import radix as radix
+from reflex.components.radix import color_mode as color_mode
 from reflex.components import recharts as recharts
 from reflex.components.moment.moment import MomentDelta as MomentDelta
 from reflex import config as config
@@ -134,7 +136,6 @@ from reflex.state import Cookie as Cookie
 from reflex.state import LocalStorage as LocalStorage
 from reflex.state import State as State
 from reflex import style as style
-from reflex.style import color_mode as color_mode
 from reflex.style import toggle_color_mode as toggle_color_mode
 from reflex import testing as testing
 from reflex import utils as utils

+ 1 - 6
reflex/components/chakra/forms/__init__.py

@@ -7,7 +7,6 @@ from .colormodeswitch import (
     ColorModeIcon,
     ColorModeScript,
     ColorModeSwitch,
-    color_mode_cond,
 )
 from .date_picker import DatePicker
 from .date_time_picker import DateTimePicker
@@ -47,8 +46,4 @@ from .switch import Switch
 from .textarea import TextArea
 from .time_picker import TimePicker
 
-helpers = [
-    "color_mode_cond",
-]
-
-__all__ = [f for f in dir() if f[0].isupper()] + helpers  # type: ignore
+__all__ = [f for f in dir() if f[0].isupper()]  # type: ignore

+ 5 - 26
reflex/components/chakra/forms/colormodeswitch.py

@@ -16,40 +16,19 @@ rx.text(
 """
 from __future__ import annotations
 
-from typing import Any
-
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra.media.icon import Icon
-from reflex.components.component import BaseComponent, Component
-from reflex.components.core.cond import Cond, cond
-from reflex.style import color_mode, toggle_color_mode
-from reflex.vars import Var
+from reflex.components.component import BaseComponent
+from reflex.components.core.cond import Cond, color_mode_cond
+from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode
 
 from .button import Button
 from .switch import Switch
 
-DEFAULT_COLOR_MODE: str = "light"
 DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun")
 DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon")
 
 
-def color_mode_cond(light: Any, dark: Any = None) -> Var | Component:
-    """Create a component or Prop based on color_mode.
-
-    Args:
-        light: The component or prop to render if color_mode is default
-        dark: The component or prop to render if color_mode is non-default
-
-    Returns:
-        The conditional component or prop.
-    """
-    return cond(
-        color_mode == DEFAULT_COLOR_MODE,
-        light,
-        dark,
-    )
-
-
 class ColorModeIcon(Cond):
     """Displays the current color mode as an icon."""
 
@@ -90,7 +69,7 @@ class ColorModeSwitch(Switch):
         """
         return Switch.create(
             *children,
-            is_checked=color_mode != DEFAULT_COLOR_MODE,
+            is_checked=color_mode != LIGHT_COLOR_MODE,
             on_change=toggle_color_mode,
             **props,
         )
@@ -121,4 +100,4 @@ class ColorModeScript(ChakraComponent):
     """Chakra color mode script."""
 
     tag = "ColorModeScript"
-    initialColorMode = "light"
+    initialColorMode = LIGHT_COLOR_MODE

+ 3 - 8
reflex/components/chakra/forms/colormodeswitch.pyi

@@ -7,22 +7,17 @@ from typing import Any, Dict, Literal, Optional, Union, overload
 from reflex.vars import Var, BaseVar, ComputedVar
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.style import Style
-from typing import Any
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra.media.icon import Icon
-from reflex.components.component import BaseComponent, Component
-from reflex.components.core.cond import Cond, cond
-from reflex.style import color_mode, toggle_color_mode
-from reflex.vars import Var
+from reflex.components.component import BaseComponent
+from reflex.components.core.cond import Cond, color_mode_cond
+from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode
 from .button import Button
 from .switch import Switch
 
-DEFAULT_COLOR_MODE: str
 DEFAULT_LIGHT_ICON: Icon
 DEFAULT_DARK_ICON: Icon
 
-def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: ...
-
 class ColorModeIcon(Cond):
     @overload
     @classmethod

+ 1 - 1
reflex/components/core/__init__.py

@@ -3,7 +3,7 @@
 from . import layout as layout
 from .banner import ConnectionBanner, ConnectionModal
 from .colors import color
-from .cond import Cond, cond
+from .cond import Cond, color_mode_cond, cond
 from .debounce import DebounceInput
 from .foreach import Foreach
 from .html import Html

+ 18 - 0
reflex/components/core/cond.py

@@ -7,6 +7,7 @@ from reflex.components.base.fragment import Fragment
 from reflex.components.component import BaseComponent, Component, MemoizationLeaf
 from reflex.components.tags import CondTag, Tag
 from reflex.constants import Dirs
+from reflex.style import LIGHT_COLOR_MODE, color_mode
 from reflex.utils import format, imports
 from reflex.vars import BaseVar, Var, VarData
 
@@ -180,3 +181,20 @@ def cond(condition: Any, c1: Any, c2: Any = None):
         _var_full_name_needs_state_prefix=False,
         merge_var_data=VarData.merge(*var_datas),
     )
+
+
+def color_mode_cond(light: Any, dark: Any = None) -> Var | Component:
+    """Create a component or Prop based on color_mode.
+
+    Args:
+        light: The component or prop to render if color_mode is default
+        dark: The component or prop to render if color_mode is non-default
+
+    Returns:
+        The conditional component or prop.
+    """
+    return cond(
+        color_mode == LIGHT_COLOR_MODE,
+        light,
+        dark,
+    )

+ 2 - 1
reflex/components/datadisplay/code.py

@@ -2,10 +2,11 @@
 import re
 from typing import Dict, Literal, Optional, Union
 
-from reflex.components.chakra.forms import Button, color_mode_cond
+from reflex.components.chakra.forms import Button
 from reflex.components.chakra.layout import Box
 from reflex.components.chakra.media import Icon
 from reflex.components.component import Component
+from reflex.components.core.cond import color_mode_cond
 from reflex.event import set_clipboard
 from reflex.style import Style
 from reflex.utils import format, imports

+ 2 - 1
reflex/components/datadisplay/code.pyi

@@ -9,10 +9,11 @@ from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.style import Style
 import re
 from typing import Dict, Literal, Optional, Union
-from reflex.components.chakra.forms import Button, color_mode_cond
+from reflex.components.chakra.forms import Button
 from reflex.components.chakra.layout import Box
 from reflex.components.chakra.media import Icon
 from reflex.components.component import Component
+from reflex.components.core.cond import color_mode_cond
 from reflex.event import set_clipboard
 from reflex.style import Style
 from reflex.utils import format, imports

+ 1 - 0
reflex/components/radix/themes/__init__.py

@@ -1,6 +1,7 @@
 """Namespace for components provided by the @radix-ui/themes library."""
 from .base import theme as theme
 from .base import theme_panel as theme_panel
+from .color_mode import color_mode_var_and_namespace as color_mode
 from .components import *
 from .layout import *
 from .typography import *

+ 18 - 0
reflex/components/radix/themes/base.py

@@ -169,6 +169,24 @@ class Theme(RadixThemesComponent):
     # Scale of all theme items: "90%" | "95%" | "100%" | "105%" | "110%". Defaults to "100%"
     scaling: Var[LiteralScaling]
 
+    @classmethod
+    def create(
+        cls, *children, color_mode: LiteralAppearance | None = None, **props
+    ) -> Component:
+        """Create a new Radix Theme specification.
+
+        Args:
+            *children: Child components.
+            color_mode: map to appearance prop.
+            **props: Component properties.
+
+        Returns:
+            A new component instance.
+        """
+        if color_mode is not None:
+            props["appearance"] = props.pop("color_mode")
+        return super().create(*children, **props)
+
     def _get_imports(self) -> imports.ImportDict:
         return imports.merge_imports(
             super()._get_imports(),

+ 3 - 69
reflex/components/radix/themes/base.pyi

@@ -335,69 +335,7 @@ class Theme(RadixThemesComponent):
     def create(  # type: ignore
         cls,
         *children,
-        color: Optional[Union[Var[str], str]] = None,
-        color_scheme: Optional[
-            Union[
-                Var[
-                    Literal[
-                        "tomato",
-                        "red",
-                        "ruby",
-                        "crimson",
-                        "pink",
-                        "plum",
-                        "purple",
-                        "violet",
-                        "iris",
-                        "indigo",
-                        "blue",
-                        "cyan",
-                        "teal",
-                        "jade",
-                        "green",
-                        "grass",
-                        "brown",
-                        "orange",
-                        "sky",
-                        "mint",
-                        "lime",
-                        "yellow",
-                        "amber",
-                        "gold",
-                        "bronze",
-                        "gray",
-                    ]
-                ],
-                Literal[
-                    "tomato",
-                    "red",
-                    "ruby",
-                    "crimson",
-                    "pink",
-                    "plum",
-                    "purple",
-                    "violet",
-                    "iris",
-                    "indigo",
-                    "blue",
-                    "cyan",
-                    "teal",
-                    "jade",
-                    "green",
-                    "grass",
-                    "brown",
-                    "orange",
-                    "sky",
-                    "mint",
-                    "lime",
-                    "yellow",
-                    "amber",
-                    "gold",
-                    "bronze",
-                    "gray",
-                ],
-            ]
-        ] = None,
+        color_mode: Optional[LiteralAppearance | None] = None,
         has_background: Optional[Union[Var[bool], bool]] = None,
         appearance: Optional[
             Union[
@@ -542,15 +480,11 @@ class Theme(RadixThemesComponent):
         ] = None,
         **props
     ) -> "Theme":
-        """Create a new component instance.
-
-        Will prepend "RadixThemes" to the component tag to avoid conflicts with
-        other UI libraries for common names, like Text and Button.
+        """Create a new Radix Theme specification.
 
         Args:
             *children: Child components.
-            color: map to CSS default color property.
-            color_scheme: map to radix color property.
+            color_mode: map to appearance prop.
             has_background: Whether to apply the themes background color to the theme node. Defaults to True.
             appearance: Override light or dark mode theme: "inherit" | "light" | "dark". Defaults to "inherit".
             accent_color: The color used for default buttons, typography, backgrounds, etc

+ 109 - 0
reflex/components/radix/themes/color_mode.py

@@ -0,0 +1,109 @@
+"""A switch component for toggling color_mode.
+
+To style components based on color mode, use style props with `color_mode_cond`:
+
+```
+rx.text(
+    "Hover over me",
+    _hover={
+        "background": rx.color_mode_cond(
+            light="var(--accent-2)",
+            dark="var(--accent-4)",
+        ),
+    },
+)
+```
+"""
+from __future__ import annotations
+
+import dataclasses
+
+from reflex.components.component import BaseComponent
+from reflex.components.core.cond import Cond, color_mode_cond
+from reflex.components.lucide.icon import Icon
+from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode
+from reflex.vars import BaseVar
+
+from .components.button import Button
+from .components.switch import Switch
+
+DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun")
+DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon")
+
+
+class ColorModeIcon(Cond):
+    """Displays the current color mode as an icon."""
+
+    @classmethod
+    def create(
+        cls,
+        light_component: BaseComponent | None = None,
+        dark_component: BaseComponent | None = None,
+    ):
+        """Create an icon component based on color_mode.
+
+        Args:
+            light_component: the component to display when color mode is default
+            dark_component: the component to display when color mode is dark (non-default)
+
+        Returns:
+            The conditionally rendered component
+        """
+        return color_mode_cond(
+            light=light_component or DEFAULT_LIGHT_ICON,
+            dark=dark_component or DEFAULT_DARK_ICON,
+        )
+
+
+class ColorModeSwitch(Switch):
+    """Switch for toggling light / dark mode via toggle_color_mode."""
+
+    @classmethod
+    def create(cls, *children, **props):
+        """Create a switch component bound to color_mode.
+
+        Args:
+            *children: The children of the component.
+            **props: The props to pass to the component.
+
+        Returns:
+            The switch component.
+        """
+        return Switch.create(
+            *children,
+            is_checked=color_mode != LIGHT_COLOR_MODE,
+            on_change=toggle_color_mode,
+            **props,
+        )
+
+
+class ColorModeButton(Button):
+    """Button for toggling chakra light / dark mode via toggle_color_mode."""
+
+    @classmethod
+    def create(cls, *children, **props):
+        """Create a button component that calls toggle_color_mode on click.
+
+        Args:
+            *children: The children of the component.
+            **props: The props to pass to the component.
+
+        Returns:
+            The button component.
+        """
+        return Button.create(
+            *children,
+            on_click=toggle_color_mode,
+            **props,
+        )
+
+
+class ColorModeNamespace(BaseVar):
+    """Namespace for color mode components."""
+
+    icon = staticmethod(ColorModeIcon.create)
+    switch = staticmethod(ColorModeSwitch.create)
+    button = staticmethod(ColorModeButton.create)
+
+
+color_mode_var_and_namespace = ColorModeNamespace(**dataclasses.asdict(color_mode))

+ 540 - 0
reflex/components/radix/themes/color_mode.pyi

@@ -0,0 +1,540 @@
+"""Stub file for reflex/components/radix/themes/color_mode.py"""
+# ------------------- DO NOT EDIT ----------------------
+# This file was generated by `scripts/pyi_generator.py`!
+# ------------------------------------------------------
+
+from typing import Any, Dict, Literal, Optional, Union, overload
+from reflex.vars import Var, BaseVar, ComputedVar
+from reflex.event import EventChain, EventHandler, EventSpec
+from reflex.style import Style
+import dataclasses
+from reflex.components.component import BaseComponent
+from reflex.components.core.cond import Cond, color_mode_cond
+from reflex.components.lucide.icon import Icon
+from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode
+from reflex.vars import BaseVar
+from .components.button import Button
+from .components.switch import Switch
+
+DEFAULT_LIGHT_ICON: Icon
+DEFAULT_DARK_ICON: Icon
+
+class ColorModeIcon(Cond):
+    @overload
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        *children,
+        cond: Optional[Union[Var[Any], Any]] = None,
+        comp1: Optional[BaseComponent] = None,
+        comp2: Optional[BaseComponent] = None,
+        style: Optional[Style] = None,
+        key: Optional[Any] = None,
+        id: Optional[Any] = None,
+        class_name: Optional[Any] = None,
+        autofocus: Optional[bool] = None,
+        _rename_props: Optional[Dict[str, str]] = None,
+        custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
+        on_blur: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_click: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_context_menu: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_double_click: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_focus: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mount: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_down: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_enter: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_leave: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_move: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_out: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_over: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_up: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_scroll: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_unmount: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        **props
+    ) -> "ColorModeIcon":
+        """Create an icon component based on color_mode.
+
+        Args:
+            light_component: the component to display when color mode is default
+            dark_component: the component to display when color mode is dark (non-default)
+
+        Returns:
+            The conditionally rendered component
+        """
+        ...
+
+class ColorModeSwitch(Switch):
+    @overload
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        *children,
+        as_child: Optional[Union[Var[bool], bool]] = None,
+        default_checked: Optional[Union[Var[bool], bool]] = None,
+        checked: Optional[Union[Var[bool], bool]] = None,
+        disabled: Optional[Union[Var[bool], bool]] = None,
+        required: Optional[Union[Var[bool], bool]] = None,
+        name: Optional[Union[Var[str], str]] = None,
+        value: Optional[Union[Var[str], str]] = None,
+        size: Optional[
+            Union[Var[Literal["1", "2", "3", "4"]], Literal["1", "2", "3", "4"]]
+        ] = None,
+        variant: Optional[
+            Union[
+                Var[Literal["classic", "solid", "soft", "surface", "outline", "ghost"]],
+                Literal["classic", "solid", "soft", "surface", "outline", "ghost"],
+            ]
+        ] = None,
+        color_scheme: Optional[
+            Union[
+                Var[
+                    Literal[
+                        "tomato",
+                        "red",
+                        "ruby",
+                        "crimson",
+                        "pink",
+                        "plum",
+                        "purple",
+                        "violet",
+                        "iris",
+                        "indigo",
+                        "blue",
+                        "cyan",
+                        "teal",
+                        "jade",
+                        "green",
+                        "grass",
+                        "brown",
+                        "orange",
+                        "sky",
+                        "mint",
+                        "lime",
+                        "yellow",
+                        "amber",
+                        "gold",
+                        "bronze",
+                        "gray",
+                    ]
+                ],
+                Literal[
+                    "tomato",
+                    "red",
+                    "ruby",
+                    "crimson",
+                    "pink",
+                    "plum",
+                    "purple",
+                    "violet",
+                    "iris",
+                    "indigo",
+                    "blue",
+                    "cyan",
+                    "teal",
+                    "jade",
+                    "green",
+                    "grass",
+                    "brown",
+                    "orange",
+                    "sky",
+                    "mint",
+                    "lime",
+                    "yellow",
+                    "amber",
+                    "gold",
+                    "bronze",
+                    "gray",
+                ],
+            ]
+        ] = None,
+        high_contrast: Optional[Union[Var[bool], bool]] = None,
+        radius: Optional[
+            Union[
+                Var[Literal["none", "small", "medium", "large", "full"]],
+                Literal["none", "small", "medium", "large", "full"],
+            ]
+        ] = None,
+        style: Optional[Style] = None,
+        key: Optional[Any] = None,
+        id: Optional[Any] = None,
+        class_name: Optional[Any] = None,
+        autofocus: Optional[bool] = None,
+        _rename_props: Optional[Dict[str, str]] = None,
+        custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
+        on_blur: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_change: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_click: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_context_menu: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_double_click: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_focus: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mount: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_down: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_enter: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_leave: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_move: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_out: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_over: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_up: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_scroll: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_unmount: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        **props
+    ) -> "ColorModeSwitch":
+        """Create a switch component bound to color_mode.
+
+        Args:
+            *children: The children of the component.
+            as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
+            default_checked: Whether the switch is checked by default
+            checked: Whether the switch is checked
+            disabled: If true, prevent the user from interacting with the switch
+            required: If true, the user must interact with the switch to submit the form
+            name: The name of the switch (when submitting a form)
+            value: The value associated with the "on" position
+            size: Switch size "1" - "4"
+            variant: Variant of switch: "solid" | "soft" | "outline" | "ghost"
+            color_scheme: Override theme color for switch
+            high_contrast: Whether to render the switch with higher contrast color against background
+            radius: Override theme radius for switch: "none" | "small" | "medium" | "large" | "full"
+            style: Props to rename  The style of the component.
+            key: A unique key for the component.
+            id: The id for the component.
+            class_name: The class name for the component.
+            autofocus: Whether the component should take the focus once the page is loaded
+            _rename_props: props to change the name of
+            custom_attrs: custom attribute
+            **props: The props to pass to the component.
+
+        Returns:
+            The switch component.
+        """
+        ...
+
+class ColorModeButton(Button):
+    @overload
+    @classmethod
+    def create(  # type: ignore
+        cls,
+        *children,
+        as_child: Optional[Union[Var[bool], bool]] = None,
+        size: Optional[
+            Union[Var[Literal["1", "2", "3", "4"]], Literal["1", "2", "3", "4"]]
+        ] = None,
+        variant: Optional[
+            Union[
+                Var[Literal["classic", "solid", "soft", "surface", "outline", "ghost"]],
+                Literal["classic", "solid", "soft", "surface", "outline", "ghost"],
+            ]
+        ] = None,
+        color_scheme: Optional[
+            Union[
+                Var[
+                    Literal[
+                        "tomato",
+                        "red",
+                        "ruby",
+                        "crimson",
+                        "pink",
+                        "plum",
+                        "purple",
+                        "violet",
+                        "iris",
+                        "indigo",
+                        "blue",
+                        "cyan",
+                        "teal",
+                        "jade",
+                        "green",
+                        "grass",
+                        "brown",
+                        "orange",
+                        "sky",
+                        "mint",
+                        "lime",
+                        "yellow",
+                        "amber",
+                        "gold",
+                        "bronze",
+                        "gray",
+                    ]
+                ],
+                Literal[
+                    "tomato",
+                    "red",
+                    "ruby",
+                    "crimson",
+                    "pink",
+                    "plum",
+                    "purple",
+                    "violet",
+                    "iris",
+                    "indigo",
+                    "blue",
+                    "cyan",
+                    "teal",
+                    "jade",
+                    "green",
+                    "grass",
+                    "brown",
+                    "orange",
+                    "sky",
+                    "mint",
+                    "lime",
+                    "yellow",
+                    "amber",
+                    "gold",
+                    "bronze",
+                    "gray",
+                ],
+            ]
+        ] = None,
+        high_contrast: Optional[Union[Var[bool], bool]] = None,
+        radius: Optional[
+            Union[
+                Var[Literal["none", "small", "medium", "large", "full"]],
+                Literal["none", "small", "medium", "large", "full"],
+            ]
+        ] = None,
+        auto_focus: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        disabled: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        form: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        form_action: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        form_enc_type: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        form_method: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        form_no_validate: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        form_target: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        name: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        type: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        value: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        access_key: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        auto_capitalize: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        content_editable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        context_menu: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        dir: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        draggable: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        enter_key_hint: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        hidden: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        input_mode: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        item_prop: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        lang: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        role: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        slot: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None,
+        spell_check: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        tab_index: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        title: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        translate: Optional[
+            Union[Var[Union[str, int, bool]], Union[str, int, bool]]
+        ] = None,
+        style: Optional[Style] = None,
+        key: Optional[Any] = None,
+        id: Optional[Any] = None,
+        class_name: Optional[Any] = None,
+        autofocus: Optional[bool] = None,
+        _rename_props: Optional[Dict[str, str]] = None,
+        custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
+        on_blur: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_click: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_context_menu: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_double_click: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_focus: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mount: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_down: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_enter: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_leave: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_move: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_out: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_over: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_mouse_up: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_scroll: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        on_unmount: Optional[
+            Union[EventHandler, EventSpec, list, function, BaseVar]
+        ] = None,
+        **props
+    ) -> "ColorModeButton":
+        """Create a button component that calls toggle_color_mode on click.
+
+        Args:
+            *children: The children of the component.
+            as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
+            size: Button size "1" - "4"
+            variant: Variant of button: "solid" | "soft" | "outline" | "ghost"
+            color_scheme: Override theme color for button
+            high_contrast: Whether to render the button with higher contrast color against background
+            radius: Override theme radius for button: "none" | "small" | "medium" | "large" | "full"
+            auto_focus: Automatically focuses the button when the page loads
+            disabled: Disables the button
+            form: Associates the button with a form (by id)
+            form_action: URL to send the form data to (for type="submit" buttons)
+            form_enc_type: How the form data should be encoded when submitting to the server (for type="submit" buttons)
+            form_method: HTTP method to use for sending form data (for type="submit" buttons)
+            form_no_validate: Bypasses form validation when submitting (for type="submit" buttons)
+            form_target: Specifies where to display the response after submitting the form (for type="submit" buttons)
+            name: Name of the button, used when sending form data
+            type: Type of the button (submit, reset, or button)
+            value: Value of the button, used when sending form data
+            access_key:  Provides a hint for generating a keyboard shortcut for the current element.
+            auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+            content_editable: Indicates whether the element's content is editable.
+            context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
+            dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
+            draggable: Defines whether the element can be dragged.
+            enter_key_hint: Hints what media types the media element is able to play.
+            hidden: Defines whether the element is hidden.
+            input_mode: Defines the type of the element.
+            item_prop: Defines the name of the element for metadata purposes.
+            lang: Defines the language used in the element.
+            role: Defines the role of the element.
+            slot: Assigns a slot in a shadow DOM shadow tree to an element.
+            spell_check: Defines whether the element may be checked for spelling errors.
+            tab_index: Defines the position of the current element in the tabbing order.
+            title: Defines a tooltip for the element.
+            translate: Specifies whether the content of an element should be translated or not.
+            style: The style of the component.
+            key: A unique key for the component.
+            id: The id for the component.
+            class_name: The class name for the component.
+            autofocus: Whether the component should take the focus once the page is loaded
+            _rename_props: props to change the name of
+            custom_attrs: custom attribute
+            **props: The props to pass to the component.
+
+        Returns:
+            The button component.
+        """
+        ...
+
+class ColorModeNamespace(BaseVar):
+    icon = staticmethod(ColorModeIcon.create)
+    switch = staticmethod(ColorModeSwitch.create)
+    button = staticmethod(ColorModeButton.create)
+
+color_mode_var_and_namespace = ColorModeNamespace(**dataclasses.asdict(color_mode))

+ 3 - 0
reflex/style.py

@@ -12,6 +12,9 @@ from reflex.vars import BaseVar, Var, VarData
 
 VarData.update_forward_refs()  # Ensure all type definitions are resolved
 
+LIGHT_COLOR_MODE: str = "light"
+DARK_COLOR_MODE: str = "dark"
+
 # Reference the global ColorModeContext
 color_mode_var_data = VarData(  # type: ignore
     imports={