From 31ad29f03d9cabf7da4fe201eb9c10ad38ac160c Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 25 Feb 2026 11:02:54 +0100 Subject: [PATCH 1/5] ENG-8904: Update footer --- assets/tailwind-theme.css | 4 +- pcweb/components/icons/icons.py | 32 +++ pcweb/pages/about/__init__.py | 1 - pcweb/pages/customers/views/footer.py | 181 -------------- pcweb/pages/docs_landing/__init__.py | 3 +- pcweb/pages/framework/views/divider.py | 17 ++ pcweb/pages/framework/views/footer_index.py | 245 +++++++++++-------- pcweb/pages/framework/views/os_newsletter.py | 2 +- pcweb/pages/pricing/faq.py | 2 +- pcweb/pages/pricing/pricing.py | 2 + pcweb/templates/gallery_app_page.py | 11 +- pcweb/templates/highlightpage.py | 13 +- pcweb/templates/mainpage.py | 4 +- pcweb/templates/marketing_page.py | 6 +- pcweb/templates/storypage.py | 10 +- pcweb/templates/webpage.py | 6 +- pcweb/views/bottom_section/newsletter.py | 2 +- pcweb/views/footer.py | 2 +- 18 files changed, 228 insertions(+), 315 deletions(-) delete mode 100644 pcweb/pages/customers/views/footer.py create mode 100644 pcweb/pages/framework/views/divider.py diff --git a/assets/tailwind-theme.css b/assets/tailwind-theme.css index b4a18b2c6..a22af658b 100644 --- a/assets/tailwind-theme.css +++ b/assets/tailwind-theme.css @@ -1120,8 +1120,8 @@ 0 1px 1px 0 rgba(0, 0, 0, 0.01), 0 0 0 1px #FFF inset; --shadow-card-dark: 0 0 0 1px var(--m-slate-9, #2A3037); - --text-xs: 0.75rem; - --text-xs--line-height: 1rem; + --text-xs: 0.8125rem; + --text-xs--line-height: 1.25rem; --text-sm: 0.875rem; --text-sm--line-height: 1.5rem; --text-base: 1rem; diff --git a/pcweb/components/icons/icons.py b/pcweb/components/icons/icons.py index 588542978..5743424ad 100644 --- a/pcweb/components/icons/icons.py +++ b/pcweb/components/icons/icons.py @@ -540,6 +540,32 @@ markdown = """""" +twitter_footer = """ + +""" + +linkedin_footer = """ + +""" + +forum_footer = """ + + +""" + +moon_footer = """ + +""" + +sun_footer = """ + + +""" + +computer_footer = """ + + +""" ICONS = { # Socials "github": github, @@ -624,6 +650,12 @@ "link_blog": link_blog, "reddit_blog": reddit_blog, "markdown": markdown, + "twitter_footer": twitter_footer, + "linkedin_footer": linkedin_footer, + "forum_footer": forum_footer, + "moon_footer": moon_footer, + "sun_footer": sun_footer, + "computer_footer": computer_footer, } diff --git a/pcweb/pages/about/__init__.py b/pcweb/pages/about/__init__.py index fbaeb814e..ea145654c 100644 --- a/pcweb/pages/about/__init__.py +++ b/pcweb/pages/about/__init__.py @@ -37,7 +37,6 @@ def about() -> rx.Component: hiring(), team(), news(), - divider(), footer_index(), class_name="flex flex-col relative justify-center items-center w-full", ), diff --git a/pcweb/pages/customers/views/footer.py b/pcweb/pages/customers/views/footer.py deleted file mode 100644 index c8c35bf9d..000000000 --- a/pcweb/pages/customers/views/footer.py +++ /dev/null @@ -1,181 +0,0 @@ -from datetime import datetime - -import reflex as rx - -from pcweb.components.button import button -from pcweb.components.icons import get_icon -from pcweb.constants import ( - DISCORD_URL, - FORUM_URL, - GITHUB_URL, - LINKEDIN_URL, - ROADMAP_URL, - TWITTER_URL, -) -from pcweb.pages.blog import blogs -from pcweb.pages.docs import getting_started, hosting -from pcweb.pages.docs.library import library -from pcweb.pages.errors import errors -from pcweb.pages.faq import faq -from pcweb.pages.framework.views.footer_index import dark_mode_toggle -from pcweb.pages.gallery import gallery -from pcweb.pages.use_cases.use_cases import use_cases_page -from pcweb.signup import IndexState -from pcweb.views.footer import ph_1 - - -def footer_link(text: str, href: str) -> rx.Component: - return rx.link( - text, - href=href, - class_name="font-small text-slate-9 hover:!text-slate-11 no-underline transition-color w-fit", - ) - - -def footer_link_flex( - heading: str, links: list[rx.Component], class_name: str = "" -) -> rx.Component: - return rx.box( - rx.el.h4( - heading, - class_name="font-smbold text-slate-12 w-fit", - ), - *links, - class_name="flex flex-col gap-4 p-10" + " " + class_name, - ) - - -def social_menu_item(icon: str, url: str) -> rx.Component: - return rx.link( - get_icon(icon, class_name="!text-slate-9 shrink-0"), - class_name="flex size-8 border border-slate-5 rounded-lg hover:bg-slate-3 transition-bg bg-slate-1 px-3 py-1.5 justify-center items-center border-solid", - href=url, - is_external=True, - ) - - -def menu_socials() -> rx.Component: - return rx.box( - social_menu_item("discord_navbar", DISCORD_URL), - social_menu_item("forum", FORUM_URL), - social_menu_item("twitter", TWITTER_URL), - social_menu_item("github_navbar", GITHUB_URL), - social_menu_item("linkedin", LINKEDIN_URL), - class_name="flex flex-row h-full align-center gap-2", - ) - - -def newletter_input() -> rx.Component: - return rx.box( - rx.cond( - IndexState.signed_up, - rx.box( - rx.box( - rx.icon( - tag="circle-check", - size=16, - class_name="!text-violet-9", - ), - rx.text( - "Thanks for subscribing!", - class_name="font-base text-slate-11", - ), - class_name="flex flex-row items-center gap-2", - ), - button( - "Sign up for another email", - variant="muted", - on_click=IndexState.signup_for_another_user, - ), - class_name="flex flex-col flex-wrap gap-2", - ), - rx.form( - rx.el.input( - placeholder="Your email", - name="input_email", - type="email", - class_name="relative box-border border-slate-4 focus:border-violet-9 focus:border-1 bg-slate-2 p-[0.5rem_0.75rem] border rounded-lg font-small text-slate-11 placeholder:text-slate-9 outline-none focus:outline-none w-full h-8", - ), - button( - "Subscribe", - type="submit", - variant="muted", - class_name="!h-8 !rounded-lg !py-2 !px-3.5 !font-small-smbold", - ), - class_name="w-full flex flex-row gap-2 items-center", - on_submit=IndexState.signup, - ), - ) - ) - - -def news_letter() -> rx.Component: - return ( - rx.box( - rx.text( - "Get updates", - class_name="font-small text-slate-9", - ), - newletter_input(), - class_name="flex flex-col items-start gap-4 self-stretch p-10", - ), - ) - - -@rx.memo -def footer_customer() -> rx.Component: - return rx.el.footer( - rx.box( - footer_link_flex( - "Reflex", - [ - footer_link("Home", "/"), - footer_link("Templates", gallery.path), - footer_link("Blog", blogs.path), - footer_link( - "Changelog", "https://github.com/reflex-dev/reflex/releases" - ), - ], - class_name="!row-span-2", - ), - footer_link_flex( - "Documentation", - [ - footer_link("Introduction", getting_started.introduction.path), - footer_link("Installation", getting_started.installation.path), - footer_link("Components", library.path), - footer_link("Hosting", hosting.deploy_quick_start.path), - ], - class_name="!border-t-0 !row-span-2", - ), - footer_link_flex( - "Resources", - [ - footer_link("FAQ", faq.path), - footer_link("Common Errors", errors.path), - footer_link("Roadmap", ROADMAP_URL), - footer_link("Forum", FORUM_URL), - footer_link("Use Cases", use_cases_page.path), - rx.box(class_name="grow"), - rx.el.div( - ph_1(), - dark_mode_toggle(), - class_name="flex flex-row items-center gap-6", - ), - ], - class_name="!row-span-3 !border-t-0", - ), - # Socials - rx.box( - rx.text( - f"Copyright © {datetime.now().year} Pynecone, Inc.", - class_name="font-small text-slate-9", - ), - menu_socials(), - class_name="flex flex-col items-start gap-4 self-stretch p-10 !border-l-0", - ), - news_letter(), - class_name="grid grid-cols-1 lg:grid-cols-3 gap-0 grid-rows-2 w-full lg:divide-y divide-slate-3 lg:divide-x border-t border-slate-3", - ), - class_name="flex max-w-[64.19rem] justify-center items-center w-full mx-auto", - ) diff --git a/pcweb/pages/docs_landing/__init__.py b/pcweb/pages/docs_landing/__init__.py index b3e8d2950..38a8a2d17 100644 --- a/pcweb/pages/docs_landing/__init__.py +++ b/pcweb/pages/docs_landing/__init__.py @@ -3,7 +3,6 @@ from pcweb.meta.meta import create_meta_tags from pcweb.pages.docs_landing.views import ( ai_builder_section, - divider, enterprise_section, framework, hero, @@ -11,6 +10,7 @@ other_section, self_hosting_section, ) +from pcweb.pages.framework.views.divider import divider from pcweb.pages.framework.views.footer_index import footer_index from pcweb.views.docs_navbar import docs_navbar @@ -37,7 +37,6 @@ def docs_landing() -> rx.Component: hosting_section(), self_hosting_section(), other_section(), - divider(), footer_index(), class_name="flex flex-col relative justify-center items-center w-full overflow-hidden", ), diff --git a/pcweb/pages/framework/views/divider.py b/pcweb/pages/framework/views/divider.py new file mode 100644 index 000000000..f092fc069 --- /dev/null +++ b/pcweb/pages/framework/views/divider.py @@ -0,0 +1,17 @@ +import reflex as rx +import reflex_ui as ui + + +def divider(class_name: str = "") -> rx.Component: + return rx.el.div( + rx.el.div( + class_name="absolute top-0 -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10" + ), + rx.el.div( + 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", + class_name, + ), + ) diff --git a/pcweb/pages/framework/views/footer_index.py b/pcweb/pages/framework/views/footer_index.py index ddc411d55..eff82824f 100644 --- a/pcweb/pages/framework/views/footer_index.py +++ b/pcweb/pages/framework/views/footer_index.py @@ -4,91 +4,137 @@ import reflex_ui as ui from reflex.style import color_mode, set_color_mode -from pcweb.components.button import button from pcweb.components.icons import get_icon +from pcweb.components.marketing_button import button as marketing_button from pcweb.constants import ( + CHANGELOG_URL, DISCORD_URL, FORUM_URL, GITHUB_URL, LINKEDIN_URL, + REFLEX_BUILD_URL, ROADMAP_URL, TWITTER_URL, ) -from pcweb.pages.blog import blogs -from pcweb.pages.docs import getting_started, hosting -from pcweb.pages.docs.library import library from pcweb.pages.errors import errors from pcweb.pages.faq import faq -from pcweb.pages.gallery import gallery +from pcweb.pages.framework.framework import framework +from pcweb.pages.hosting.hosting import hosting_landing +from pcweb.pages.use_cases.consulting import consulting_use_case_page +from pcweb.pages.use_cases.finance import finance_use_case_page +from pcweb.pages.use_cases.government import government_use_case_page +from pcweb.pages.use_cases.healthcare import healthcare_use_case_page +from pcweb.pages.use_cases.use_cases import use_cases_page from pcweb.signup import IndexState -from pcweb.views.footer import ph_1 + + +def ph_1() -> rx.Component: + return rx.fragment( + rx.image( + src="/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="/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: + return rx.el.a( + rx.image( + src="/logos/light/reflex.svg", + alt="Reflex Logo", + class_name="shrink-0 block dark:hidden", + ), + rx.image( + src="/logos/dark/reflex.svg", + alt="Reflex Logo", + class_name="shrink-0 hidden dark:block", + ), + to="/", + class_name="block shrink-0 mr-[7rem]", + ) def tab_item(mode: str, icon: str) -> rx.Component: - active_cn = " text-secondary-11 shadow-small bg-slate-1" - unactive_cn = " hover:text-slate-12 text-secondary-11" + 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" return rx.el.button( - ui.icon(icon, class_name="shrink-0"), + get_icon(icon, class_name="shrink-0"), on_click=set_color_mode(mode), - class_name="flex items-center cursor-pointer justify-center rounded-md transition-color size-7 outline-none focus:outline-none " - + rx.cond(mode == color_mode, active_cn, unactive_cn), + class_name=ui.cn( + "flex items-center cursor-pointer justify-center rounded-lg transition-colors size-7 outline-none focus:outline-none ", + rx.cond(mode == color_mode, active_cn, unactive_cn), + ), custom_attrs={"aria-label": f"Toggle {mode} color mode"}, ) def dark_mode_toggle() -> rx.Component: return rx.box( - tab_item("system", "ComputerIcon"), - tab_item("light", "Sun01Icon"), - tab_item("dark", "Moon02Icon"), - class_name="flex flex-row items-center bg-slate-3 p-1 rounded-[0.625rem] w-fit", + 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-10 border border-transparent lg:ml-auto", ) def footer_link(text: str, href: str) -> rx.Component: - return rx.link( + return rx.el.a( text, rx.icon( tag="chevron-right", size=16, class_name="shrink-0 lg:hidden flex", ), - href=href, - class_name="font-small text-secondary-11 hover:!text-slate-12 no-underline transition-color w-full lg:w-fit flex flex-row justify-between items-center", + to=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", ) def footer_link_flex( heading: str, links: list[rx.Component], class_name: str = "" ) -> rx.Component: - return rx.box( + return rx.el.div( rx.el.h3( heading, - class_name="font-smbold text-slate-12 w-fit", + class_name="text-xs text-m-slate-12 dark:text-m-slate-3 font-semibold w-fit mb-3", ), *links, - class_name="flex flex-col gap-4 px-8 py-10 lg:p-10" + " " + class_name, + class_name=ui.cn("flex flex-col gap-2", class_name), ) def social_menu_item(icon: str, url: str, name: str) -> rx.Component: - return rx.link( - get_icon(icon, class_name="!text-secondary-11 shrink-0"), - class_name="flex w-full h-8 lg:size-8 border border-slate-5 rounded-lg hover:bg-slate-3 transition-bg bg-slate-1 px-3 py-1.5 justify-center items-center border-solid", - href=url, + return rx.el.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, + ), + to=url, custom_attrs={"aria-label": "Social link for " + name}, - is_external=True, + target="_blank", ) def menu_socials() -> rx.Component: return rx.box( - social_menu_item("discord_navbar", DISCORD_URL, "Discord"), - social_menu_item("forum", FORUM_URL, "Forum"), - social_menu_item("twitter", TWITTER_URL, "Twitter"), + social_menu_item("twitter_footer", TWITTER_URL, "Twitter"), social_menu_item("github_navbar", GITHUB_URL, "Github"), - social_menu_item("linkedin", LINKEDIN_URL, "LinkedIn"), - class_name="flex flex-row h-full align-center gap-2 w-full lg:w-fit max-w-[24rem]", + 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", ) @@ -105,31 +151,34 @@ def newletter_input() -> rx.Component: ), rx.text( "Thanks for subscribing!", - class_name="font-base text-slate-11", + class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-semibold", ), class_name="flex flex-row items-center gap-2", ), - button( + marketing_button( "Sign up for another email", - variant="muted", + variant="outline", + size="sm", on_click=IndexState.signup_for_another_user, ), class_name="flex flex-col flex-wrap gap-2", ), rx.form( rx.el.input( - placeholder="Your email", + placeholder="Email", name="input_email", type="email", - class_name="relative box-border border-slate-4 focus:border-violet-9 focus:border-1 bg-slate-2 p-[0.5rem_0.75rem] border rounded-lg font-small text-slate-11 placeholder:text-slate-9 outline-none focus:outline-none w-full h-8 max-w-[24rem]", + 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-10", ), - button( + marketing_button( "Subscribe", type="submit", - variant="muted", - class_name="!h-8 !rounded-lg !py-2 !px-3.5 !font-small-smbold w-full lg:w-auto lg:max-w-full max-w-[24rem] !text-secondary-11", + variant="outline", + size="sm", + class_name="w-fit max-w-full", ), - class_name="w-full flex flex-col lg:flex-row gap-2 items-center", + class_name="w-full flex flex-col lg:flex-row gap-2 lg:items-center items-start", on_submit=IndexState.signup, ), ), @@ -138,72 +187,76 @@ def newletter_input() -> rx.Component: def newsletter() -> rx.Component: - return ( - rx.box( - rx.text( - "Get updates", - class_name="font-small text-secondary-11", - ), - newletter_input(), - class_name="flex flex-col items-center lg:items-start gap-4 self-stretch p-10 lg:border-r border-slate-3", + return rx.el.div( + rx.text( + "Get updates", + class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-semibold", ), + newletter_input(), + class_name="flex flex-col items-start gap-4 self-stretch", ) @rx.memo def footer_index() -> rx.Component: return rx.el.footer( - rx.box( - footer_link_flex( - "Reflex", - [ - footer_link("Home", "/"), - footer_link("Templates", gallery.path), - footer_link("Blog", blogs.path), - footer_link( - "Changelog", "https://github.com/reflex-dev/reflex/releases" - ), - ], - class_name="lg:!border-l !border-slate-3 !row-span-2", + rx.el.div( + logo(), + rx.el.div( + footer_link_flex( + "Product", + [ + footer_link("AI Builder", REFLEX_BUILD_URL), + footer_link("Framework", framework.path), + footer_link("Cloud", hosting_landing.path), + ], + ), + footer_link_flex( + "Solutions", + [ + footer_link("Enterprise", use_cases_page.path), + footer_link("Finance", finance_use_case_page.path), + footer_link("Healthcare", healthcare_use_case_page.path), + footer_link("Consulting", consulting_use_case_page.path), + footer_link("Government", government_use_case_page.path), + ], + ), + footer_link_flex( + "Resources", + [ + footer_link("Documentation", "/docs"), + footer_link("FAQ", faq.path), + footer_link("Common Errors", errors.path), + 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" + ), + class_name="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 ml-auto relative", ), - footer_link_flex( - "Documentation", - [ - footer_link("Introduction", getting_started.introduction.path), - footer_link("Installation", getting_started.installation.path), - footer_link("Components", library.path), - footer_link("Hosting", hosting.deploy_quick_start.path), - ], - class_name="lg:!border-t-0 !row-span-2", + 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", ), - footer_link_flex( - "Resources", - [ - footer_link("FAQ", faq.path), - footer_link("Common Errors", errors.path), - footer_link("Roadmap", ROADMAP_URL), - footer_link("Forum", FORUM_URL), - footer_link("Use Cases", "/use-cases"), - rx.box(class_name="grow"), - rx.el.div( - ph_1(), - dark_mode_toggle(), - class_name="flex flex-row items-center gap-6", - ), - ], - class_name="!row-span-3 lg:!border-t-0 lg:!border-r !border-slate-3", + class_name="flex flex-col max-lg:gap-6 lg:flex-row w-full", + ), + 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", ), - # Socials - rx.box( - rx.text( - f"Copyright © {datetime.now().year} Pynecone, Inc.", - class_name="font-small text-secondary-11", - ), - menu_socials(), - class_name="flex flex-col items-center lg:items-start gap-4 self-stretch p-10 lg:border-l border-slate-3", + menu_socials(), + 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" ), - newsletter(), - class_name="grid grid-cols-1 lg:grid-cols-3 gap-0 grid-rows-2 w-full divide-y divide-slate-3 lg:divide-x border-t border-slate-3 lg:border-t-0", + 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="flex max-w-[64.19rem] justify-center items-center w-full mx-auto", + class_name="flex flex-col max-w-(--docs-layout-max-width) justify-center items-center w-full mx-auto mt-24", ) diff --git a/pcweb/pages/framework/views/os_newsletter.py b/pcweb/pages/framework/views/os_newsletter.py index 25796d7a9..ad4c9afb5 100644 --- a/pcweb/pages/framework/views/os_newsletter.py +++ b/pcweb/pages/framework/views/os_newsletter.py @@ -75,7 +75,7 @@ def newletter_input() -> rx.Component: class_name="lg:flex hidden shrink-0 absolute -translate-y-1/2 left-[-2.5rem] top-1/2 h-[5.5625rem] w-[25.1875rem] z-[-1]", ), rx.el.input( - placeholder="Your email", + placeholder="Email", name="input_email", type="email", class_name="relative box-border border-slate-4 focus:border-violet-9 focus:border-1 bg-slate-2 p-[0.5rem_0.75rem] border rounded-xl font-base text-slate-11 placeholder:text-slate-9 outline-none focus:outline-none w-full max-w-[24rem]", diff --git a/pcweb/pages/pricing/faq.py b/pcweb/pages/pricing/faq.py index b537440d1..94897142e 100644 --- a/pcweb/pages/pricing/faq.py +++ b/pcweb/pages/pricing/faq.py @@ -137,5 +137,5 @@ def faq() -> rx.Component: class_name="max-w-[40rem] flex justify-center items-center flex-col mx-auto w-full gap-2", ), sales_button(), - class_name="flex flex-col gap-8 w-full max-w-[64.19rem] 2xl:border-x border-slate-4 2xl:border-b pb-[6rem]", + class_name="flex flex-col gap-8 w-full max-w-[64.19rem] 2xl:border-x border-slate-4 pb-[6rem]", ) diff --git a/pcweb/pages/pricing/pricing.py b/pcweb/pages/pricing/pricing.py index 660cf2fa5..f19af2080 100644 --- a/pcweb/pages/pricing/pricing.py +++ b/pcweb/pages/pricing/pricing.py @@ -2,6 +2,7 @@ from pcweb.meta.meta import hosting_meta_tags from pcweb.pages.framework.index_colors import index_colors +from pcweb.pages.framework.views.divider import divider from pcweb.pages.framework.views.footer_index import footer_index from pcweb.pages.pricing.calculator import calculator_section from pcweb.pages.pricing.faq import faq @@ -33,6 +34,7 @@ def pricing() -> rx.Component: ), class_name="flex flex-col w-full relative h-full justify-center items-center", ), + divider(), footer_index(), class_name="flex flex-col w-full max-w-[94.5rem] justify-center items-center mx-auto px-4 lg:px-5 relative", ) diff --git a/pcweb/templates/gallery_app_page.py b/pcweb/templates/gallery_app_page.py index 551397f6c..1cb4f4b0b 100644 --- a/pcweb/templates/gallery_app_page.py +++ b/pcweb/templates/gallery_app_page.py @@ -58,8 +58,8 @@ def wrapper(*children, **props) -> rx.Component: The component with the template applied. """ # Import here to avoid circular imports. + from pcweb.pages.framework.views.divider import divider from pcweb.pages.framework.views.footer_index import footer_index - from pcweb.views.bottom_section.bottom_section import bottom_section from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -71,14 +71,11 @@ def wrapper(*children, **props) -> rx.Component: contents(*children, **props), class_name="w-full z-[1] relative flex flex-col mx-auto lg:border-x border-slate-3 pt-24 lg:pt-48", ), - rx.box( - bottom_section(), - class_name="border-t border-slate-3 border-x flex flex-col items-center w-full border-b", - ), - footer_index(), class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-instrument-sans mx-auto max-w-[64.19rem]", ), - class_name="relative overflow-hidden", + divider(), + footer_index(), + class_name="relative overflow-hidden flex flex-col justify-center items-center w-full", **props, ) diff --git a/pcweb/templates/highlightpage.py b/pcweb/templates/highlightpage.py index f0ffcaca5..f225137bc 100644 --- a/pcweb/templates/highlightpage.py +++ b/pcweb/templates/highlightpage.py @@ -55,9 +55,9 @@ def wrapper(*children, **props) -> rx.Component: The component with the template applied. """ # Import here to avoid circular imports. - from pcweb.pages.customers.views.footer import footer_customer from pcweb.pages.framework.index_colors import index_colors - from pcweb.views.bottom_section.bottom_section import bottom_section + from pcweb.pages.framework.views.divider import divider + from pcweb.pages.framework.views.footer_index import footer_index from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -70,12 +70,11 @@ def wrapper(*children, **props) -> rx.Component: rx.box(class_name="flex-grow"), class_name="w-full z-[1] relative flex flex-col justify-center mx-auto max-w-[640px] lg:px-0 px-4 pb-20", ), - rx.box(class_name="h-[1px] bg-slate-3 w-full"), - bottom_section(), - footer_customer(), - class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-instrument-sans gap-4 mx-auto max-w-[64.19rem] lg:border-x border-slate-3 pt-24 lg:pt-48", + class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-sans gap-4 mx-auto max-w-[64.19rem] lg:border-x border-slate-3 pt-24 lg:pt-48", ), - class_name="relative overflow-hidden", + divider(), + footer_index(), + class_name="relative overflow-hidden flex flex-col justify-center items-center w-full", ) return Route( diff --git a/pcweb/templates/mainpage.py b/pcweb/templates/mainpage.py index ca078419f..49e1bd3cb 100644 --- a/pcweb/templates/mainpage.py +++ b/pcweb/templates/mainpage.py @@ -59,8 +59,8 @@ def wrapper(*children, **props) -> rx.Component: # Import here to avoid circular imports. from pcweb.components.hosting_banner import HostingBannerState from pcweb.pages.framework.index_colors import index_colors + from pcweb.pages.framework.views.divider import divider from pcweb.pages.framework.views.footer_index import footer_index - from pcweb.pages.framework.views.get_started import get_started from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -69,7 +69,7 @@ def wrapper(*children, **props) -> rx.Component: marketing_navbar(), rx.el.main( contents(*children, **props), - get_started(), + divider(), ), footer_index(), class_name="flex flex-col w-full max-w-[94.5rem] justify-center mx-auto px-4 lg:px-5 relative overflow-hidden", diff --git a/pcweb/templates/marketing_page.py b/pcweb/templates/marketing_page.py index 3df41a651..1c83c87fd 100644 --- a/pcweb/templates/marketing_page.py +++ b/pcweb/templates/marketing_page.py @@ -58,8 +58,7 @@ def wrapper(*children, **props) -> rx.Component: The component with the template applied. """ # Import here to avoid circular imports. - from pcweb.views.bottom_section.bottom_section import bottom_section - from pcweb.views.footer import footer + from pcweb.pages.framework.views.footer_index import footer_index from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -68,8 +67,7 @@ def wrapper(*children, **props) -> rx.Component: rx.el.main( rx.el.div( contents(*children, **props), - bottom_section(), - footer(), + footer_index(), class_name="flex flex-col relative justify-center items-center w-full", ), class_name="flex flex-col w-full relative h-full justify-center items-center", diff --git a/pcweb/templates/storypage.py b/pcweb/templates/storypage.py index 431a5caf6..e03238e63 100644 --- a/pcweb/templates/storypage.py +++ b/pcweb/templates/storypage.py @@ -252,8 +252,8 @@ def wrapper(*children, **props) -> rx.Component: The component with the template applied. """ # Import here to avoid circular imports. - from pcweb.pages.customers.views.footer import footer_customer - from pcweb.views.bottom_section.bottom_section import bottom_section + from pcweb.pages.framework.views.divider import divider + from pcweb.pages.framework.views.footer_index import footer_index from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -270,11 +270,11 @@ def wrapper(*children, **props) -> rx.Component: class_name="w-full z-[1] relative flex flex-col justify-center mx-auto max-w-[640px] lg:px-0 px-4 pb-20", ), rx.box(class_name="h-[1px] bg-slate-3 w-full"), - bottom_section(), - footer_customer(), class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-instrument-sans gap-4 mx-auto max-w-[64.19rem] lg:border-x border-slate-3 pt-24 lg:pt-48", ), - class_name="relative overflow-hidden", + divider(), + footer_index(), + class_name="relative overflow-hidden flex flex-col justify-center items-center w-full", **props, ) diff --git a/pcweb/templates/webpage.py b/pcweb/templates/webpage.py index bc6fcd713..73aaf7849 100644 --- a/pcweb/templates/webpage.py +++ b/pcweb/templates/webpage.py @@ -59,8 +59,7 @@ def wrapper(*children, **props) -> rx.Component: """ # Import here to avoid circular imports. from pcweb.components.icons.patterns import default_patterns - from pcweb.views.bottom_section.bottom_section import bottom_section - from pcweb.views.footer import footer + from pcweb.pages.framework.views.footer_index import footer_index from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -72,8 +71,7 @@ def wrapper(*children, **props) -> rx.Component: rx.box(class_name="flex-grow"), class_name="w-full z-[1]", ), - bottom_section(), - footer(), + footer_index(), class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-instrument-sans overflow-hidden", **props, ) diff --git a/pcweb/views/bottom_section/newsletter.py b/pcweb/views/bottom_section/newsletter.py index e595fcfe2..bc5ee12b1 100644 --- a/pcweb/views/bottom_section/newsletter.py +++ b/pcweb/views/bottom_section/newsletter.py @@ -45,7 +45,7 @@ def newletter_input() -> rx.Component: class_name="shrink-0 absolute -translate-y-1/2 left-[-3rem] top-1/2 h-[5.5625rem] w-[35.1875rem] z-[-1]", ), rx.el.input( - placeholder="Your email", + placeholder="Email", name="input_email", type="email", class_name="relative box-border border-slate-4 focus:border-violet-9 focus:border-1 bg-slate-2 p-[0.5rem_0.75rem] border rounded-xl font-base text-slate-11 placeholder:text-slate-9 outline-none focus:outline-none w-full", diff --git a/pcweb/views/footer.py b/pcweb/views/footer.py index e9b296dc7..8d925eb8c 100644 --- a/pcweb/views/footer.py +++ b/pcweb/views/footer.py @@ -114,7 +114,7 @@ def newsletter_form() -> rx.Component: rx.form( rx.box( rx.el.input( - placeholder="Your email", + placeholder="Email", name="input_email", type="email", class_name="box-border border-slate-5 focus:border-violet-9 focus:border-1 bg-white-1 p-[0.5rem_0.75rem] border rounded-[10px] font-small text-slate-11 placeholder:text-slate-9 outline-none focus:outline-none", From 777b153b9f3df1de62a8f8cefe9e9b143d94c819 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Thu, 26 Feb 2026 22:12:14 +0100 Subject: [PATCH 2/5] add cta card --- assets/common/dark/cta.svg | 358 ++++++++++++++++++++ assets/common/light/cta.svg | 358 ++++++++++++++++++++ pcweb/pages/about/__init__.py | 2 + pcweb/pages/docs_landing/__init__.py | 4 +- pcweb/pages/framework/views/footer_index.py | 2 +- pcweb/templates/marketing_page.py | 2 + pcweb/templates/webpage.py | 2 + pcweb/views/cta_card.py | 50 +++ pcweb/views/footer.py | 2 +- 9 files changed, 777 insertions(+), 3 deletions(-) create mode 100644 assets/common/dark/cta.svg create mode 100644 assets/common/light/cta.svg create mode 100644 pcweb/views/cta_card.py diff --git a/assets/common/dark/cta.svg b/assets/common/dark/cta.svg new file mode 100644 index 000000000..65369631e --- /dev/null +++ b/assets/common/dark/cta.svg @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/common/light/cta.svg b/assets/common/light/cta.svg new file mode 100644 index 000000000..65369631e --- /dev/null +++ b/assets/common/light/cta.svg @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcweb/pages/about/__init__.py b/pcweb/pages/about/__init__.py index ea145654c..49d8e001e 100644 --- a/pcweb/pages/about/__init__.py +++ b/pcweb/pages/about/__init__.py @@ -12,6 +12,7 @@ team, ) from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.views.cta_card import cta_card from pcweb.views.marketing_navbar import marketing_navbar @@ -37,6 +38,7 @@ def about() -> rx.Component: hiring(), team(), news(), + cta_card(), footer_index(), class_name="flex flex-col relative justify-center items-center w-full", ), diff --git a/pcweb/pages/docs_landing/__init__.py b/pcweb/pages/docs_landing/__init__.py index 38a8a2d17..e8d96ef28 100644 --- a/pcweb/pages/docs_landing/__init__.py +++ b/pcweb/pages/docs_landing/__init__.py @@ -12,6 +12,7 @@ ) from pcweb.pages.framework.views.divider import divider from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.views.cta_card import cta_card from pcweb.views.docs_navbar import docs_navbar @@ -30,13 +31,14 @@ def docs_landing() -> rx.Component: rx.el.main( rx.el.div( hero(), - divider(), + divider(class_name="max-w-full"), ai_builder_section(), framework(), enterprise_section(), hosting_section(), self_hosting_section(), other_section(), + cta_card(), footer_index(), class_name="flex flex-col relative justify-center items-center w-full overflow-hidden", ), diff --git a/pcweb/pages/framework/views/footer_index.py b/pcweb/pages/framework/views/footer_index.py index eff82824f..6a6089437 100644 --- a/pcweb/pages/framework/views/footer_index.py +++ b/pcweb/pages/framework/views/footer_index.py @@ -258,5 +258,5 @@ def footer_index() -> rx.Component: ), 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="flex flex-col max-w-(--docs-layout-max-width) justify-center items-center w-full mx-auto mt-24", + class_name="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", ) diff --git a/pcweb/templates/marketing_page.py b/pcweb/templates/marketing_page.py index 1c83c87fd..4cd7080c4 100644 --- a/pcweb/templates/marketing_page.py +++ b/pcweb/templates/marketing_page.py @@ -59,6 +59,7 @@ def wrapper(*children, **props) -> rx.Component: """ # Import here to avoid circular imports. from pcweb.pages.framework.views.footer_index import footer_index + from pcweb.views.cta_card import cta_card from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -67,6 +68,7 @@ def wrapper(*children, **props) -> rx.Component: rx.el.main( rx.el.div( contents(*children, **props), + cta_card(), footer_index(), class_name="flex flex-col relative justify-center items-center w-full", ), diff --git a/pcweb/templates/webpage.py b/pcweb/templates/webpage.py index 73aaf7849..43c19900b 100644 --- a/pcweb/templates/webpage.py +++ b/pcweb/templates/webpage.py @@ -60,6 +60,7 @@ def wrapper(*children, **props) -> rx.Component: # Import here to avoid circular imports. from pcweb.components.icons.patterns import default_patterns from pcweb.pages.framework.views.footer_index import footer_index + from pcweb.views.cta_card import cta_card from pcweb.views.marketing_navbar import marketing_navbar # Wrap the component in the template. @@ -71,6 +72,7 @@ def wrapper(*children, **props) -> rx.Component: rx.box(class_name="flex-grow"), class_name="w-full z-[1]", ), + cta_card(), footer_index(), class_name="relative flex flex-col justify-start items-center w-full h-full min-h-screen font-instrument-sans overflow-hidden", **props, diff --git a/pcweb/views/cta_card.py b/pcweb/views/cta_card.py new file mode 100644 index 000000000..82a821e36 --- /dev/null +++ b/pcweb/views/cta_card.py @@ -0,0 +1,50 @@ +import reflex as rx +import reflex_ui as ui +from reflex_ui.blocks.demo_form import demo_form_dialog + +from pcweb.components.marketing_button import button +from pcweb.constants import REFLEX_BUILD_URL + + +@rx.memo +def cta_card(): + return rx.el.div( + rx.el.div( + rx.el.span( + "The Unified Platform to Build and Scale Enterprise Apps", + class_name="text-slate-12 lg:text-3xl text-2xl font-[575]", + ), + rx.el.span( + "Describe your idea, and let AI transform it into a complete, production-ready Python web application.", + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-medium", + ), + rx.el.div( + demo_form_dialog( + trigger=button( + "Book a Demo", + variant="primary", + native_button=False, + ), + ), + rx.el.a( + button( + "Try for free", + ui.icon("ArrowRight01Icon"), + variant="outline", + native_button=False, + ), + to=REFLEX_BUILD_URL, + target="_blank", + ), + class_name="flex flex-row gap-4 items-center", + ), + class_name="flex flex-col gap-6 justify-center max-w-[24.5rem]", + ), + rx.image( + f"/common/{rx.color_mode_cond('light', 'dark')}/cta.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-t dark:border-m-slate-9 pl-16 max-lg:hidden mb-12", + ) diff --git a/pcweb/views/footer.py b/pcweb/views/footer.py index 8d925eb8c..12e0726e9 100644 --- a/pcweb/views/footer.py +++ b/pcweb/views/footer.py @@ -208,5 +208,5 @@ def footer() -> rx.Component: ), class_name="flex flex-row flex-wrap justify-between gap-[4.5rem] p-[3rem_1rem_3rem_1.5rem] w-full max-w-[94.5rem]", ), - class_name="flex justify-center border-slate-3 border-t w-full", + class_name="flex justify-center border-slate-3 border-t w-full overflow-hidden", ) From 284fac7a26005a33349ba19123e829f509881001 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 27 Feb 2026 14:33:17 +0100 Subject: [PATCH 3/5] add dark mode graphic --- assets/common/dark/cta.svg | 411 +++++++------------- pcweb/pages/framework/views/footer_index.py | 4 +- pcweb/views/cta_card.py | 4 +- 3 files changed, 149 insertions(+), 270 deletions(-) diff --git a/assets/common/dark/cta.svg b/assets/common/dark/cta.svg index 65369631e..b63bc95d7 100644 --- a/assets/common/dark/cta.svg +++ b/assets/common/dark/cta.svg @@ -1,357 +1,236 @@ - - + + - - - + + + - - - - - - - + + + + + + - - - - - - - + + + + - + + + + - - + + - - - - + + + + - + - - - - - + + + + + + + - - - + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - - - + + + - + - + - + - + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - - - - - - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - - + + - + - + diff --git a/pcweb/pages/framework/views/footer_index.py b/pcweb/pages/framework/views/footer_index.py index 6a6089437..013ca141f 100644 --- a/pcweb/pages/framework/views/footer_index.py +++ b/pcweb/pages/framework/views/footer_index.py @@ -81,7 +81,7 @@ 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-10 border border-transparent lg:ml-auto", + 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", ) @@ -169,7 +169,7 @@ def newletter_input() -> rx.Component: 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-10", + 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", ), marketing_button( "Subscribe", diff --git a/pcweb/views/cta_card.py b/pcweb/views/cta_card.py index 82a821e36..cb6959106 100644 --- a/pcweb/views/cta_card.py +++ b/pcweb/views/cta_card.py @@ -30,7 +30,7 @@ def cta_card(): button( "Try for free", ui.icon("ArrowRight01Icon"), - variant="outline", + variant="ghost", native_button=False, ), to=REFLEX_BUILD_URL, @@ -46,5 +46,5 @@ def cta_card(): 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-t dark:border-m-slate-9 pl-16 max-lg:hidden mb-12", + 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", ) From 7eab36bf169b7d139fde3a2b26e84edbbc1e86a4 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 27 Feb 2026 20:18:10 +0100 Subject: [PATCH 4/5] rebase --- pcweb/pages/about/__init__.py | 4 -- pcweb/pages/blog/blog.py | 67 ++++++++++++++++++--------------- pcweb/pages/blog/page.py | 8 ++-- pcweb/views/marketing_navbar.py | 2 +- 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/pcweb/pages/about/__init__.py b/pcweb/pages/about/__init__.py index 029f1be38..60f50ff2e 100644 --- a/pcweb/pages/about/__init__.py +++ b/pcweb/pages/about/__init__.py @@ -11,8 +11,6 @@ square_logo, team, ) -from pcweb.pages.framework.views.footer_index import footer_index -from pcweb.views.cta_card import cta_card from pcweb.templates.marketing_page import marketing_page @@ -36,8 +34,6 @@ def about_page() -> rx.Component: hiring(), team(), news(), - divider(), - cta_card(), class_name="flex flex-col relative justify-center items-center w-full", ), class_name="flex flex-col w-full relative h-full justify-center items-center", diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py index 81d973349..fd58d0362 100644 --- a/pcweb/pages/blog/blog.py +++ b/pcweb/pages/blog/blog.py @@ -96,44 +96,51 @@ def newsletter_input() -> rx.Component: ) -def card_content(meta: dict, path: str) -> rx.Component: - return rx.el.div( - rx.el.a( - rx.el.div( - rx.image( - src=meta["image"], - loading="eager", - custom_attrs={"fetchPriority": "high"}, - alt="Image preview for blog post: " + str(meta["title"]), - class_name="group-hover:scale-105 w-full h-full transition-transform duration-150 ease-out object-top object-cover", - ), - class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[19.5rem] overflow-hidden", +def card_inner(meta: dict, path: str) -> rx.Component: + return rx.el.a( + rx.el.div( + rx.image( + src=meta["image"], + loading="eager", + custom_attrs={"fetchPriority": "high"}, + alt="Image preview for blog post: " + str(meta["title"]), + class_name="group-hover:scale-105 w-full h-full transition-transform duration-150 ease-out object-top object-cover", ), - rx.el.div( - rx.el.span( - meta["title"], - class_name="text-2xl font-[575] text-m-slate-12 dark:text-m-slate-3 mb-4", - ), - rx.el.p( - meta["description"], - class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] mb-6", - ), - rx.el.span( - meta["author"], - class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525] mt-auto", - ), - class_name="flex flex-col w-full h-full pb-12 px-12", + class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[19.5rem] overflow-hidden", + ), + rx.el.div( + rx.el.span( + meta["title"], + class_name="text-2xl font-[575] text-m-slate-12 dark:text-m-slate-3 mb-4", + ), + rx.el.p( + meta["description"], + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] mb-6", + ), + rx.el.span( + meta["author"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525] mt-auto", ), - to=path, - class_name="flex flex-col gap-10 rounded-xl backdrop-blur-[16px] [box-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)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", + class_name="flex flex-col w-full h-full pb-12 px-12", ), + to=path, + class_name="flex flex-col gap-10 rounded-xl backdrop-blur-[16px] [box-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)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", + ) + + +def card_content(meta: dict, path: str, class_name: str = "") -> rx.Component: + return rx.el.div( + card_inner(meta, path), display=rx.cond( (blog_filter_cs.value == "All") | (blog_filter_cs.value == meta.get("tag", "")), "block", "none", ), - class_name="relative border-y border-m-slate-4 dark:border-m-slate-10 lg:odd:border-r lg:even:border-l lg:even:before:content-[''] lg:even:before:absolute lg:even:before:w-12 lg:even:before:-left-12 lg:even:before:top-0 lg:even:before:bottom-0 lg:even:before:border-y lg:even:before:border-m-slate-4 lg:dark:even:before:border-m-slate-10", + class_name=ui.cn( + "relative border-y border-m-slate-4 dark:border-m-slate-10 lg:odd:border-r lg:even:border-l lg:even:before:content-[''] lg:even:before:absolute lg:even:before:w-12 lg:even:before:-left-12 lg:even:before:top-0 lg:even:before:bottom-0 lg:even:before:border-y lg:even:before:border-m-slate-4 lg:dark:even:before:border-m-slate-10", + class_name, + ), ) @@ -161,7 +168,7 @@ def component_grid() -> rx.Component: rx.el.div( class_name="absolute -bottom-24 -right-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" ), - class_name="grid lg:grid-cols-2 grid-cols-1 lg:border border-m-slate-4 dark:border-m-slate-10 w-full gap-x-12 gap-y-12 lg:gap-y-24 relative py-24", + class_name="grid lg:grid-cols-2 grid-cols-1 lg:border border-m-slate-4 dark:border-m-slate-10 w-full gap-x-12 gap-y-12 lg:gap-y-24 relative py-24 lg:mb-48 mb-24", ) diff --git a/pcweb/pages/blog/page.py b/pcweb/pages/blog/page.py index 4c5954226..5d792eb71 100644 --- a/pcweb/pages/blog/page.py +++ b/pcweb/pages/blog/page.py @@ -132,7 +132,7 @@ def table_of_contents(toc: list, path: str, page_url: str) -> rx.Component: def more_posts(current_post: dict) -> rx.Component: - from .blog import card_content + from pcweb.pages.blog.blog import card_inner posts = [] blog_items = list(blog_data.items()) @@ -163,17 +163,17 @@ def more_posts(current_post: dict) -> rx.Component: for path, document in selected_posts: meta = document.metadata - posts.append(card_content(meta=meta, path=f"/blog/{path}")) + posts.append(card_inner(meta=meta, path=f"/blog/{path}")) return rx.el.section( rx.el.h2( "More Posts", - class_name="font-x-large gradient-heading", + class_name="text-4xl font-[575] text-m-slate-12 dark:text-m-slate-3 text-center", ), rx.box( *posts, class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 [&>*]:min-w-[320px] w-full mb-4 blog-grid", ), - class_name="flex flex-col gap-10 mt-20", + class_name="flex flex-col gap-10 mt-20 mb-24", ) diff --git a/pcweb/views/marketing_navbar.py b/pcweb/views/marketing_navbar.py index 017784f63..77d943ea7 100644 --- a/pcweb/views/marketing_navbar.py +++ b/pcweb/views/marketing_navbar.py @@ -270,7 +270,7 @@ def blog_item(blog: dict, path: str) -> rx.Component: rx.moment( rx.Var.create(blog.metadata["date"]).to(str), format="MMM DD YYYY", - class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase", + class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase text-nowrap", ), rx.image( src=f"/common/{rx.color_mode_cond('light', 'dark')}/squares_blog.svg", From 3a85ccff7d56b4df435f93994704841b85e73166 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Mon, 2 Mar 2026 10:19:44 +0100 Subject: [PATCH 5/5] show 3 blog posts per row --- pcweb/pages/blog/blog.py | 14 +++++++------- pcweb/pages/blog/page.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py index fd58d0362..2755cf1f0 100644 --- a/pcweb/pages/blog/blog.py +++ b/pcweb/pages/blog/blog.py @@ -106,25 +106,25 @@ def card_inner(meta: dict, path: str) -> rx.Component: alt="Image preview for blog post: " + str(meta["title"]), class_name="group-hover:scale-105 w-full h-full transition-transform duration-150 ease-out object-top object-cover", ), - class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[19.5rem] overflow-hidden", + class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[17.5rem] overflow-hidden", ), rx.el.div( rx.el.span( meta["title"], - class_name="text-2xl font-[575] text-m-slate-12 dark:text-m-slate-3 mb-4", + class_name="text-2xl font-[575] text-m-slate-12 dark:text-m-slate-3 mb-4 line-clamp-3", ), rx.el.p( meta["description"], - class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] mb-6", + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] mb-6 line-clamp-3", ), rx.el.span( meta["author"], class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525] mt-auto", ), - class_name="flex flex-col w-full h-full pb-12 px-12", + class_name="flex flex-col w-full h-full pb-8 px-8", ), to=path, - class_name="flex flex-col gap-10 rounded-xl backdrop-blur-[16px] [box-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)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", + class_name="flex flex-col gap-8 rounded-xl backdrop-blur-[16px] [box-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)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", ) @@ -138,7 +138,7 @@ def card_content(meta: dict, path: str, class_name: str = "") -> rx.Component: "none", ), class_name=ui.cn( - "relative border-y border-m-slate-4 dark:border-m-slate-10 lg:odd:border-r lg:even:border-l lg:even:before:content-[''] lg:even:before:absolute lg:even:before:w-12 lg:even:before:-left-12 lg:even:before:top-0 lg:even:before:bottom-0 lg:even:before:border-y lg:even:before:border-m-slate-4 lg:dark:even:before:border-m-slate-10", + "relative border-y border-m-slate-4 dark:border-m-slate-10 lg:before:absolute lg:before:w-[calc(2rem+2px)] lg:before:-left-[calc(2rem+1px)] lg:before:-top-[0.5px] lg:before:-bottom-[0.5px] lg:before:border-y lg:before:border-m-slate-4 lg:dark:before:border-m-slate-10 lg:max-xl:odd:border-r lg:max-xl:even:border-l lg:max-xl:even:before:content-[''] xl:[&:nth-child(3n+1)]:border-r xl:[&:nth-child(3n+2)]:border-l xl:[&:nth-child(3n+2)]:border-r xl:[&:nth-child(3n+2)]:before:content-[''] xl:[&:nth-child(3n)]:border-l xl:[&:nth-child(3n)]:before:content-['']", class_name, ), ) @@ -168,7 +168,7 @@ def component_grid() -> rx.Component: rx.el.div( class_name="absolute -bottom-24 -right-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" ), - class_name="grid lg:grid-cols-2 grid-cols-1 lg:border border-m-slate-4 dark:border-m-slate-10 w-full gap-x-12 gap-y-12 lg:gap-y-24 relative py-24 lg:mb-48 mb-24", + class_name="grid lg:grid-cols-2 xl:grid-cols-3 grid-cols-1 lg:border border-m-slate-4 dark:border-m-slate-10 w-full gap-x-8 gap-y-8 lg:gap-y-24 relative py-24 lg:mb-48 mb-24", ) diff --git a/pcweb/pages/blog/page.py b/pcweb/pages/blog/page.py index 5d792eb71..080abdbaa 100644 --- a/pcweb/pages/blog/page.py +++ b/pcweb/pages/blog/page.py @@ -146,19 +146,19 @@ def more_posts(current_post: dict) -> rx.Component: ) if current_index is None: - # If current post is not found, default to first 2 posts - selected_posts = blog_items[:2] + # If current post is not found, default to first 3 posts + selected_posts = blog_items[:3] elif current_index == 0: - # If it's the first post, get the next 2 - selected_posts = blog_items[1:3] + # If it's the first post, get the next 3 + selected_posts = blog_items[1:4] elif current_index == len(blog_items) - 1: - # If it's the last post, get the previous 2 - selected_posts = blog_items[-3:-1] + # If it's the last post, get the previous 3 + selected_posts = blog_items[-4:-1] else: - # Get previous 1 and next 1, excluding current post + # Get previous 1 and next 2, excluding current post selected_posts = ( blog_items[max(0, current_index - 1) : current_index] - + blog_items[current_index + 1 : current_index + 2] + + blog_items[current_index + 1 : current_index + 3] ) for path, document in selected_posts: @@ -171,7 +171,7 @@ def more_posts(current_post: dict) -> rx.Component: ), rx.box( *posts, - class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 [&>*]:min-w-[320px] w-full mb-4 blog-grid", + class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 [&>*]:min-w-[320px] w-full mb-4 blog-grid", ), class_name="flex flex-col gap-10 mt-20 mb-24", )