diff --git a/reflex/app.py b/reflex/app.py index dd8eef795..638c01a4e 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -133,6 +133,12 @@ class App(Base): # Components to add to the head of every page. head_components: List[Component] = [] + # The language to add to the html root tag of every page. + html_lang: Optional[str] = None + + # Attributes to add to the html root tag of every page. + html_custom_attrs: Optional[Dict[str, str]] = None + # A component that is present on every page. overlay_component: Optional[ Union[Component, ComponentCallable] @@ -782,7 +788,12 @@ class App(Base): submit_work(compiler.compile_root_stylesheet, self.stylesheets) # Compile the root document. - submit_work(compiler.compile_document_root, self.head_components) + submit_work( + compiler.compile_document_root, + self.head_components, + html_lang=self.html_lang, + html_custom_attrs=self.html_custom_attrs, + ) # Compile the theme. submit_work(compiler.compile_theme, style=self.style) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 6256b7ab1..d2abc086e 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -4,7 +4,7 @@ from __future__ import annotations import os from pathlib import Path -from typing import Iterable, Optional, Type +from typing import Dict, Iterable, Optional, Type, Union from reflex import constants from reflex.compiler import templates, utils @@ -19,6 +19,7 @@ from reflex.config import get_config from reflex.state import BaseState from reflex.style import LIGHT_COLOR_MODE from reflex.utils.imports import ImportVar +from reflex.vars import Var def _compile_document_root(root: Component) -> str: @@ -299,11 +300,17 @@ def _compile_tailwind( ) -def compile_document_root(head_components: list[Component]) -> tuple[str, str]: +def compile_document_root( + head_components: list[Component], + html_lang: Optional[str] = None, + html_custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, +) -> tuple[str, str]: """Compile the document root. Args: head_components: The components to include in the head. + html_lang: The language of the document, will be added to the html root element. + html_custom_attrs: custom attributes added to the html root element. Returns: The path and code of the compiled document root. @@ -312,7 +319,9 @@ def compile_document_root(head_components: list[Component]) -> tuple[str, str]: output_path = utils.get_page_path(constants.PageNames.DOCUMENT_ROOT) # Create the document root. - document_root = utils.create_document_root(head_components) + document_root = utils.create_document_root( + head_components, html_lang=html_lang, html_custom_attrs=html_custom_attrs + ) # Compile the document root. code = _compile_document_root(document_root) diff --git a/reflex/compiler/utils.py b/reflex/compiler/utils.py index c86e890d0..33202e56b 100644 --- a/reflex/compiler/utils.py +++ b/reflex/compiler/utils.py @@ -2,7 +2,7 @@ from __future__ import annotations import os -from typing import Any, Callable, Type +from typing import Any, Callable, Dict, Optional, Type, Union from urllib.parse import urlparse from pydantic.fields import ModelField @@ -24,6 +24,7 @@ from reflex.components.component import Component, ComponentStyle, CustomCompone from reflex.state import BaseState, Cookie, LocalStorage from reflex.style import Style from reflex.utils import console, format, imports, path_ops +from reflex.vars import Var # To re-export this function. merge_imports = imports.merge_imports @@ -261,11 +262,17 @@ def compile_custom_component( ) -def create_document_root(head_components: list[Component] | None = None) -> Component: +def create_document_root( + head_components: list[Component] | None = None, + html_lang: Optional[str] = None, + html_custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, +) -> Component: """Create the document root. Args: head_components: The components to add to the head. + html_lang: The language of the document, will be added to the html root element. + html_custom_attrs: custom attributes added to the html root element. Returns: The document root. @@ -277,6 +284,8 @@ def create_document_root(head_components: list[Component] | None = None) -> Comp Main.create(), NextScript.create(), ), + lang=html_lang or "en", + custom_attrs=html_custom_attrs or {}, ) diff --git a/reflex/components/base/document.py b/reflex/components/base/document.py index b867e05c2..8f841cb52 100644 --- a/reflex/components/base/document.py +++ b/reflex/components/base/document.py @@ -1,5 +1,7 @@ """Document components.""" +from typing import Optional + from reflex.components.component import Component @@ -14,6 +16,8 @@ class Html(NextDocumentLib): tag = "Html" + lang: Optional[str] + class DocumentHead(NextDocumentLib): """The document head.""" diff --git a/reflex/components/base/document.pyi b/reflex/components/base/document.pyi index 746a2f18b..6e86badf0 100644 --- a/reflex/components/base/document.pyi +++ b/reflex/components/base/document.pyi @@ -7,6 +7,7 @@ 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 Optional from reflex.components.component import Component class NextDocumentLib(Component): @@ -94,6 +95,7 @@ class Html(NextDocumentLib): def create( # type: ignore cls, *children, + lang: Optional[str] = None, style: Optional[Style] = None, key: Optional[Any] = None, id: Optional[Any] = None, diff --git a/tests/compiler/test_compiler.py b/tests/compiler/test_compiler.py index 1def5ca4f..137ff04d3 100644 --- a/tests/compiler/test_compiler.py +++ b/tests/compiler/test_compiler.py @@ -195,8 +195,11 @@ def test_create_document_root(): """Test that the document root is created correctly.""" # Test with no components. root = utils.create_document_root() + root.render() assert isinstance(root, utils.Html) assert isinstance(root.children[0], utils.DocumentHead) + # Default language. + assert root.lang == "en" # type: ignore # No children in head. assert len(root.children[0].children) == 0 @@ -205,6 +208,10 @@ def test_create_document_root(): utils.NextScript.create(src="foo.js"), utils.NextScript.create(src="bar.js"), ] - root = utils.create_document_root(head_components=comps) # type: ignore + root = utils.create_document_root(head_components=comps, html_lang="rx", html_custom_attrs={"project": "reflex"}) # type: ignore # Two children in head. + assert isinstance(root, utils.Html) assert len(root.children[0].children) == 2 + assert root.lang == "rx" # type: ignore + assert isinstance(root.custom_attrs, dict) + assert root.custom_attrs == {"project": "reflex"}