diff --git a/reflex/components/core/colors.py b/reflex/components/core/colors.py
index cd14266e6..fbc6825aa 100644
--- a/reflex/components/core/colors.py
+++ b/reflex/components/core/colors.py
@@ -2,11 +2,10 @@
from reflex.constants.colors import Color, ColorType, ShadeType
from reflex.utils.types import validate_parameter_literals
-from reflex.vars import Var
@validate_parameter_literals
-def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Var:
+def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Color:
"""Create a color object.
Args:
@@ -17,4 +16,4 @@ def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Var:
Returns:
The color object.
"""
- return Var.create(Color(color, shade, alpha))._replace(_var_is_string=True) # type: ignore
+ return Color(color, shade, alpha)
diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py
index b17ab5409..76e6c56c1 100644
--- a/reflex/components/core/cond.py
+++ b/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.constants.colors import Color
from reflex.utils import format, imports
from reflex.vars import BaseVar, Var, VarData
@@ -167,6 +168,17 @@ def cond(condition: Any, c1: Any, c2: Any = None):
if isinstance(c2, Var):
var_datas.append(c2._var_data)
+ def create_var(cond_part):
+ return Var.create_safe(
+ cond_part,
+ _var_is_string=type(cond_part) is str or isinstance(cond_part, Color),
+ )
+
+ # convert the truth and false cond parts into vars so the _var_data can be obtained.
+ c1 = create_var(c1)
+ c2 = create_var(c2)
+ var_datas.extend([c1._var_data, c2._var_data])
+
# Create the conditional var.
return cond_var._replace(
_var_name=format.format_cond(
diff --git a/reflex/components/core/match.py b/reflex/components/core/match.py
index 4cce14b33..2b966497b 100644
--- a/reflex/components/core/match.py
+++ b/reflex/components/core/match.py
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
from reflex.components.base import Fragment
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
+from reflex.components.core.colors import Color
from reflex.components.tags import MatchTag, Tag
from reflex.style import Style
from reflex.utils import format, imports, types
@@ -116,7 +117,8 @@ class Match(MemoizationLeaf):
"""
_var_data = case_element._var_data if isinstance(case_element, Style) else None # type: ignore
case_element = Var.create(
- case_element, _var_is_string=type(case_element) is str
+ case_element,
+ _var_is_string=type(case_element) is str or isinstance(case_element, Color),
)
if _var_data is not None:
case_element._var_data = VarData.merge(case_element._var_data, _var_data) # type: ignore
diff --git a/reflex/constants/__init__.py b/reflex/constants/__init__.py
index c05f62fe5..7663a0c76 100644
--- a/reflex/constants/__init__.py
+++ b/reflex/constants/__init__.py
@@ -6,6 +6,8 @@ from .base import (
LOCAL_STORAGE,
POLLING_MAX_HTTP_BUFFER_SIZE,
PYTEST_CURRENT_TEST,
+ REFLEX_VAR_CLOSING_TAG,
+ REFLEX_VAR_OPENING_TAG,
RELOAD_CONFIG,
SKIP_COMPILE_ENV_VAR,
ColorMode,
@@ -73,6 +75,8 @@ __ALL__ = [
Expiration,
Ext,
Fnm,
+ REFLEX_VAR_CLOSING_TAG,
+ REFLEX_VAR_OPENING_TAG,
GitIgnore,
Hooks,
Imports,
diff --git a/reflex/constants/base.py b/reflex/constants/base.py
index eeef595eb..272756ec0 100644
--- a/reflex/constants/base.py
+++ b/reflex/constants/base.py
@@ -187,3 +187,6 @@ SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
# Testing os env set by pytest when running a test case.
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
RELOAD_CONFIG = "__REFLEX_RELOAD_CONFIG"
+
+REFLEX_VAR_OPENING_TAG = ""
+REFLEX_VAR_CLOSING_TAG = ""
diff --git a/reflex/utils/types.py b/reflex/utils/types.py
index 41e910dd2..abed498ee 100644
--- a/reflex/utils/types.py
+++ b/reflex/utils/types.py
@@ -25,6 +25,7 @@ from pydantic.fields import ModelField
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
+from reflex import constants
from reflex.base import Base
from reflex.utils import serializers
@@ -332,6 +333,18 @@ def check_prop_in_allowed_types(prop: Any, allowed_types: Iterable) -> bool:
return type_ in allowed_types
+def is_encoded_fstring(value) -> bool:
+ """Check if a value is an encoded Var f-string.
+
+ Args:
+ value: The value string to check.
+
+ Returns:
+ Whether the value is an f-string
+ """
+ return isinstance(value, str) and constants.REFLEX_VAR_OPENING_TAG in value
+
+
def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
"""Check that a value is a valid literal.
@@ -349,6 +362,7 @@ def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
if (
is_literal(expected_type)
and not isinstance(value, Var) # validating vars is not supported yet.
+ and not is_encoded_fstring(value) # f-strings are not supported.
and value not in expected_type.__args__
):
allowed_values = expected_type.__args__
diff --git a/reflex/vars.py b/reflex/vars.py
index ac0d0a8d2..98118b1d1 100644
--- a/reflex/vars.py
+++ b/reflex/vars.py
@@ -202,7 +202,10 @@ def _encode_var(value: Var) -> str:
The encoded var.
"""
if value._var_data:
- return f"{value._var_data.json()}" + str(value)
+ return (
+ f"{constants.REFLEX_VAR_OPENING_TAG}{value._var_data.json()}{constants.REFLEX_VAR_CLOSING_TAG}"
+ + str(value)
+ )
return str(value)
@@ -219,7 +222,7 @@ def _decode_var(value: str) -> tuple[VarData | None, str]:
if isinstance(value, str):
# Extract the state name from a formatted var
while m := re.match(
- pattern=r"(.*)(.*)(.*)",
+ pattern=rf"(.*){constants.REFLEX_VAR_OPENING_TAG}(.*){constants.REFLEX_VAR_CLOSING_TAG}(.*)",
string=value,
flags=re.DOTALL, # Ensure . matches newline characters.
):
diff --git a/tests/components/core/test_colors.py b/tests/components/core/test_colors.py
index ef1fb8978..717d41de7 100644
--- a/tests/components/core/test_colors.py
+++ b/tests/components/core/test_colors.py
@@ -1,24 +1,46 @@
import pytest
import reflex as rx
+from reflex.vars import Var
class ColorState(rx.State):
"""Test color state."""
color: str = "mint"
+ color_part: str = "tom"
shade: int = 4
+def create_color_var(color):
+ return Var.create(color)
+
+
@pytest.mark.parametrize(
"color, expected",
[
- (rx.color("mint"), "{`var(--mint-7)`}"),
- (rx.color("mint", 3), "{`var(--mint-3)`}"),
- (rx.color("mint", 3, True), "{`var(--mint-a3)`}"),
+ (create_color_var(rx.color("mint")), "var(--mint-7)"),
+ (create_color_var(rx.color("mint", 3)), "var(--mint-3)"),
+ (create_color_var(rx.color("mint", 3, True)), "var(--mint-a3)"),
(
- rx.color(ColorState.color, ColorState.shade), # type: ignore
- "{`var(--${state__color_state.color}-${state__color_state.shade})`}",
+ create_color_var(rx.color(ColorState.color, ColorState.shade)), # type: ignore
+ "var(--${state__color_state.color}-${state__color_state.shade})",
+ ),
+ (
+ create_color_var(rx.color(f"{ColorState.color}", f"{ColorState.shade}")), # type: ignore
+ "var(--${state__color_state.color}-${state__color_state.shade})",
+ ),
+ (
+ create_color_var(rx.color(f"{ColorState.color_part}ato", f"{ColorState.shade}")), # type: ignore
+ "var(--${state__color_state.color_part}ato-${state__color_state.shade})",
+ ),
+ (
+ create_color_var(f'{rx.color(ColorState.color, f"{ColorState.shade}")}'), # type: ignore
+ "var(--${state__color_state.color}-${state__color_state.shade})",
+ ),
+ (
+ create_color_var(f'{rx.color(f"{ColorState.color}", f"{ColorState.shade}")}'), # type: ignore
+ "var(--${state__color_state.color}-${state__color_state.shade})",
),
],
)