Browse Source

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

Masen Furer 1 năm trước cách đây
mục cha
commit
de6244483d

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

@@ -15,7 +15,7 @@ class State(rx.State):
 
 
 def index() -> rx.Component:
 def index() -> rx.Component:
     return rx.fragment(
     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.vstack(
             rx.heading("Welcome to Reflex!", font_size="2em"),
             rx.heading("Welcome to Reflex!", font_size="2em"),
             rx.box("Get started by editing ", rx.code(filename, font_size="1em")),
             rx.box("Get started by editing ", rx.code(filename, font_size="1em")),

+ 3 - 3
reflex/__init__.py

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

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

@@ -7,7 +7,6 @@ from .colormodeswitch import (
     ColorModeIcon,
     ColorModeIcon,
     ColorModeScript,
     ColorModeScript,
     ColorModeSwitch,
     ColorModeSwitch,
-    color_mode_cond,
 )
 )
 from .date_picker import DatePicker
 from .date_picker import DatePicker
 from .date_time_picker import DateTimePicker
 from .date_time_picker import DateTimePicker
@@ -47,8 +46,4 @@ from .switch import Switch
 from .textarea import TextArea
 from .textarea import TextArea
 from .time_picker import TimePicker
 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 __future__ import annotations
 
 
-from typing import Any
-
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra.media.icon import Icon
 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 .button import Button
 from .switch import Switch
 from .switch import Switch
 
 
-DEFAULT_COLOR_MODE: str = "light"
 DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun")
 DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun")
 DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon")
 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):
 class ColorModeIcon(Cond):
     """Displays the current color mode as an icon."""
     """Displays the current color mode as an icon."""
 
 
@@ -90,7 +69,7 @@ class ColorModeSwitch(Switch):
         """
         """
         return Switch.create(
         return Switch.create(
             *children,
             *children,
-            is_checked=color_mode != DEFAULT_COLOR_MODE,
+            is_checked=color_mode != LIGHT_COLOR_MODE,
             on_change=toggle_color_mode,
             on_change=toggle_color_mode,
             **props,
             **props,
         )
         )
@@ -121,4 +100,4 @@ class ColorModeScript(ChakraComponent):
     """Chakra color mode script."""
     """Chakra color mode script."""
 
 
     tag = "ColorModeScript"
     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.vars import Var, BaseVar, ComputedVar
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.event import EventChain, EventHandler, EventSpec
 from reflex.style import Style
 from reflex.style import Style
-from typing import Any
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra import ChakraComponent
 from reflex.components.chakra.media.icon import Icon
 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 .button import Button
 from .switch import Switch
 from .switch import Switch
 
 
-DEFAULT_COLOR_MODE: str
 DEFAULT_LIGHT_ICON: Icon
 DEFAULT_LIGHT_ICON: Icon
 DEFAULT_DARK_ICON: Icon
 DEFAULT_DARK_ICON: Icon
 
 
-def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: ...
-
 class ColorModeIcon(Cond):
 class ColorModeIcon(Cond):
     @overload
     @overload
     @classmethod
     @classmethod

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

@@ -3,7 +3,7 @@
 from . import layout as layout
 from . import layout as layout
 from .banner import ConnectionBanner, ConnectionModal
 from .banner import ConnectionBanner, ConnectionModal
 from .colors import color
 from .colors import color
-from .cond import Cond, cond
+from .cond import Cond, color_mode_cond, cond
 from .debounce import DebounceInput
 from .debounce import DebounceInput
 from .foreach import Foreach
 from .foreach import Foreach
 from .html import Html
 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.component import BaseComponent, Component, MemoizationLeaf
 from reflex.components.tags import CondTag, Tag
 from reflex.components.tags import CondTag, Tag
 from reflex.constants import Dirs
 from reflex.constants import Dirs
+from reflex.style import LIGHT_COLOR_MODE, color_mode
 from reflex.utils import format, imports
 from reflex.utils import format, imports
 from reflex.vars import BaseVar, Var, VarData
 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,
         _var_full_name_needs_state_prefix=False,
         merge_var_data=VarData.merge(*var_datas),
         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
 import re
 from typing import Dict, Literal, Optional, Union
 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.layout import Box
 from reflex.components.chakra.media import Icon
 from reflex.components.chakra.media import Icon
 from reflex.components.component import Component
 from reflex.components.component import Component
+from reflex.components.core.cond import color_mode_cond
 from reflex.event import set_clipboard
 from reflex.event import set_clipboard
 from reflex.style import Style
 from reflex.style import Style
 from reflex.utils import format, imports
 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
 from reflex.style import Style
 import re
 import re
 from typing import Dict, Literal, Optional, Union
 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.layout import Box
 from reflex.components.chakra.media import Icon
 from reflex.components.chakra.media import Icon
 from reflex.components.component import Component
 from reflex.components.component import Component
+from reflex.components.core.cond import color_mode_cond
 from reflex.event import set_clipboard
 from reflex.event import set_clipboard
 from reflex.style import Style
 from reflex.style import Style
 from reflex.utils import format, imports
 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."""
 """Namespace for components provided by the @radix-ui/themes library."""
 from .base import theme as theme
 from .base import theme as theme
 from .base import theme_panel as theme_panel
 from .base import theme_panel as theme_panel
+from .color_mode import color_mode_var_and_namespace as color_mode
 from .components import *
 from .components import *
 from .layout import *
 from .layout import *
 from .typography 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%"
     # Scale of all theme items: "90%" | "95%" | "100%" | "105%" | "110%". Defaults to "100%"
     scaling: Var[LiteralScaling]
     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:
     def _get_imports(self) -> imports.ImportDict:
         return imports.merge_imports(
         return imports.merge_imports(
             super()._get_imports(),
             super()._get_imports(),

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

@@ -335,69 +335,7 @@ class Theme(RadixThemesComponent):
     def create(  # type: ignore
     def create(  # type: ignore
         cls,
         cls,
         *children,
         *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,
         has_background: Optional[Union[Var[bool], bool]] = None,
         appearance: Optional[
         appearance: Optional[
             Union[
             Union[
@@ -542,15 +480,11 @@ class Theme(RadixThemesComponent):
         ] = None,
         ] = None,
         **props
         **props
     ) -> "Theme":
     ) -> "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:
         Args:
             *children: Child components.
             *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.
             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".
             appearance: Override light or dark mode theme: "inherit" | "light" | "dark". Defaults to "inherit".
             accent_color: The color used for default buttons, typography, backgrounds, etc
             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
 VarData.update_forward_refs()  # Ensure all type definitions are resolved
 
 
+LIGHT_COLOR_MODE: str = "light"
+DARK_COLOR_MODE: str = "dark"
+
 # Reference the global ColorModeContext
 # Reference the global ColorModeContext
 color_mode_var_data = VarData(  # type: ignore
 color_mode_var_data = VarData(  # type: ignore
     imports={
     imports={