diff --git a/docs/app/assets/tailwind-theme.css b/docs/app/assets/tailwind-theme.css
index 14806139e37..4a555afa94c 100644
--- a/docs/app/assets/tailwind-theme.css
+++ b/docs/app/assets/tailwind-theme.css
@@ -1226,6 +1226,8 @@
--animate-ellipse-3: ellipse-3 2400ms ease-out infinite;
--animate-ellipse-4: ellipse-4 2400ms ease-out infinite;
--animate-ellipse-reversed: ellipse-reversed 2400ms ease-out infinite;
+ --arrow-sweep-duration: 4s;
+ --animate-arrow-sweep: arrow-sweep var(--arrow-sweep-duration) linear infinite backwards;
/* Radius */
--radius-ui-xxs: calc(var(--radius) - 0.25rem);
--radius-ui-xs: calc(var(--radius) - 0.125rem);
@@ -1237,6 +1239,7 @@
/* Width */
--layout-max-width: 81rem;
--docs-layout-max-width: 69rem;
+ --landing-layout-max-width: 78.5rem;
@keyframes accordion-down {
from {
@@ -1631,6 +1634,20 @@
}
}
+ @keyframes arrow-sweep {
+ 0% {
+ transform: translateX(-100%);
+ }
+
+ 25% {
+ transform: translateX(200%);
+ }
+
+ 100% {
+ transform: translateX(200%);
+ }
+ }
+
}
@@ -2184,6 +2201,6 @@
}
body {
- @apply isolate bg-slate-1 font-sans antialiased;
+ @apply isolate bg-secondary-1 font-sans antialiased;
}
-}
+}
\ No newline at end of file
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py b/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py
index f45e3c13632..52bb83c635e 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py
@@ -183,7 +183,7 @@ def ai_builder_section() -> rx.Component:
),
class_name="grid grid-cols-1 lg:grid-cols-3 gap-12",
),
- class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto",
+ class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto",
),
class_name="bg-gradient-to-b from-white-1 to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12 w-full lg:pt-24 lg:pb-24 pb-10 max-xl:px-6 max-lg:pt-10",
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/divider.py b/docs/app/reflex_docs/pages/docs_landing/views/divider.py
index 0468cc4bc3f..1796075ba6f 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/divider.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/divider.py
@@ -11,7 +11,7 @@ def divider(class_name: str = "") -> rx.Component:
class_name="absolute top-0 -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10"
),
class_name=ui.cn(
- "w-full h-[1px] bg-m-slate-4 dark:bg-m-slate-10 relative max-w-(--docs-layout-max-width) mx-auto",
+ "w-full h-[1px] bg-m-slate-4 dark:bg-m-slate-10 relative max-w-(--landing-layout-max-width) mx-auto",
class_name,
),
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py b/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py
index 419fce7237c..3ed024c832b 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py
@@ -34,5 +34,5 @@ def enterprise_section() -> rx.Component:
),
class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-secondary-4 relative",
),
- class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden",
+ class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden",
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/framework.py b/docs/app/reflex_docs/pages/docs_landing/views/framework.py
index aad79932cff..e7d46eba0f5 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/framework.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/framework.py
@@ -187,5 +187,5 @@ def framework() -> rx.Component:
components_section(),
class_name="flex flex-col lg:flex-row relative",
),
- class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden",
+ class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden",
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/hero.py b/docs/app/reflex_docs/pages/docs_landing/views/hero.py
index 70b440b6e53..42d9fba2c01 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/hero.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/hero.py
@@ -61,6 +61,6 @@ def hero() -> rx.Component:
),
),
class_name=ui.cn(
- "flex lg:flex-row flex-col max-w-(--docs-layout-max-width) mx-auto w-full max-lg:pb-10 max-xl:px-6",
+ "flex lg:flex-row flex-col max-w-(--landing-layout-max-width) mx-auto w-full max-lg:pb-10 max-xl:px-6",
),
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/hosting.py b/docs/app/reflex_docs/pages/docs_landing/views/hosting.py
index 4c7bfba9d5d..4851decde7a 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/hosting.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/hosting.py
@@ -47,5 +47,5 @@ def hosting_section() -> rx.Component:
),
class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-m-slate-4 dark:border-m-slate-10 relative",
),
- class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start max-xl:px-6 lg:mb-24 overflow-hidden",
+ class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start max-xl:px-6 lg:mb-24 overflow-hidden",
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/other.py b/docs/app/reflex_docs/pages/docs_landing/views/other.py
index 3a9172779c5..f3945d82c7d 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/other.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/other.py
@@ -35,5 +35,5 @@ def other_section() -> rx.Component:
faded_borders(),
class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-secondary-4 relative",
),
- class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden",
+ class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden",
)
diff --git a/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py b/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py
index da8ddba27e4..6f94f974f6e 100644
--- a/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py
+++ b/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py
@@ -34,5 +34,5 @@ def self_hosting_section() -> rx.Component:
),
class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-secondary-4 relative",
),
- class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden max-lg:pt-10",
+ class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden max-lg:pt-10",
)
diff --git a/docs/app/reflex_docs/templates/docpage/docpage.py b/docs/app/reflex_docs/templates/docpage/docpage.py
index 96dfe4b5d6b..46880da6d8f 100644
--- a/docs/app/reflex_docs/templates/docpage/docpage.py
+++ b/docs/app/reflex_docs/templates/docpage/docpage.py
@@ -55,6 +55,40 @@ def footer_link_flex(heading: str, links):
)
+def social_menu_item(icon: str, url: str, name: str) -> rx.Component:
+ return rx.el.elements.a(
+ marketing_button(
+ get_icon(icon, class_name="shrink-0"),
+ variant="ghost",
+ size="icon-sm",
+ class_name="text-m-slate-7 dark:text-m-slate-6",
+ native_button=False,
+ ),
+ href=url,
+ custom_attrs={"aria-label": "Social link for " + name},
+ target="_blank",
+ )
+
+
+def menu_socials() -> rx.Component:
+ from reflex_site_shared.constants import (
+ DISCORD_URL,
+ FORUM_URL,
+ GITHUB_URL,
+ LINKEDIN_URL,
+ TWITTER_URL,
+ )
+
+ return rx.box(
+ social_menu_item("twitter_footer", TWITTER_URL, "Twitter"),
+ social_menu_item("github_navbar", GITHUB_URL, "Github"),
+ social_menu_item("discord_navbar", DISCORD_URL, "Discord"),
+ social_menu_item("linkedin_footer", LINKEDIN_URL, "LinkedIn"),
+ social_menu_item("forum_footer", FORUM_URL, "Forum"),
+ class_name="flex flex-row items-center gap-2",
+ )
+
+
def thumb_card(score: int, icon: str, label: str) -> rx.Component:
return rx.el.button(
ui.icon(
@@ -238,7 +272,6 @@ def link_pill(text: str, href: str) -> rx.Component:
@rx.memo
def docpage_footer(path: str):
from reflex_site_shared.constants import FORUM_URL, ROADMAP_URL
- from reflex_site_shared.views.footer import menu_socials
return rx.el.footer(
rx.box(
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py b/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py
index 0c4312787f1..316c40042ee 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py
@@ -572,6 +572,28 @@
circle = """
"""
+
+grok = """
+"""
+
+claude = """"""
+
+openai = """
+"""
+
+gemini = """
+"""
+
+cursor = """"""
+
+arrow_turn = """"""
+
+reflex_small = """"""
+
+docs = """"""
+
+curved_line = """"""
+
ICONS = {
# Socials
"github": github,
@@ -663,6 +685,15 @@
"sun_footer": sun_footer,
"computer_footer": computer_footer,
"circle": circle,
+ "grok": grok,
+ "claude": claude,
+ "openai": openai,
+ "gemini": gemini,
+ "arrow_turn": arrow_turn,
+ "reflex_small": reflex_small,
+ "docs": docs,
+ "cursor": cursor,
+ "curved_line": curved_line,
}
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/constants.py b/packages/reflex-site-shared/src/reflex_site_shared/constants.py
index 850ec53b1e0..1c2379b1b86 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/constants.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/constants.py
@@ -34,7 +34,7 @@
"REFLEX_DEV_WEB_GENERAL_FORM_FEEDBACK_WEBHOOK_URL", ""
)
RECENT_BLOGS_API_URL: str = os.environ.get(
- "RECENT_BLOGS_API_URL", "https://reflex.dev/api/v1/recent-blogs"
+ "RECENT_BLOGS_API_URL", "https://reflex.dev/blog-api/api/v1/recent-blogs"
)
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py b/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py
index 6f84c6211c7..a685adf4da3 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py
@@ -4,12 +4,6 @@
from reflex_site_shared.components.blocks.flexdown import markdown_with_shiki
from reflex_site_shared.templates.webpage import webpage
-contents = f"""
-# Page Not Found
-
-The page at `{rx.State.router.page.raw_path}` doesn't exist.
-"""
-
@webpage(path="/404", title="Page Not Found · Reflex.dev", add_as_page=False)
def page404():
@@ -19,6 +13,11 @@ def page404():
The component.
"""
return rx.box(
- markdown_with_shiki(contents),
+ markdown_with_shiki("# Page Not Found"),
+ rx.el.p(
+ "The page at ",
+ rx.code(rx.State.router.page.raw_path, class_name="code-style"),
+ " doesn't exist.",
+ ),
class_name="h-[80vh] w-full flex flex-col items-center justify-center",
)
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py
index 32e5f3e3288..16803ae025e 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py
@@ -18,7 +18,7 @@ def cta_card():
return rx.el.div(
rx.el.div(
rx.el.span(
- "The Unified Platform to Build and Scale Enterprise Apps",
+ "The Platform to Build and Scale Enterprise Apps",
class_name="text-slate-12 lg:text-3xl text-2xl font-[575]",
),
rx.el.span(
@@ -43,13 +43,13 @@ def cta_card():
),
class_name="flex flex-row gap-4 items-center",
),
- class_name="flex flex-col gap-6 justify-center max-w-[24.5rem]",
+ class_name="flex flex-col gap-6 justify-center max-w-[29.25rem]",
),
rx.image(
- f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/cta.svg",
+ f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/cta_gray_lines_2.svg",
class_name="w-auto h-full pointer-events-none",
loading="lazy",
alt="CTA Card",
),
- class_name="flex flex-row justify-between max-w-(--docs-layout-max-width) mx-auto w-full bg-white/96 dark:bg-m-slate-11 backdrop-blur-[16px] rounded-xl relative overflow-hidden shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_12px_24px_0_rgba(0,0,0,0.08),0_1px_1px_0_rgba(0,0,0,0.01),0_4px_8px_0_rgba(0,0,0,0.03)] dark:shadow-none dark:border dark:border-m-slate-9 pl-16 max-lg:hidden mb-12 mt-24",
+ class_name="flex flex-row justify-between max-w-(--landing-layout-max-width) mx-auto w-full bg-white/96 dark:bg-m-slate-11 backdrop-blur-[16px] rounded-xl relative overflow-hidden shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_12px_24px_0_rgba(0,0,0,0.08),0_1px_1px_0_rgba(0,0,0,0.01),0_4px_8px_0_rgba(0,0,0,0.03)] dark:shadow-none dark:border dark:border-m-slate-9 pl-16 max-lg:hidden mb-12 mt-24",
)
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py
index 834361dbf51..2c8b4c7905a 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py
@@ -1,4 +1,4 @@
-"""Footer module."""
+"""Site footer layout and sections for marketing pages."""
from datetime import datetime
@@ -19,35 +19,12 @@
LINKEDIN_URL,
REFLEX_ASSETS_CDN,
REFLEX_BUILD_URL,
- ROADMAP_URL,
TWITTER_URL,
)
-def ph_1() -> rx.Component:
- """Ph 1.
-
- Returns:
- The component.
- """
- return rx.fragment(
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}logos/dark/ph_1.svg",
- class_name="hidden dark:block h-[36px] w-fit",
- alt="1st product of the day logo",
- loading="lazy",
- ),
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}logos/light/ph_1.svg",
- class_name="dark:hidden block h-[36px] w-fit",
- alt="1st product of the day logo",
- loading="lazy",
- ),
- )
-
-
def logo() -> rx.Component:
- """Logo.
+ """Homepage logo link for the footer.
Returns:
The component.
@@ -69,13 +46,13 @@ def logo() -> rx.Component:
def tab_item(mode: str, icon: str) -> rx.Component:
- """Tab item.
+ """Single color-mode toggle tab (system, light, or dark).
Returns:
The component.
"""
- active_cn = " shadow-[0_-1px_0_0_rgba(0,0,0,0.08)_inset,0_0_0_1px_rgba(0,0,0,0.08)_inset,0_1px_2px_0_rgba(0,0,0,0.02),0_1px_4px_0_rgba(0,0,0,0.02)] dark:shadow-[0_1px_0_0_rgba(255,255,255,0.16)_inset] bg-white dark:bg-m-slate-10 hover:bg-m-slate-2 dark:hover:bg-m-slate-9 text-m-slate-12 dark:text-m-slate-3"
- unactive_cn = " hover:text-m-slate-12 dark:hover:text-m-slate-3 text-m-slate-7 dark:text-m-slate-6"
+ active_cn = " shadow-[0_-1px_0_0_rgba(0,0,0,0.08)_inset,0_0_0_1px_rgba(0,0,0,0.08)_inset,0_1px_2px_0_rgba(0,0,0,0.02),0_1px_4px_0_rgba(0,0,0,0.02)] dark:shadow-[0_1px_0_0_rgba(255,255,255,0.16)_inset] bg-white-1 hover:bg-secondary-2 text-secondary-12"
+ unactive_cn = " hover:text-secondary-12 text-secondary-11"
return rx.el.button(
get_icon(icon, class_name="shrink-0"),
on_click=set_color_mode(mode), # type: ignore[reportArgumentType]
@@ -88,7 +65,7 @@ def tab_item(mode: str, icon: str) -> rx.Component:
def dark_mode_toggle() -> rx.Component:
- """Dark mode toggle.
+ """Footer control group for switching color mode.
Returns:
The component.
@@ -97,12 +74,12 @@ def dark_mode_toggle() -> rx.Component:
tab_item("system", "computer_footer"),
tab_item("light", "sun_footer"),
tab_item("dark", "moon_footer"),
- class_name="flex flex-row gap-0.5 items-center p-0.5 [box-shadow:0_1px_0_0_rgba(0,_0,_0,_0.08),_0_0_0_1px_rgba(0,_0,_0,_0.08),_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] w-fit mt-auto bg-m-slate-1 dark:bg-m-slate-12 rounded-[0.625rem] dark:border dark:border-m-slate-9 border border-transparent lg:ml-auto mr-px",
+ class_name="flex flex-row gap-0.5 items-center p-0.5 [box-shadow:0_1px_0_0_rgba(0,_0,_0,_0.08),_0_0_0_1px_rgba(0,_0,_0,_0.08),_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] w-fit mt-auto bg-secondary-1 rounded-[0.625rem] dark:border dark:border-secondary-4 border border-transparent",
)
def footer_link(text: str, href: str) -> rx.Component:
- """Footer link.
+ """Link styled for footer link columns.
Returns:
The component.
@@ -115,15 +92,15 @@ def footer_link(text: str, href: str) -> rx.Component:
class_name="shrink-0 lg:hidden flex",
),
href=href,
- target="_blank",
- class_name="font-[525] text-m-slate-7 hover:text-m-slate-8 dark:hover:text-m-slate-5 dark:text-m-slate-6 text-sm transition-color w-full lg:w-fit flex flex-row justify-between items-center",
+ target="_blank" if not href.startswith("/") else "",
+ class_name="font-[525] text-secondary-11 hover:text-secondary-12 text-sm transition-color w-full lg:w-fit flex flex-row justify-between items-center min-h-[24px]",
)
def footer_link_flex(
heading: str, links: list[rx.Component], class_name: str = ""
) -> rx.Component:
- """Footer link flex.
+ """Column with a heading and stacked footer links.
Returns:
The component.
@@ -131,51 +108,52 @@ def footer_link_flex(
return rx.el.div(
rx.el.h3(
heading,
- class_name="text-xs text-m-slate-12 dark:text-m-slate-3 font-semibold w-fit mb-3",
+ class_name="text-xs text-secondary-12 font-[525] w-fit mb-2",
),
*links,
class_name=ui.cn("flex flex-col gap-2", class_name),
)
-def social_menu_item(icon: str, url: str, name: str) -> rx.Component:
- """Social menu item.
+def social_menu_item(
+ icon: str, url: str, name: str, class_name: str = ""
+) -> rx.Component:
+ """Icon link to a social profile or community.
Returns:
The component.
"""
return rx.el.elements.a(
- marketing_button(
- get_icon(icon, class_name="shrink-0"),
- variant="ghost",
- size="icon-sm",
- class_name="text-m-slate-7 dark:text-m-slate-6",
- native_button=False,
- ),
+ get_icon(icon, class_name="size-5 shrink-0"),
href=url,
custom_attrs={"aria-label": "Social link for " + name},
target="_blank",
+ class_name=ui.cn(
+ "text-secondary-11 hover:text-secondary-12 transition-colors flex items-center justify-center size-7 rounded-md",
+ "lg:size-auto lg:h-full lg:w-full lg:rounded-none",
+ class_name,
+ ),
)
def menu_socials() -> rx.Component:
- """Menu socials.
+ """Row of major social and community links.
Returns:
The component.
"""
- return rx.box(
+ return rx.el.div(
social_menu_item("twitter_footer", TWITTER_URL, "Twitter"),
social_menu_item("github_navbar", GITHUB_URL, "Github"),
social_menu_item("discord_navbar", DISCORD_URL, "Discord"),
social_menu_item("linkedin_footer", LINKEDIN_URL, "LinkedIn"),
social_menu_item("forum_footer", FORUM_URL, "Forum"),
- class_name="flex flex-row items-center gap-2",
+ class_name="flex flex-row items-center gap-2 lg:gap-0 lg:grid lg:grid-cols-5 lg:border-y lg:border-secondary-4 lg:divide-x lg:divide-secondary-4 lg:w-full lg:h-16",
)
def newsletter_input() -> rx.Component:
- """Newsletter input.
+ """Email signup form or post-subscribe confirmation.
Returns:
The component.
@@ -188,11 +166,11 @@ def newsletter_input() -> rx.Component:
rx.icon(
tag="circle-check",
size=16,
- class_name="!text-violet-9",
+ class_name="!text-primary-9",
),
rx.text(
"Thanks for subscribing!",
- class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-semibold",
+ class_name="text-xs font-[525] text-secondary-12",
),
class_name="flex flex-row items-center gap-2",
),
@@ -205,17 +183,18 @@ def newsletter_input() -> rx.Component:
class_name="flex flex-col flex-wrap gap-2",
),
rx.form(
- rx.el.input(
+ ui.input(
placeholder="Email",
name="input_email",
type="email",
required=True,
- class_name="relative [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.08)_inset,_0_0_0_1px_rgba(0,_0,_0,_0.08)_inset,_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] rounded-lg h-8 px-2 py-1.5 w-[12rem] text-sm placeholder:text-m-slate-7 dark:placeholder:text-m-slate-6 font-[525] focus:outline-none outline-none dark:border dark:border-m-slate-9 dark:bg-m-slate-11",
+ size="sm",
+ class_name="w-[195px]",
),
- marketing_button(
+ ui.button(
"Subscribe",
type="submit",
- variant="outline",
+ variant="outline-shadow",
size="sm",
class_name="w-fit max-w-full",
),
@@ -228,96 +207,149 @@ def newsletter_input() -> rx.Component:
def newsletter() -> rx.Component:
- """Newsletter.
+ """Newsletter call-to-action block with heading.
Returns:
The component.
"""
return rx.el.div(
- rx.text(
- "Get updates",
- class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-semibold",
- ),
newsletter_input(),
- class_name="flex flex-col items-start gap-4 self-stretch",
+ class_name="flex flex-col items-start gap-4 self-stretch mt-6",
)
@rx.memo
def footer_index(class_name: str = "", grid_class_name: str = "") -> rx.Component:
- """Footer index.
+ """Full marketing footer: logo, newsletter, links, and legal.
Returns:
The component.
"""
return rx.el.footer(
rx.el.div(
- logo(),
rx.el.div(
- footer_link_flex(
- "Product",
- [
- footer_link("AI Builder", REFLEX_BUILD_URL),
- footer_link("Framework", "/framework"),
- footer_link("Cloud", "/cloud"),
- ],
- ),
- footer_link_flex(
- "Solutions",
- [
- footer_link("Enterprise", "/use-cases"),
- footer_link("Finance", "/use-cases/finance"),
- footer_link("Healthcare", "/use-cases/healthcare"),
- footer_link("Consulting", "/use-cases/consulting"),
- footer_link("Government", "/use-cases/government"),
- ],
- ),
- footer_link_flex(
- "Resources",
- [
- footer_link("Documentation", "/docs"),
- footer_link("FAQ", "/faq"),
- footer_link("Common Errors", "/errors"),
- footer_link("Roadmap", ROADMAP_URL),
- footer_link("Changelog", CHANGELOG_URL),
- ],
- ),
rx.el.div(
- class_name="absolute -top-24 -right-px w-px h-24 bg-gradient-to-b from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ rx.el.div(
+ rx.el.div(
+ class_name="absolute -right-px -top-24 h-24 w-px bg-linear-to-b from-transparent to-current text-secondary-4 max-lg:hidden"
+ ),
+ logo(),
+ newsletter(),
+ rx.el.div(
+ menu_socials(),
+ class_name="mt-6 w-full lg:-mx-8 lg:w-[calc(100%+4rem)]",
+ ),
+ rx.el.div(
+ dark_mode_toggle(),
+ rx.el.span(
+ f"Copyright © {datetime.now().year} Pynecone, Inc.",
+ class_name="text-xs font-[525] text-secondary-11",
+ ),
+ rx.el.div(
+ server_status(StatusState.status), class_name="-ml-2.5"
+ ),
+ class_name="mt-auto justify-start flex flex-col items-start gap-6 pt-8",
+ ),
+ class_name="flex flex-col lg:pr-8 lg:pl-8 lg:pb-8 min-w-[337px] lg:border-r border-secondary-4 shrink-0 relative pt-16",
+ ),
+ rx.el.div(
+ footer_link_flex(
+ "Product",
+ [
+ footer_link("AI Builder", REFLEX_BUILD_URL),
+ footer_link(
+ "Agent Toolkit",
+ "/docs/ai/integrations/overview/",
+ ),
+ footer_link(
+ "Enterprise",
+ "/docs/enterprise/overview/",
+ ),
+ footer_link("App Management", "/hosting"),
+ footer_link("Pricing", "/pricing"),
+ ],
+ ),
+ footer_link_flex(
+ "Solutions",
+ [
+ footer_link("Enterprise", "/use-cases"),
+ footer_link("Finance", "/use-cases/finance"),
+ footer_link("Healthcare", "/use-cases/healthcare"),
+ footer_link("Consulting", "/use-cases/consulting"),
+ footer_link("Government", "/use-cases/government"),
+ ],
+ ),
+ footer_link_flex(
+ "Resources",
+ [
+ footer_link("Blog", "/blog"),
+ footer_link("Templates", "/templates"),
+ footer_link(
+ "Integrations",
+ "/docs/ai/integrations/overview/",
+ ),
+ ],
+ ),
+ footer_link_flex(
+ "Migration",
+ [
+ footer_link("From No-Code", "/migration/no-code/"),
+ footer_link("From Low-Code", "/migration/low-code/"),
+ footer_link(
+ "From Other Frameworks",
+ "/migration/other-frameworks/",
+ ),
+ footer_link(
+ "From Other AI Tools",
+ "/migration/other-ai-tools/",
+ ),
+ ],
+ ),
+ footer_link_flex(
+ "Developers",
+ [
+ footer_link("Documentation", "/docs"),
+ footer_link("Changelog", CHANGELOG_URL),
+ footer_link("Common Errors", "/errors"),
+ ],
+ ),
+ footer_link_flex(
+ "Company",
+ [
+ footer_link("About", "/about"),
+ footer_link(
+ "Careers",
+ "https://www.ycombinator.com/companies/reflex/jobs",
+ ),
+ footer_link(
+ "Privacy Policy",
+ "https://build.reflex.dev/privacy-policy",
+ ),
+ footer_link(
+ "Terms of Service",
+ "https://build.reflex.dev/terms-of-use",
+ ),
+ ],
+ ),
+ class_name=ui.cn(
+ "grid grid-cols-1 lg:grid-cols-3 gap-x-12 gap-y-12 w-full relative lg:px-8 pb-8 pt-16",
+ grid_class_name,
+ ),
+ ),
+ class_name="flex lg:flex-row flex-col max-lg:gap-6 w-full",
),
- class_name=ui.cn(
- "grid grid-cols-1 lg:grid-cols-3 gap-12 w-full lg:pr-12 pb-8 lg:border-r border-m-slate-4 dark:border-m-slate-10 xl:ml-auto relative",
- grid_class_name,
+ rx.el.div(
+ class_name="absolute -top-px -right-24 w-24 h-px bg-linear-to-l from-transparent to-current text-secondary-4 max-lg:hidden"
),
- ),
- rx.el.div(
- newsletter(),
- ph_1(),
- dark_mode_toggle(),
- class_name="flex flex-col gap-6 lg:pl-12 pb-8 max-lg:justify-start",
- ),
- class_name="flex flex-col max-lg:gap-6 lg:flex-row w-full",
- ),
- rx.el.div(
- server_status(StatusState.status),
- rx.el.div(
- rx.el.span(
- f"Copyright © {datetime.now().year} Pynecone, Inc.",
- class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-medium",
+ rx.el.div(
+ class_name="absolute -top-px -left-24 w-24 h-px bg-linear-to-r from-transparent to-current text-secondary-4 max-lg:hidden"
),
- menu_socials(),
- class_name="flex flex-row items-center gap-6",
- ),
- rx.el.div(
- class_name="absolute -top-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
- ),
- rx.el.div(
- class_name="absolute -top-px -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden"
+ class_name="relative flex flex-col w-full lg:border-x border-secondary-4 border-t",
),
- class_name="flex flex-row items-center justify-between py-6 gap-4 w-full border-t border-m-slate-4 dark:border-m-slate-10 relative",
+ class_name="w-full min-w-0 lg:px-2",
),
class_name=ui.cn(
- "flex flex-col max-w-(--docs-layout-max-width) justify-center items-center w-full mx-auto mt-24 max-lg:px-4 overflow-hidden",
+ "flex flex-col w-full min-w-0 max-w-(--landing-layout-max-width) items-stretch mx-auto max-lg:px-4 lg:border-x border-secondary-4 relative",
class_name,
),
)
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py b/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py
index 7f00ba51ad1..97a5806c28e 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py
@@ -121,10 +121,10 @@ def hosting_banner() -> rx.Component:
ui.button(
"Learn more",
ui.icon("ArrowRight01Icon"),
- variant="ghost",
+ variant="ghost-highlight",
size="xs",
aria_label="Learn more",
- class_name="text-m-slate-3 dark:hover:text-m-slate-5 max-lg:hidden",
+ class_name="max-lg:hidden text-white hover:text-primary-10",
),
class_name="flex flex-row items-center md:gap-4 gap-2",
),
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py
index d50a5a62aca..dc812a53e15 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py
@@ -7,65 +7,19 @@
from reflex_site_shared.backend.get_blogs import BlogPostDict, RecentBlogsState
from reflex_site_shared.components.icons import get_icon
from reflex_site_shared.components.marketing_button import button as marketing_button
-from reflex_site_shared.components.marquee import marquee
from reflex_site_shared.constants import (
CHANGELOG_URL,
- CONTRIBUTING_URL,
- DISCUSSIONS_URL,
+ DISCORD_URL,
GITHUB_STARS,
GITHUB_URL,
- JOBS_BOARD_URL,
REFLEX_ASSETS_CDN,
REFLEX_BUILD_URL,
)
from reflex_site_shared.views.sidebar import navbar_sidebar_button
-
-
-def social_proof_card(image: str) -> rx.Component:
- """Social proof card.
-
- Returns:
- The component.
- """
- return rx.el.div(
- rx.image(
- f"{REFLEX_ASSETS_CDN}companies/{rx.color_mode_cond('light', 'dark')}/{image}_small.svg",
- loading="lazy",
- alt=f"{image} logo",
- class_name="w-auto h-fit pointer-events-none",
- ),
- class_name="flex justify-center items-center px-3",
- )
-
-
-def logos_carousel() -> rx.Component:
- """Logos carousel.
-
- Returns:
- The component.
- """
- logos = [
- "agricole",
- "man",
- "shell",
- "red_hat",
- "accenture",
- "dell",
- "microsoft",
- "world",
- "ford",
- "unicef",
- "nike",
- ]
- return marquee(
- *[social_proof_card(logo) for logo in logos],
- direction="left",
- gradient_color="light-dark(var(--c-white-1), var(--c-m-slate-11))",
- class_name="h-[1.625rem] w-full overflow-hidden mt-auto",
- gradient_width=65,
- speed=25,
- pause_on_hover=False,
- )
+from reflex_site_shared.views.workflow_stage import (
+ workflow_stage_image,
+ workflow_stage_row,
+)
def github() -> rx.Component:
@@ -152,7 +106,7 @@ def menu_trigger(title: str, content: rx.Component) -> rx.Component:
def menu_content(content: rx.Component, class_name: str = "") -> rx.Component:
- """Menu content.
+ """Styled dropdown panel wrapper for navigation menu items.
Returns:
The component.
@@ -169,36 +123,150 @@ def menu_content(content: rx.Component, class_name: str = "") -> rx.Component:
)
-def platform_item(image: str, title: str, description: str, href: str) -> rx.Component:
- """Platform item.
+def products_column_gutter() -> rx.Component:
+ """Vertical separator between product mega-menu columns.
Returns:
The component.
"""
return rx.el.div(
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/{image}",
- alt=title,
- class_name="size-18",
+ rx.el.div(
+ class_name="pointer-events-none absolute top-0 left-0 h-12 w-full border-b border-secondary-4",
),
+ role="presentation",
+ class_name="w-8 shrink-0 bg-secondary-1 border-secondary-4 border-x relative",
+ )
+
+
+def products_iterate_column_body() -> rx.Component:
+ """Iterate stage column: framework pitch and GitHub link.
+
+ Returns:
+ The component.
+ """
+ return rx.el.div(
+ rx.el.elements.a(
+ rx.el.div(
+ rx.el.span(
+ "Framework",
+ class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors",
+ ),
+ badge("Open source"),
+ class_name="flex flex-row items-center gap-2.5",
+ ),
+ rx.el.p(
+ "The full-stack Python framework, optimized to build with AI agents—apps that can be used by humans and agents alike.",
+ class_name="text-secondary-11 text-sm font-[475]",
+ ),
+ href="/open-source/",
+ class_name="group flex flex-col gap-2 items-center text-center",
+ ),
+ rx.el.elements.a(
+ get_icon(
+ "github_navbar",
+ class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9",
+ ),
+ "View on GitHub",
+ ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"),
+ href=GITHUB_URL,
+ target="_blank",
+ class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group mt-auto",
+ ),
+ class_name="flex flex-col p-6 text-center justify-center items-center min-h-[252px] h-full",
+ )
+
+
+def products_ship_column_body() -> rx.Component:
+ """Ship stage column: deploy messaging and illustration.
+
+ Returns:
+ The component.
+ """
+ return rx.el.elements.a(
rx.el.div(
rx.el.span(
- title,
- class_name="dark:text-m-slate-3 text-m-slate-12 text-sm font-[525]",
+ "Deploy, monitor & scale",
+ class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors",
+ ),
+ class_name="flex flex-row items-center gap-2.5 px-6 pt-6",
+ ),
+ rx.el.p(
+ "One click to enterprise-grade infrastructure. Track, version, and grow across teams.",
+ class_name="text-secondary-11 text-sm font-[475] px-6",
+ ),
+ rx.el.div(
+ rx.image(
+ src=f"{REFLEX_ASSETS_CDN}landing/features/{rx.color_mode_cond('light', 'dark')}/ship_navbar_3.svg",
+ alt="Deploy, monitor & scale",
+ loading="lazy",
+ class_name="h-auto w-full max-w-full object-cover",
+ ),
+ class_name="flex w-full mt-auto",
+ ),
+ href="/hosting/",
+ class_name="group flex flex-col gap-2 text-center justify-center items-center min-h-[252px]",
+ )
+
+
+def products_build_column_body() -> rx.Component:
+ """Build stage column: AI builder and agent toolkit teaser.
+
+ Returns:
+ The component.
+ """
+ return rx.el.div(
+ rx.el.elements.a(
+ rx.el.div(
+ rx.el.span(
+ "AI app builder",
+ class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors",
+ ),
+ badge("New"),
+ class_name="flex flex-row items-center gap-2.5 px-6 pt-6",
+ ),
+ rx.el.p(
+ "Describe it, Reflex builds it.",
+ class_name="text-secondary-11 text-sm font-[475] px-6 pb-6",
+ ),
+ href=REFLEX_BUILD_URL,
+ target="_blank",
+ class_name="group flex flex-col gap-2 items-center text-center",
+ ),
+ rx.el.div(
+ get_icon(
+ "arrow_turn", class_name="shrink-0 text-secondary-10 -translate-y-0.75"
),
rx.el.span(
- description,
- class_name="dark:text-m-slate-6 text-m-slate-7 text-sm font-[475]",
+ "OR",
+ class_name="px-2 font-mono text-xs font-[415] uppercase text-secondary-12",
),
- class_name="flex flex-col",
+ get_icon(
+ "arrow_turn",
+ class_name="shrink-0 text-secondary-10 rotate-180 translate-y-0.75",
+ ),
+ class_name="flex w-full shrink-0 items-center justify-center py-3 border-y border-secondary-4 px-6 bg-secondary-1",
+ ),
+ rx.el.elements.a(
+ rx.el.div(
+ rx.el.span(
+ "Agent Toolkit",
+ class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors",
+ ),
+ class_name="flex flex-row items-center gap-2.5 px-6 pt-6",
+ ),
+ rx.el.p(
+ "Get started with our MCP and Skills.",
+ class_name="text-secondary-11 text-sm font-[475] px-6 pb-6",
+ ),
+ href="/docs/ai/integrations/ai-onboarding/",
+ class_name="group flex flex-col gap-2 items-center text-center",
),
- rx.el.elements.a(class_name="absolute inset-0", href=href),
- class_name="p-4 flex flex-row gap-6 relative cursor-pointer rounded-sm hover-card-shadow",
+ class_name="flex flex-col text-center justify-center items-center min-h-[252px]",
)
-def platform_content() -> rx.Component:
- """Platform content.
+def products_content() -> rx.Component:
+ """Mega-menu body for Products: build, iterate, ship columns.
Returns:
The component.
@@ -207,67 +275,41 @@ def platform_content() -> rx.Component:
rx.el.div(
rx.el.div(
rx.el.div(
- rx.el.div(
- rx.el.span(
- "AI Builder",
- class_name="dark:text-m-slate-3 text-m-slate-12 text-lg font-semibold mb-2",
- ),
- rx.el.span(
- "Build production-ready web apps for your team in seconds with AI-powered code generation.",
- class_name="dark:text-m-slate-6 text-m-slate-7 text-sm font-medium",
- ),
- class_name="p-4 flex flex-col relative hover-card-shadow rounded-md",
+ workflow_stage_row(
+ "Build", right=workflow_stage_image(sweep_index=0)
),
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/ai_builder_pattern.svg",
- alt="AI Builder Navbar Pattern",
- class_name="pointer-events-none",
- ),
- rx.el.elements.a(
- class_name="absolute inset-0",
- href=REFLEX_BUILD_URL,
- target="_blank",
- ),
- class_name="relative flex flex-col hover-card-shadow rounded-md",
- ),
- class_name="p-4 flex flex-col rounded-xl bg-white-1 dark:bg-m-slate-11 h-full shadow-card dark:shadow-card-dark dark:border-r dark:border-m-slate-9",
- ),
- rx.el.div(
- platform_item(
- "framework_pixel.svg",
- "Reflex Framework",
- "Iterate on full-stack apps in pure Python. No JavaScript required.",
- "/open-source/",
- ),
- platform_item(
- "cloud_pixel.svg",
- "Cloud Hosting",
- "Deploy your app with a single command to Reflex Cloud.",
- "/hosting/",
+ products_build_column_body(),
+ class_name="flex min-w-0 flex-1 shrink-0 flex-col",
),
+ products_column_gutter(),
rx.el.div(
- rx.el.span(
- "Reflex Is The Operating System ",
- rx.el.br(),
- " for Enterprise Apps",
- class_name="dark:text-m-slate-6 text-m-slate-7 font-mono font-[415] text-[0.75rem] leading-4.5 uppercase",
+ workflow_stage_row(
+ "Iterate",
+ left=workflow_stage_image(size="small", sweep_index=1),
+ right=workflow_stage_image(size="small", sweep_index=2),
),
- rx.image(
- src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/squares_navbar.svg",
- alt="Squares Navbar",
- class_name="absolute bottom-4 right-4 pointer-events-none",
+ products_iterate_column_body(),
+ class_name="flex min-w-0 flex-1 shrink-0 flex-col",
+ ),
+ products_column_gutter(),
+ rx.el.div(
+ workflow_stage_row(
+ "Ship", left=workflow_stage_image(sweep_index=3)
),
- class_name="relative p-4",
+ products_ship_column_body(),
+ class_name="flex min-w-0 flex-1 shrink-0 flex-col",
),
- class_name="p-4 flex flex-col h-full",
+ class_name="flex min-h-0 w-full flex-row bg-white-1",
),
- class_name="w-[46.5rem] grid grid-cols-2",
+ products_menu_footer(),
+ class_name="flex max-w-[min(100vw-2rem,1240px)] w-[1240px] flex-col overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark",
),
+ class_name="p-0",
)
def solutions_item(title: str, icon: str, href: str) -> rx.Component:
- """Solutions item.
+ """Solutions submenu row with icon and title.
Returns:
The component.
@@ -275,16 +317,51 @@ def solutions_item(title: str, icon: str, href: str) -> rx.Component:
return rx.el.elements.a(
ui.icon(
icon,
- class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6 size-4.5",
+ stroke_width=1.5,
+ class_name="shrink-0 text-secondary-11 size-5",
),
title,
href=href,
- class_name="flex flex-row px-4 py-2 rounded-sm text-sm font-[525] text-m-slate-12 dark:text-m-slate-3 gap-3 items-center justify-start cursor-pointer hover-card-shadow",
+ class_name="flex flex-row px-4 py-2 rounded-sm text-sm font-[525] text-secondary-12 gap-3 items-center justify-start cursor-pointer hover-card-shadow text-nowrap",
+ )
+
+
+def solutions_row(
+ title: str,
+ icon: str,
+ href: str,
+ *,
+ trailing_pill: str | None = None,
+) -> rx.Component:
+ """Full-width solutions link row with optional badge.
+
+ Returns:
+ The component.
+ """
+ link_children: list[rx.Component] = [
+ ui.icon(
+ icon,
+ stroke_width=1.5,
+ class_name="shrink-0 text-secondary-11 size-5",
+ ),
+ rx.el.span(
+ title,
+ class_name="text-sm font-[525] text-secondary-12 text-nowrap",
+ ),
+ ]
+ if trailing_pill:
+ link_children.append(
+ badge(trailing_pill),
+ )
+ return rx.el.elements.a(
+ *link_children,
+ href=href,
+ class_name="flex flex-row px-4 py-2 rounded-sm gap-3 items-center w-full cursor-pointer hover-card-shadow text-nowrap",
)
def solutions_column(title: str, items: list[tuple[str, str, str]]) -> rx.Component:
- """Solutions column.
+ """Grouped solutions links under a section heading.
Returns:
The component.
@@ -293,7 +370,7 @@ def solutions_column(title: str, items: list[tuple[str, str, str]]) -> rx.Compon
rx.el.div(
rx.el.span(
title,
- class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed dark:border-m-slate-8 border-m-slate-6 dark:text-m-slate-6 text-m-slate-7",
+ class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed border-secondary-6 text-secondary-11",
),
class_name="px-4 pt-4 flex flex-col",
),
@@ -306,7 +383,7 @@ def solutions_column(title: str, items: list[tuple[str, str, str]]) -> rx.Compon
def blog_item(post: BlogPostDict) -> rx.Component:
- """Blog item.
+ """Compact preview card for a recent blog post.
Returns:
The component.
@@ -316,7 +393,7 @@ def blog_item(post: BlogPostDict) -> rx.Component:
rx.moment(
post["date"],
format="MMM DD YYYY",
- class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase text-nowrap",
+ class_name="text-secondary-11 text-xs font-[415] font-mono uppercase text-nowrap",
),
rx.image(
src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/squares_blog.svg",
@@ -327,7 +404,7 @@ def blog_item(post: BlogPostDict) -> rx.Component:
),
rx.el.span(
post["title"],
- class_name="dark:text-m-slate-3 text-m-slate-12 text-sm font-[525] group-hover:text-primary-10 dark:group-hover:text-primary-9 line-clamp-3",
+ class_name="text-secondary-12 text-base font-[525] group-hover:text-primary-10 line-clamp-3",
),
rx.el.elements.a(
href=post["url"],
@@ -337,30 +414,20 @@ def blog_item(post: BlogPostDict) -> rx.Component:
)
-def blog_column() -> rx.Component:
- """Blog column.
+def badge(text: str) -> rx.Component:
+ """Small pill label for nav highlights (e.g. New, Enterprise).
Returns:
The component.
"""
return rx.el.div(
- rx.foreach(
- RecentBlogsState.posts[:2],
- blog_item,
- ),
- rx.el.elements.a(
- "Read All in Blog",
- ui.icon("ArrowRight01Icon", class_name="ml-auto"),
- href="/blog",
- class_name="dark:text-m-slate-3 text-m-slate-12 text-sm font-[525] h-10 flex items-center justify-start gap-2 hover:text-primary-10 dark:hover:text-primary-9 mt-auto",
- ),
- on_mount=RecentBlogsState.fetch_recent_blogs,
- class_name="flex flex-col gap-6 p-4 h-full",
+ text,
+ class_name="text-secondary-11 text-xs font-[475] bg-secondary-1 px-1.5 h-5 rounded-md flex items-center justify-center border border-secondary-4",
)
-def customers_column() -> rx.Component:
- """Customers column.
+def case_studies_column() -> rx.Component:
+ """Case study teaser and book-demo CTA for Solutions menu.
Returns:
The component.
@@ -369,32 +436,55 @@ def customers_column() -> rx.Component:
rx.el.div(
rx.el.div(
rx.el.span(
- "Customers",
- class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed dark:border-m-slate-8 border-m-slate-6 dark:text-m-slate-6 text-m-slate-7",
+ "Case Studies",
+ class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed border-secondary-6 text-secondary-11",
),
class_name="px-4 pt-4 flex flex-col",
),
rx.el.div(
- rx.el.span(
- "Read Stories How Teams Use Reflex",
- class_name="text-m-slate-12 dark:text-m-slate-3 text-lg font-[575]",
+ rx.el.elements.a(
+ "How Autodesk saved 25% of their development time",
+ href="/customers/autodesk/",
+ class_name="text-secondary-12 text-lg font-[525] hover:text-primary-10 dark:hover:text-primary-9 mt-auto",
),
- rx.el.span(
- "Discover how companies build internal tools, AI apps, and production dashboards in pure Python.",
- class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475]",
+ rx.el.div(
+ badge("Enterprise"),
+ badge("AI"),
+ class_name="flex flex-row gap-2",
+ ),
+ class_name="flex flex-col gap-2 px-4 pb-4",
+ ),
+ rx.el.div(
+ class_name="h-px w-[calc(100%+2rem)] -mx-4 shrink-0 bg-secondary-4",
+ ),
+ rx.el.div(
+ demo_form_dialog(
+ trigger=rx.el.div(
+ rx.el.div(
+ "Book a Demo",
+ ui.icon(
+ "ArrowRight01Icon",
+ class_name="size-5",
+ stroke_width=1.5,
+ ),
+ class_name="flex flex-row items-center justify-between text-sm font-[525] text-secondary-12",
+ ),
+ rx.el.span(
+ "Reflex in action with your team.",
+ class_name="text-secondary-11 text-sm font-[475]",
+ ),
+ class_name="flex flex-col px-4 py-2 rounded-sm hover-card-shadow cursor-pointer ",
+ ),
),
- logos_carousel(),
- class_name="flex flex-col gap-2 px-4 pb-4 h-full",
),
- rx.el.elements.a(class_name="absolute inset-0", href="/customers/"),
- class_name="flex flex-col gap-6 hover-card-shadow rounded-lg relative h-full hover:[--m-slate-11:var(--m-slate-10)] hover:shadow-card dark:hover:shadow-card-dark",
+ class_name="flex flex-col relative h-full justify-between",
),
- class_name="p-4 block rounded-lg shadow-card dark:shadow-card-dark z-[1] bg-white-1 dark:bg-m-slate-11 dark:border-x dark:border-m-slate-9",
+ class_name="p-4 block z-[1] bg-secondary-1 dark:border-x dark:border-secondary-4 w-[296px]",
)
def solutions_content() -> rx.Component:
- """Solutions content.
+ """Mega-menu body for Solutions: industries, features, case studies.
Returns:
The component.
@@ -404,111 +494,180 @@ def solutions_content() -> rx.Component:
rx.el.div(
rx.el.div(
solutions_column(
- "Who's It For",
+ "Industries",
[
- ("Executives", "LocationUser01Icon", "/use-cases/"),
- ("Developers", "SourceCodeSquareIcon", "/use-cases/"),
- ("Data Teams", "DatabaseIcon", "/use-cases/"),
+ ("AI", "OfficeIcon", "/use-cases/"),
+ ("Finance", "Wallet05Icon", "/use-cases/finance/"),
+ ("Healthcare", "HealthIcon", "/use-cases/healthcare/"),
(
- "Non Technical",
- "CursorCircleSelection02Icon",
- "/use-cases/",
+ "Tech / SaaS",
+ "SourceCodeCircleIcon",
+ "/use-cases/consulting/",
),
+ ("Government", "BankIcon", "/use-cases/government/"),
],
),
solutions_column(
- "Industries",
+ "Features",
[
- ("Enterprise", "OfficeIcon", "/use-cases/"),
- ("Finance", "Wallet05Icon", "/use-cases/finance/"),
- ("Healthcare", "HealthIcon", "/use-cases/healthcare/"),
+ ("Security", "ShieldKeyIcon", "/docs/enterprise/overview/"),
(
- "Consulting",
- "DocumentValidationIcon",
- "/use-cases/consulting/",
+ "Auth",
+ "LoginMethodIcon",
+ "/docs/authentication/authentication-overview/",
+ ),
+ (
+ "Role-based access",
+ "UserUnlock01Icon",
+ "/docs/hosting/adding-members/",
+ ),
+ (
+ "On-prem & VPC Deploy",
+ "ServerStack01Icon",
+ "/blog/on-premises-deployment/",
),
(
- "Government",
- "BankIcon",
- "/use-cases/government/",
+ "Integrations",
+ "PlugSocketIcon",
+ "/docs/ai/integrations/overview/",
),
],
),
class_name="grid grid-cols-2",
),
- class_name="p-4 flex flex-col rounded-xl bg-white-1 dark:bg-m-slate-11 h-full w-[28rem] shadow-card dark:shadow-card-dark dark:border-r dark:border-m-slate-9",
+ class_name="p-4 flex flex-col bg-white-1 h-full w-[28rem] shadow-card dark:shadow-card-dark border-r border-secondary-4",
),
- rx.el.div(
- solutions_column(
- "Migration",
- [
- (
- "Switch from No Code",
- "WebDesign01Icon",
- "/migration/no-code/",
- ),
- (
- "Switch from Low Code",
- "SourceCodeSquareIcon",
- "/migration/low-code/",
- ),
- (
- "Switch from Other Frameworks",
- "CodeIcon",
- "/migration/other-frameworks/",
- ),
- (
- "Switch from Other AI tools",
- "ArtificialIntelligence04Icon",
- "/migration/other-ai-tools/",
- ),
- ],
+ case_studies_column(),
+ class_name="flex flex-row",
+ ),
+ )
+
+
+def resources_agent_column() -> rx.Component:
+ """Agent onboarding links (agents, MCP, skills).
+
+ Returns:
+ The component.
+ """
+ return rx.el.div(
+ rx.el.div(
+ rx.el.span(
+ "Agent onboarding",
+ class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed border-secondary-6 text-secondary-11",
+ ),
+ class_name="px-4 pt-4 flex flex-col",
+ ),
+ rx.el.div(
+ solutions_row(
+ "Agents",
+ "RoboticIcon",
+ "/docs/ai/integrations/mcp-installation/",
+ trailing_pill="Overview",
+ ),
+ solutions_row(
+ "MCP", "McpServerIcon", "/docs/ai/integrations/mcp-overview/"
+ ),
+ solutions_row("Skills", "ToolsIcon", "/docs/ai/integrations/skills/"),
+ class_name="flex flex-col",
+ ),
+ class_name="flex flex-col gap-4",
+ )
+
+
+def resources_blog_column() -> rx.Component:
+ """Recent posts list with link to the blog index.
+
+ Returns:
+ The component.
+ """
+ return rx.el.div(
+ rx.foreach(
+ RecentBlogsState.posts[:2],
+ blog_item,
+ ),
+ rx.el.elements.a(
+ "Read All in Blog",
+ ui.icon("ArrowRight01Icon", class_name="size-4 shrink-0"),
+ href="/blog",
+ class_name="text-secondary-12 text-sm font-[525] flex items-center gap-1.5 hover:text-primary-10 dark:hover:text-primary-9 mt-auto pt-3",
+ ),
+ on_mount=RecentBlogsState.fetch_recent_blogs,
+ class_name="flex flex-col gap-4 p-8 h-full justify-between border-l border-secondary-4",
+ )
+
+
+def products_menu_footer() -> rx.Component:
+ """Footer links inside the Products mega-menu.
+
+ Returns:
+ The component.
+ """
+ return rx.el.div(
+ rx.el.div(
+ rx.el.elements.a(
+ get_icon(
+ "docs",
+ class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9",
),
- class_name="p-4 flex flex-col h-full",
+ "View All Docs",
+ ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"),
+ href="/docs/",
+ target="_blank",
+ class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group",
),
- class_name="flex flex-row",
+ rx.el.elements.a(
+ get_icon(
+ "reflex_small",
+ class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9",
+ ),
+ "Get started for free",
+ ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"),
+ href=REFLEX_BUILD_URL,
+ target="_blank",
+ class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group",
+ ),
+ class_name="flex flex-row items-center gap-8 px-6 py-3",
),
+ class_name="flex flex-col w-full shrink-0 bg-white-1 border-t border-secondary-4",
)
-def resources_content() -> rx.Component:
- """Resources content.
+def resources_menu_footer() -> rx.Component:
+ """Community links footer inside the Resources mega-menu.
Returns:
The component.
"""
- return menu_content(
+ return rx.el.div(
rx.el.div(
- rx.el.div(
- solutions_column(
- "Developers",
- [
- ("Templates", "Layout02Icon", "/templates/"),
- (
- "Integrations",
- "PlugSocketIcon",
- "/docs/ai-builder/integrations/overview/",
- ),
- ("Changelog", "Clock02Icon", CHANGELOG_URL),
- ("Contributing", "GitCommitIcon", CONTRIBUTING_URL),
- ("Discussion", "BubbleChatIcon", DISCUSSIONS_URL),
- ("FAQ", "HelpSquareIcon", "/faq/"),
- ],
+ rx.el.elements.a(
+ get_icon(
+ "github_navbar",
+ class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9",
),
- class_name="p-4 flex flex-col rounded-xl bg-m-slate-1 dark:bg-m-slate-12 h-full",
+ "GitHub",
+ href=GITHUB_URL,
+ target="_blank",
+ class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group",
),
- customers_column(),
- rx.el.div(
- blog_column(),
- class_name="p-4 flex flex-col h-full bg-m-slate-1 dark:bg-m-slate-12",
+ rx.el.elements.a(
+ get_icon(
+ "discord_navbar",
+ class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9",
+ ),
+ "Discord",
+ href=DISCORD_URL,
+ target="_blank",
+ class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group",
),
- class_name="w-[52.5rem] grid grid-cols-3",
+ class_name="flex flex-row items-center gap-8 px-6 py-3",
),
+ class_name="flex flex-col w-full shrink-0 bg-white-1 border-t border-secondary-4",
)
-def about_content() -> rx.Component:
- """About content.
+def resources_content() -> rx.Component:
+ """Mega-menu body for Resources: learn, agents, blog.
Returns:
The component.
@@ -516,26 +675,56 @@ def about_content() -> rx.Component:
return menu_content(
rx.el.div(
rx.el.div(
- solutions_item("Company", "Profile02Icon", "/about/"),
- solutions_item("Careers", "WorkIcon", JOBS_BOARD_URL),
- class_name="p-4 flex flex-col rounded-xl bg-white-1 h-full dark:shadow-none dark:border dark:border-m-slate-9 dark:bg-m-slate-11 shadow-card",
+ rx.el.div(
+ rx.el.div(
+ rx.el.div(
+ solutions_column(
+ "Learn",
+ [
+ ("Documentation", "File02Icon", "/docs"),
+ ("Templates", "Layout02Icon", "/templates"),
+ ("Changelog", "Clock02Icon", CHANGELOG_URL),
+ ],
+ ),
+ resources_agent_column(),
+ class_name="grid grid-cols-2 gap-8 min-w-0 p-5 bg-white-1 flex-1",
+ ),
+ resources_menu_footer(),
+ class_name="flex flex-col min-w-0 h-full",
+ ),
+ resources_blog_column(),
+ class_name="grid grid-cols-1 min-[480px]:grid-cols-[minmax(0,1fr)_min(17.5rem,40%)] h-full",
+ ),
+ class_name="flex flex-col w-[728px] max-w-full overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark",
),
- class_name="w-[12.5rem]",
+ class_name="p-0",
),
)
def navigation_menu() -> rx.Component:
- """Navigation menu.
+ """Desktop navigation: mega-menus, CTAs, and GitHub.
Returns:
The component.
"""
return ui.navigation_menu.root(
ui.navigation_menu.list(
- menu_trigger("Platform", platform_content()),
- menu_trigger("Solutions", solutions_content()),
+ menu_trigger("Products", products_content()),
menu_trigger("Resources", resources_content()),
+ menu_trigger("Solutions", solutions_content()),
+ ui.navigation_menu.item(
+ rx.el.elements.a(
+ marketing_button(
+ "Enterprise",
+ size="sm",
+ variant="ghost",
+ ),
+ href="/docs/enterprise/overview/",
+ ),
+ class_name="xl:flex hidden px-1",
+ custom_attrs={"role": "menuitem"},
+ ),
ui.navigation_menu.item(
rx.el.elements.a(
marketing_button(
@@ -561,7 +750,6 @@ def navigation_menu() -> rx.Component:
class_name="xl:flex hidden px-1",
custom_attrs={"role": "menuitem"},
),
- menu_trigger("About", about_content()),
class_name="flex flex-row items-center m-0 h-full list-none",
custom_attrs={"role": "menubar"},
),
@@ -615,13 +803,13 @@ def navigation_menu() -> rx.Component:
class_name="relative h-full w-full overflow-hidden rounded-[inherit]",
),
unstyled=True,
- class_name="relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] rounded-xl bg-m-slate-1 dark:bg-m-slate-12 navbar-shadow transition-[opacity,transform,width,height,scale,translate] duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] data-[ending-style]:ease-[ease] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:scale-90 data-[starting-style]:opacity-0",
+ class_name="relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] rounded-xl bg-secondary-1 navbar-shadow transition-[opacity,transform,width,height,scale,translate] duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] data-[ending-style]:ease-[ease] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:scale-90 data-[starting-style]:opacity-0",
),
unstyled=True,
class_name="safari-nav-positioner box-border h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none",
side_offset=30,
align="start",
- align_offset=-20,
+ align_offset=-109,
position_method="fixed",
),
),
@@ -632,7 +820,7 @@ def navigation_menu() -> rx.Component:
@rx.memo
def marketing_navbar() -> rx.Component:
- """Marketing navbar.
+ """Fixed header: hosting banner plus logo and full navigation.
Returns:
The component.
@@ -644,7 +832,7 @@ def marketing_navbar() -> rx.Component:
rx.el.header(
logo(),
navigation_menu(),
- class_name="w-full max-w-[71.5rem] h-[4.5rem] mx-auto flex flex-row items-center p-5 rounded-b-xl backdrop-blur-[16px] shadow-[0_-2px_2px_1px_rgba(0,0,0,0.02),0_1px_1px_0_rgba(0,0,0,0.08),0_4px_8px_0_rgba(0,0,0,0.03),0_0_0_1px_#FFF_inset] dark:shadow-none dark:border-x dark:border-b dark:border-m-slate-10 bg-gradient-to-b from-white to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12",
+ class_name="w-full max-w-[81rem] h-[4.5rem] mx-auto flex flex-row items-center p-5 rounded-b-xl backdrop-blur-[16px] shadow-[0_-2px_2px_1px_rgba(0,0,0,0.02),0_1px_1px_0_rgba(0,0,0,0.08),0_4px_8px_0_rgba(0,0,0,0.03),0_0_0_1px_#FFF_inset] dark:shadow-none dark:border-x dark:border-b dark:border-m-slate-10 bg-gradient-to-b from-white to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12",
),
class_name="flex flex-col w-full top-0 z-[9999] fixed self-center",
)
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py b/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py
index 3b78a151be1..22984658324 100644
--- a/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py
+++ b/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py
@@ -194,7 +194,7 @@ def navbar_sidebar_button() -> rx.Component:
},
),
size="icon-sm",
- variant="outline",
+ variant="outline-shadow",
custom_attrs={"aria-label": "Open sidebar"},
native_button=False,
),
diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py
new file mode 100644
index 00000000000..d5337f06de5
--- /dev/null
+++ b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py
@@ -0,0 +1,126 @@
+"""Workflow stage visuals (animated arrows) used in marketing mega-menus."""
+
+from typing import Literal
+from urllib.parse import quote
+
+import reflex_components_internal as ui
+
+import reflex as rx
+
+ArrowSize = Literal["large", "small"]
+
+_ARROW_VARIANTS: dict[ArrowSize, tuple[int, int]] = {
+ "large": (26, 136),
+ "small": (24, 126),
+}
+
+# Total animated groups; delay per group = duration / _ARROW_SWEEP_GROUPS so
+# the four sweeps chain end-to-end across one animation cycle.
+_ARROW_SWEEP_GROUPS = 4
+
+
+def _arrow_paths(count: int) -> str:
+ return "".join(
+ f''
+ for i in range(count)
+ )
+
+
+def _arrow_row_svg(count: int, width: int, *, inline_style: bool = False) -> str:
+ style_attr = ' style="display:block;height:100%;width:auto"' if inline_style else ""
+ return (
+ f'"
+ )
+
+
+def _arrow_mask_data_url(count: int, width: int) -> str:
+ # Percent-encode the SVG body so Firefox/Safari accept the data URL
+ # in mask-image; raw `<`, `>`, and spaces are not reliably parsed.
+ encoded = quote(_arrow_row_svg(count, width), safe="")
+ return f'url("data:image/svg+xml,{encoded}")'
+
+
+def workflow_stage_image(
+ wrapper_class_name: str = "",
+ size: ArrowSize = "large",
+ sweep_index: int = 0,
+) -> rx.Component:
+ """Animated arrow strip with a phased sweep highlight.
+
+ Returns:
+ The component.
+ """
+ count, width = _ARROW_VARIANTS[size]
+ mask_url = _arrow_mask_data_url(count, width)
+
+ return rx.el.div(
+ rx.html(
+ _arrow_row_svg(count, width, inline_style=True),
+ class_name="block h-full w-auto text-secondary-7",
+ ),
+ rx.el.div(
+ rx.el.div(
+ class_name="absolute -top-[7px] left-0 h-[24px] w-1/2 bg-primary-9 blur-[8px] animate-arrow-sweep",
+ style={
+ "animationDelay": (
+ f"calc(var(--arrow-sweep-duration) / {_ARROW_SWEEP_GROUPS} "
+ f"* {sweep_index})"
+ ),
+ },
+ ),
+ class_name=ui.cn(
+ "absolute inset-0 pointer-events-none overflow-hidden",
+ "[mask-repeat:no-repeat] [-webkit-mask-repeat:no-repeat]",
+ "[mask-size:100%_100%] [-webkit-mask-size:100%_100%]",
+ "[mask-position:left_center] [-webkit-mask-position:left_center]",
+ ),
+ style={
+ "maskImage": mask_url,
+ "WebkitMaskImage": mask_url,
+ },
+ ),
+ class_name=ui.cn(
+ "relative flex h-[10px] w-auto shrink-0 items-center justify-start overflow-hidden max-lg:hidden",
+ wrapper_class_name,
+ ),
+ custom_attrs={"aria-hidden": "true"},
+ )
+
+
+def workflow_stage_row(
+ title: str,
+ *,
+ left: rx.Component | None = None,
+ right: rx.Component | None = None,
+) -> rx.Component:
+ """Three-column row: optional left graphic, centered title, optional right graphic.
+
+ Returns:
+ The component.
+ """
+ left_cell = (
+ rx.el.div(
+ left,
+ class_name="pointer-events-none flex min-w-0 items-center justify-self-start px-2",
+ )
+ if left is not None
+ else rx.el.div(class_name="min-w-0 justify-self-start")
+ )
+ right_cell = (
+ rx.el.div(
+ right,
+ class_name="pointer-events-none flex min-w-0 items-center justify-self-end px-2",
+ )
+ if right is not None
+ else rx.el.div(class_name="min-w-0 justify-self-end")
+ )
+ return rx.el.div(
+ left_cell,
+ rx.el.span(title, class_name="shrink-0 justify-self-center text-center"),
+ right_cell,
+ class_name="grid h-12 w-full shrink-0 grid-cols-[1fr_auto_1fr] items-center bg-secondary-1 font-mono text-xs font-[415] uppercase text-secondary-12 lg:border-b border-secondary-4",
+ )