diff --git a/blog/2023-06-28-rebrand-to-reflex.md b/blog/2023-06-28-rebrand-to-reflex.md index a1f1ad2f7..01335e415 100644 --- a/blog/2023-06-28-rebrand-to-reflex.md +++ b/blog/2023-06-28-rebrand-to-reflex.md @@ -4,6 +4,7 @@ date: 2023-06-28 title: Pynecone is now Reflex description: We have some exciting news to share about the next stage of our company! image: /reflex_banner.png +tag: Announcements meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-08-02-seed-annoucement.md b/blog/2023-08-02-seed-annoucement.md index 3018f79c1..b4edc487d 100644 --- a/blog/2023-08-02-seed-annoucement.md +++ b/blog/2023-08-02-seed-annoucement.md @@ -4,6 +4,7 @@ date: 2023-08-02 title: Announcing our Seed Round description: Reflex has raised a $5M seed led by Lux Capital. image: /blog/fundraise_dark.webp +tag: Announcements meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md b/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md index 13d9d5b3b..aac39927a 100644 --- a/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md +++ b/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md @@ -4,6 +4,7 @@ date: 2023-09-28 title: Unlocking New Workflows with Background Tasks description: What is a background task and how can it help you build better apps? image: /blog/background_tasks.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-10-11-graphing-update.md b/blog/2023-10-11-graphing-update.md index 3478cd3a0..3e76a0538 100644 --- a/blog/2023-10-11-graphing-update.md +++ b/blog/2023-10-11-graphing-update.md @@ -4,6 +4,7 @@ date: 2023-10-11 title: "New Core Graphing Components" description: "Using Reflex's new core graphing feature to build a live streaming graphing app." image: /blog/graphing.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-10-25-implementing-sign-in-with-google.md b/blog/2023-10-25-implementing-sign-in-with-google.md index 667ada5db..06aac4fd9 100644 --- a/blog/2023-10-25-implementing-sign-in-with-google.md +++ b/blog/2023-10-25-implementing-sign-in-with-google.md @@ -4,6 +4,7 @@ date: 2023-10-25 title: "Implementing Sign In with Google" description: "How to wrap a third-party auth component and integrate it into a Reflex app." image: /blog/google_auth.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-02-16-reflex-v0-4-0.md b/blog/2024-02-16-reflex-v0-4-0.md index 982ac725b..79c34c19e 100644 --- a/blog/2024-02-16-reflex-v0-4-0.md +++ b/blog/2024-02-16-reflex-v0-4-0.md @@ -4,6 +4,7 @@ date: 2024-02-16 title: "Reflex v0.4.0" description: "New features coming in the 0.4.0 release." image: /blog/reflex-040.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-03-21-reflex-architecture.md b/blog/2024-03-21-reflex-architecture.md index bc05dc8e3..c49c76585 100644 --- a/blog/2024-03-21-reflex-architecture.md +++ b/blog/2024-03-21-reflex-architecture.md @@ -4,6 +4,7 @@ date: 2024-03-21 title: Designing a Pure Python Web Framework description: A look at how Reflex works under the hood. image: /blog/web_framework.webp +tag: Open Source meta: [ {"name": "keywords", "content": "python web app, python web app framework, web app framework python, python web apps, web app python, react python websocket, python react, react python, react with python, python and react js, react js with python, python and react, react js python"}, ] diff --git a/blog/2024-03-27-structuring-a-large-app.md b/blog/2024-03-27-structuring-a-large-app.md index a698e4287..e3a4fe122 100644 --- a/blog/2024-03-27-structuring-a-large-app.md +++ b/blog/2024-03-27-structuring-a-large-app.md @@ -4,6 +4,7 @@ date: 2024-03-27 title: "Structuring a Large App" description: "So your Reflex app is getting large? Here's some advice on how to lay it out." image: /blog/project_structure.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-04-16-custom-components.md b/blog/2024-04-16-custom-components.md index 89378ebe5..56fd07766 100644 --- a/blog/2024-04-16-custom-components.md +++ b/blog/2024-04-16-custom-components.md @@ -4,6 +4,7 @@ date: 2024-04-16 title: Custom Components description: Announcing our custom component ecosystem. image: /blog/custom_components.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-06-28-using-table-component.md b/blog/2024-06-28-using-table-component.md index 0ca775b97..86b64a829 100644 --- a/blog/2024-06-28-using-table-component.md +++ b/blog/2024-06-28-using-table-component.md @@ -4,6 +4,7 @@ date: 2024-06-28 title: Creating Tables in Reflex description: Describing main uses of the Table component with a Database image: /blog/table.webp +tag: Open Source meta: [ {"name": "keywords", "content": "data table, table data, data tables, data table example, python data table, python table"}, ] diff --git a/blog/2024-09-21-reflex-v060.md b/blog/2024-09-21-reflex-v060.md index 326d9c4c0..17416247a 100644 --- a/blog/2024-09-21-reflex-v060.md +++ b/blog/2024-09-21-reflex-v060.md @@ -4,6 +4,7 @@ date: 2024-09-21 title: Reflex v0.6.0 description: New features and improvements in Reflex v0.6.0 image: /blog/reflex-060.webp +tag: Open Source meta: [ {"name": "keywords", "content": "web application framework in python, python gui library, python web framework, reflex python"}, ] diff --git a/blog/2024-09-25-using-ag-grid-in-reflex.md b/blog/2024-09-25-using-ag-grid-in-reflex.md index 6ba06e797..7140e13b6 100644 --- a/blog/2024-09-25-using-ag-grid-in-reflex.md +++ b/blog/2024-09-25-using-ag-grid-in-reflex.md @@ -4,6 +4,7 @@ date: 2024-10-03 title: AG Grid in Reflex description: Getting started with powerful data tables in your Python web apps image: /blog/ag-grid.webp +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2024-10-8-self-hosting-reflex-with-docker.md b/blog/2024-10-8-self-hosting-reflex-with-docker.md index fa2c97f52..ec2be0514 100644 --- a/blog/2024-10-8-self-hosting-reflex-with-docker.md +++ b/blog/2024-10-8-self-hosting-reflex-with-docker.md @@ -4,6 +4,7 @@ date: 2024-10-08 title: Self Hosting Reflex with Docker description: Hosting Reflex on your own infra using Docker for efficient containerization. image: /blog/self-hosting-with-docker.webp +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2024-11-19-microsoft-azure-authentication.md b/blog/2024-11-19-microsoft-azure-authentication.md index d32a72bce..7f65f85be 100644 --- a/blog/2024-11-19-microsoft-azure-authentication.md +++ b/blog/2024-11-19-microsoft-azure-authentication.md @@ -4,6 +4,7 @@ date: 2024-11-19 title: Microsoft Azure Auth description: Implementing Microsoft Azure Single Sign-On (SSO) Auth in Reflex app. image: /blog/azure_auth.webp +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2024-12-05-reflex-cloud.md b/blog/2024-12-05-reflex-cloud.md index 959417fcb..0c8518cfb 100644 --- a/blog/2024-12-05-reflex-cloud.md +++ b/blog/2024-12-05-reflex-cloud.md @@ -4,6 +4,7 @@ date: 2024-12-05 title: Reflex Cloud description: Deploy your Reflex apps with a single command image: /blog/reflex-cloud.webp +tag: Cloud meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-12-20-python-comparison.md b/blog/2024-12-20-python-comparison.md index f766c77d7..c66e59192 100644 --- a/blog/2024-12-20-python-comparison.md +++ b/blog/2024-12-20-python-comparison.md @@ -4,6 +4,7 @@ date: 2024-12-20 title: Top Python Web Development Frameworks in 2025 description: Reflex vs Django vs Flask vs Gradio vs Streamlit vs Dash vs FastAPI image: /blog/top_python_web_frameworks.webp +tag: Builder meta: [ {"name": "keywords", "content": "streamlit python, streamlit, streamlit alternatives, plotly, dash app, plotly python, fastapi"}, ] diff --git a/blog/2025-01-27-top-10-data-visualization-libraries.md b/blog/2025-01-27-top-10-data-visualization-libraries.md index 5a8418e37..6fa36a382 100644 --- a/blog/2025-01-27-top-10-data-visualization-libraries.md +++ b/blog/2025-01-27-top-10-data-visualization-libraries.md @@ -4,6 +4,7 @@ date: 2025-01-27 title: Top 10 Python Data Visualization Libraries in 2025 description: Matplotlib vs Seaborn vs Plotly vs Bokeh vs Altair vs GeoPandas vs HoloViews vs Pygal vs Geoplotlib vs GGPlot image: /blog/data_visualization_blog.webp +tag: Builder meta: [ {"name": "keywords", "content": "matplotlib, seaborn, plotly, bokeh, altair, geopandas, holoviews, pygal, Geoplotlib, ggplot"}, ] diff --git a/blog/2025-05-16-top-5-ai-app-builders.md b/blog/2025-05-16-top-5-ai-app-builders.md index 7cc14ca92..cf8f1bf2c 100644 --- a/blog/2025-05-16-top-5-ai-app-builders.md +++ b/blog/2025-05-16-top-5-ai-app-builders.md @@ -4,6 +4,7 @@ date: 2025-05-16 title: Top AI App Builders in 2025 description: Reflex.build vs Replit Agent vs v0.dev vs Bolt.new vs Lovable.dev image: /blog/ai_app_builders.webp +tag: Builder meta: [ {"name": "keywords", "content": "Reflex vs Replit, v0.dev alternatives, build AI apps, no-code AI tools, Replit agent, AI developer tools, app builders for developers, AI UI generators, v0.dev review, Replit AI tools, Lovable.dev, Bolt.new, AI in software development, full-stack AI apps"} ] diff --git a/blog/2025-06-03-internal-tool-builders-2025.md b/blog/2025-06-03-internal-tool-builders-2025.md index cf6372837..7a7048396 100644 --- a/blog/2025-06-03-internal-tool-builders-2025.md +++ b/blog/2025-06-03-internal-tool-builders-2025.md @@ -4,6 +4,7 @@ date: 2025-06-03 title: The 10 best internal tool builders in 2025 description: Reflex vs Retool vs Budibase vs Appsmith vs ToolJet vs Zapier Interfaces vs Glide vs Softr vs DronaHQ vs Microsoft Power Apps image: /blog/10_best_internal_tool_builders.webp +tag: Builder meta: [ {"name": "keywords", "content": "Reflex vs Retool, internal tool builders 2025, Appsmith alternatives, Budibase vs ToolJet, low-code tools, no-code internal apps, best internal app platforms, enterprise internal tools, Power Apps review, build dashboards, admin panel builders, internal tool comparison, open source internal tools, Zapier Interfaces, Glide app builder, DronaHQ, Softr"} ] diff --git a/blog/2025-06-20-reflex-dash.md b/blog/2025-06-20-reflex-dash.md index d5468aa66..76d572da7 100644 --- a/blog/2025-06-20-reflex-dash.md +++ b/blog/2025-06-20-reflex-dash.md @@ -4,6 +4,7 @@ date: 2025-06-20 title: Reflex vs Plotly Dash description: A Comparison of Python Frameworks for Building Interactive Financial Dashboards image: /blog/reflex-dash.webp +tag: Builder meta: [ { "name": "keywords", diff --git a/blog/2025-06-30-reflex-databricks-partnership.md b/blog/2025-06-30-reflex-databricks-partnership.md index a89a3d752..17f609bea 100644 --- a/blog/2025-06-30-reflex-databricks-partnership.md +++ b/blog/2025-06-30-reflex-databricks-partnership.md @@ -4,6 +4,7 @@ date: 2025-06-30 title: "Reflex x Databricks: Python-Powered Vibe Coding for Enterprises" description: "We're excited to announce our official partnership with Databricks and the launch of our Reflex AI Builder integration with Databricks Apps." image: /blog/databricks-partnership.webp +tag: Announcements meta: [ {"name": "keywords", "content": "Reflex Databricks, Python apps, enterprise development, AI Builder, Unity Catalog, data apps, Python framework, Databricks Apps"} ] diff --git a/blog/2025-07-01-reflex-080.md b/blog/2025-07-01-reflex-080.md index 67a4040d9..ac2aefc77 100644 --- a/blog/2025-07-01-reflex-080.md +++ b/blog/2025-07-01-reflex-080.md @@ -4,6 +4,7 @@ date: 2025-07-01 title: Reflex v0.8.0 - Refactoring for Performance description: Major improvements in Reflex v0.8.0 developer experience and performance. image: /blog/080.webp +tag: Open Source meta: [ {"name": "keywords", "content": "reflex python, web framework, nextjs migration, react router, vite, pydantic replacement, performance improvements"}, ] diff --git a/blog/2025-08-20-reflex-streamlit.md b/blog/2025-08-20-reflex-streamlit.md index 35baed47b..49eaf48ca 100644 --- a/blog/2025-08-20-reflex-streamlit.md +++ b/blog/2025-08-20-reflex-streamlit.md @@ -4,6 +4,7 @@ date: 2025-08-20 title: Reflex vs Streamlit description: "A head‑to‑head comparison of two Python frameworks for building fast, interactive web apps" image: /blog/reflex-streamlit.webp +tag: Builder meta: [ { "name": "keywords", diff --git a/blog/2025-09-03-reflex-jupyter.md b/blog/2025-09-03-reflex-jupyter.md index bc738a2eb..1714e363a 100644 --- a/blog/2025-09-03-reflex-jupyter.md +++ b/blog/2025-09-03-reflex-jupyter.md @@ -4,6 +4,7 @@ date: 2025-09-03 title: Turn Jupyter Notebooks into Production Dashboards in Python description: "Convert Jupyter notebooks into interactive, production-ready dashboards with Python and Reflex. Keep your pandas logic, add UI, and deploy in minutes." image: /blog/jupyter_reflex.png +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2025-12-05-on-premises-deployment.md b/blog/2025-12-05-on-premises-deployment.md index 49b12a531..2956fcef2 100644 --- a/blog/2025-12-05-on-premises-deployment.md +++ b/blog/2025-12-05-on-premises-deployment.md @@ -4,6 +4,7 @@ date: 2025-12-05 title: "Reflex Build On-Prem: The Secure AI App Builder That Runs Inside Your Company Infrastructure" description: An enterprise, on-premises AI app builder for developing secure internal tools and dashboards using Python. image: /blog/on-prem.png +tag: Cloud meta: [ { "name": "keywords", diff --git a/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md b/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md index 19b2dd6dd..78a794246 100644 --- a/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md +++ b/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md @@ -4,6 +4,7 @@ date: 2025-12-17 title: "Top 7 Enterprise AI App Builders in 2026: A Practical Comparison" description: Reflex.build vs Microsoft Power Apps vs Superblocks vs Blaze.tech vs Knack vs Lovable.dev vs Replit Agent image: /blog/top7.webp +tag: Builder meta: [ { "name": "keywords", diff --git a/blog/2026-01-09-top-python-web-frameworks-2026.md b/blog/2026-01-09-top-python-web-frameworks-2026.md index db7cb2535..a7395f47a 100644 --- a/blog/2026-01-09-top-python-web-frameworks-2026.md +++ b/blog/2026-01-09-top-python-web-frameworks-2026.md @@ -4,6 +4,7 @@ date: 2026-01-09 title: Top Python Web Development Frameworks in 2026 description: Reflex vs Django vs Flask vs Gradio vs Streamlit vs Dash vs FastAPI image: /blog/top_python_web_frameworks_2026.png +tag: Builder meta: [ {"name": "keywords", "content": "streamlit python, streamlit, streamlit alternatives, plotly, dash app, plotly python, fastapi"}, ] diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py index 15c76d619..02455eb09 100644 --- a/pcweb/pages/__init__.py +++ b/pcweb/pages/__init__.py @@ -1,6 +1,6 @@ from pcweb.route import Route -from .about import about # noqa: F401 +from .about import about_page as about_page from .blog import blog_routes from .booked import booked as booked from .check_your_email_demo import page_thank_you as page_thank_you diff --git a/pcweb/pages/about/__init__.py b/pcweb/pages/about/__init__.py index fbaeb814e..3d25e5d6a 100644 --- a/pcweb/pages/about/__init__.py +++ b/pcweb/pages/about/__init__.py @@ -12,11 +12,11 @@ team, ) from pcweb.pages.framework.views.footer_index import footer_index -from pcweb.views.marketing_navbar import marketing_navbar +from pcweb.templates.marketing_page import marketing_page -@rx.page( - route="/about", +@marketing_page( + path="/about", title="Reflex · About", meta=create_meta_tags( title="Reflex · About", @@ -24,24 +24,20 @@ image="/previews/index_preview.webp", ), ) -def about() -> rx.Component: +def about_page() -> rx.Component: return rx.el.div( - marketing_navbar(), - rx.el.main( - rx.el.div( - hero(), - companies(), - square_logo(), - cards(), - divider(), - hiring(), - team(), - news(), - divider(), - 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", + rx.el.div( + hero(), + companies(), + square_logo(), + cards(), + divider(), + hiring(), + team(), + news(), + divider(), + footer_index(), + class_name="flex flex-col relative justify-center items-center w-full", ), - class_name="flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", + class_name="flex flex-col w-full relative h-full justify-center items-center", ) diff --git a/pcweb/pages/about/views/hero.py b/pcweb/pages/about/views/hero.py index 93d6144ce..3ccfca931 100644 --- a/pcweb/pages/about/views/hero.py +++ b/pcweb/pages/about/views/hero.py @@ -1,8 +1,6 @@ import reflex as rx import reflex_ui as ui -from pcweb.components.hosting_banner import HostingBannerState - def hero() -> rx.Component: return rx.el.section( @@ -92,11 +90,6 @@ def hero() -> rx.Component: class_name="flex flex-col gap-8 lg:p-12 p-6 border border-m-slate-4 dark:border-m-slate-10 shadow-[0_6px_16px_0_rgba(0,0,0,0.04)_inset] dark:shadow-none dark:bg-[linear-gradient(212deg,var(--m-slate-12)_0%,var(--m-slate-11)_100%)] lg:max-w-[34.5rem] w-full relative", ), class_name=ui.cn( - "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden", - rx.cond( - HostingBannerState.is_banner_visible, - "lg:pt-[14.5rem] pt-[12.5rem]", - "lg:pt-[10.5rem] pt-[7.5rem]", - ), + "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden pt-24", ), ) diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py index f40d32e82..81d973349 100644 --- a/pcweb/pages/blog/blog.py +++ b/pcweb/pages/blog/blog.py @@ -1,158 +1,201 @@ import reflex as rx +import reflex_ui as ui +from reflex.experimental.client_state import ClientStateVar -from pcweb.components.icons.icons import get_icon -from pcweb.components.webpage.comps import h1_title +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.components.marketing_button import button as marketing_button from pcweb.meta.meta import create_meta_tags +from pcweb.signup import IndexState from pcweb.templates.marketing_page import marketing_page -from pcweb.templates.webpage import webpage from .page import page from .paths import blog_data +blog_filter_cs = ClientStateVar.create("blog_filter", default="All") -def first_post_card(meta: dict, path: str) -> rx.Component: - return rx.link( - rx.box( - 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-left object-cover", +FILTERS = ["All", "Announcements", "Open Source", "Builder", "Cloud"] + + +def filter_button(filter: str) -> rx.Component: + active_cn = "shadow-[inset_0_-1px_0_0_var(--primary-10)] text-primary-10 dark:text-primary-9" + return marketing_button( + filter, + variant="ghost", + size="sm", + on_click=blog_filter_cs.set_value(filter), + class_name=ui.cn( + "h-full rounded-none", + rx.cond(blog_filter_cs.value == filter, active_cn, ""), + ), + ) + + +def blog_filters_row() -> rx.Component: + return rx.el.div( + rx.el.div( + *[filter_button(filter) for filter in FILTERS], + class_name="flex flex-row gap-2 items-center px-4 h-full", + ), + newsletter_input(), + class_name=ui.cn( + "flex flex-row gap-4 items-center justify-between h-[3.25rem] border-x border-m-slate-4 dark:border-m-slate-10 bg-m-slate-1 dark:bg-m-slate-11 [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.05),_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_1px_0_0_#FFF_inset] w-full z-10 lg:sticky dark:shadow-none max-lg:overflow-x-auto max-lg:-mx-4 max-lg:w-[calc(100%+2rem)]", + rx.cond( + HostingBannerState.is_banner_visible, + "lg:top-[7rem]", + "lg:top-[4.5rem]", ), - class_name="relative flex-shrink-0 w-2/5 h-[18rem] overflow-hidden border-r border-solid border-slate-5", ), - rx.box( - rx.box( - rx.el.h4( - meta["title"], - class_name="font-x-large text-slate-12", + ) + + +def newsletter_input() -> rx.Component: + return rx.el.div( + rx.cond( + IndexState.signed_up, + rx.el.div( + rx.el.div( + rx.icon( + tag="circle-check", + size=16, + class_name="!text-violet-9", + ), + rx.text( + "Thanks for subscribing!", + class_name="text-sm text-m-slate-7 dark:text-m-slate-6 font-medium", + ), + class_name="flex flex-row items-center gap-2", ), - rx.moment( - str(meta["date"]), - format="MMM DD, YYYY", - class_name="font-normal text-secondary-11 text-sm", + marketing_button( + "Sign up for another email", + variant="outline", + size="md", + on_click=IndexState.signup_for_another_user, ), - class_name="flex flex-col gap-1 p-[0.625rem_0.75rem_0rem_0.75rem] w-full", + class_name="flex flex-row items-center gap-2", ), - rx.box( - rx.text( - meta["description"], - class_name="line-clamp-2 font-base text-secondary-11", + rx.form( + rx.el.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-9 px-2.5 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 bg-white dark:bg-m-slate-12", ), - rx.box( - rx.text( - meta["author"], - class_name="font-small text-secondary-11 truncate overflow-hidden text-ellipsis max-w-[50%] min-w-0 flex-shrink", - ), - rx.el.button( - rx.text( - "Read more", - class_name="font-small text-secondary-11", - ), - get_icon(icon="new_tab", class_name="p-[5px]"), - class_name="flex items-center border-slate-5 bg-slate-1 hover:bg-slate-3 shadow-small pl-[5px] border rounded-md w-auto max-w-full text-secondary-11 transition-bg cursor-pointer overflow-hidden flex-shrink-0", - ), - class_name="flex flex-row justify-between items-center gap-1 min-w-0 w-full h-auto", + marketing_button( + "Get Updates", + type="submit", + variant="outline", + size="md", + class_name="w-fit max-w-full", ), - class_name="flex flex-col justify-between p-[0rem_0.75rem_0.75rem_0.75rem] w-full h-full", + class_name="w-full flex flex-col lg:flex-row gap-2 lg:items-center items-start max-lg:hidden", + on_submit=IndexState.signup, ), - class_name="flex flex-col gap-[10px] w-full h-auto", ), - href=path, - is_external=False, - underline="none", - class_name="box-border lg:flex flex-row flex-shrink-0 gap-3 border-slate-5 hidden bg-slate-1 shadow-large border border-solid rounded-xl w-full overflow-hidden group", + class_name="ml-auto mr-2", ) def card_content(meta: dict, path: str) -> rx.Component: - return rx.link( - rx.box( - 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-left object-cover", + 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", ), - class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[13.5rem] overflow-hidden", - ), - rx.box( - rx.box( + rx.el.div( rx.el.span( meta["title"], - class_name="font-smbold text-slate-12", - ), - rx.moment( - str(meta["date"]), - format="MMM DD, YYYY", - class_name="font-normal text-secondary-11 text-xs", + class_name="text-2xl font-[575] text-m-slate-12 dark:text-m-slate-3 mb-4", ), - class_name="flex flex-col gap-1 p-[0.625rem_0.75rem_0rem_0.75rem] w-full", - ), - rx.box( - rx.text( + rx.el.p( meta["description"], - class_name="line-clamp-2 font-small text-secondary-11", + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] mb-6", ), - rx.box( - rx.text( - meta["author"], - class_name="font-small text-secondary-11 truncate overflow-hidden text-ellipsis max-w-[50%] min-w-0 flex-shrink", - ), - rx.el.button( - rx.text( - "Read more", - class_name="font-small text-secondary-11", - ), - get_icon(icon="new_tab", class_name="p-[5px]"), - class_name="flex items-center border-slate-5 bg-slate-1 hover:bg-slate-3 shadow-small pl-[5px] border rounded-md w-auto max-w-full text-secondary-11 transition-bg cursor-pointer overflow-hidden", - ), - class_name="flex flex-row justify-between items-center gap-1 w-full h-auto", + 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 justify-between p-[0rem_0.75rem_0.75rem_0.75rem] w-full h-full", + class_name="flex flex-col w-full h-full pb-12 px-12", ), - class_name="flex flex-col gap-[10px] w-full h-full", + 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", + ), + display=rx.cond( + (blog_filter_cs.value == "All") + | (blog_filter_cs.value == meta.get("tag", "")), + "block", + "none", ), - href=path, - is_external=False, - underline="none", - class_name="box-border flex flex-col flex-shrink-0 border-slate-5 bg-slate-1 shadow-large border border-solid rounded-xl w-full h-[390px] overflow-hidden group", + 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", ) -def first_post() -> rx.Component: - for path, document in blog_data.items(): - if path == next(iter(blog_data.keys())): - return first_post_card(meta=document.metadata, path=f"/blog/{path}") - - def component_grid() -> rx.Component: posts = [] for path, document in list(blog_data.items()): posts.append(card_content(meta=document.metadata, path=f"/blog/{path}")) - return rx.box( + return rx.el.div( *posts, - class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 [&>*]:min-w-[320px] w-full mb-4 lg:[&>*:first-child]:hidden", + 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" + ), + rx.el.div( + class_name="absolute -bottom-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" + ), + rx.el.div( + class_name="absolute -bottom-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 -bottom-24 -left-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" + ), + 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", ) -@webpage(path="/blog", title="Reflex Blog") +@marketing_page( + path="/blog", + title="Reflex Blog", + description="Reflex Blog", + image="/previews/index_preview.webp", + meta=create_meta_tags( + title="Reflex Blog", + description="Reflex Blog", + image="/previews/index_preview.webp", + url="https://reflex.dev/blog", + ), +) def blogs(): return rx.el.section( rx.el.header( - h1_title(title="Blog"), + rx.el.h1( + "Blog", + class_name="text-4xl font-[575] text-m-slate-12 dark:text-m-slate-3 text-center", + ), rx.el.h2( - "Reflex is the open-source framework empowering Python developers to build web apps faster.", - class_name="font-md text-balance text-slate-10", + "Reflex is the open-source framework empowering Python ", + rx.el.br(class_name=""), + " developers to build web apps faster.", + class_name="font-medium text-center text-m-slate-7 dark:text-m-slate-6 text-base", ), - class_name="pb-4 section-header", + class_name="pb-12 pt-24 lg:border-x border-m-slate-4 dark:border-m-slate-10 flex flex-col justify-center items-center gap-6 w-full", ), - first_post(), + blog_filters_row(), component_grid(), id="blog", - class_name="section-content", + class_name="flex flex-col justify-center items-center max-w-(--docs-layout-max-width) mx-auto w-full max-lg:px-4", ) diff --git a/pcweb/pages/blog/page.py b/pcweb/pages/blog/page.py index 404ad909a..9d6b4e3ec 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 3 posts - selected_posts = blog_items[:3] + # If current post is not found, default to first 2 posts + selected_posts = blog_items[:2] elif current_index == 0: - # If it's the first post, get the next 3 - selected_posts = blog_items[1:4] + # If it's the first post, get the next 2 + selected_posts = blog_items[1:3] elif current_index == len(blog_items) - 1: - # If it's the last post, get the previous 3 - selected_posts = blog_items[-4:-1] + # If it's the last post, get the previous 2 + selected_posts = blog_items[-3:-1] else: - # Get previous 1 and next 2, excluding current post + # Get previous 1 and next 1, excluding current post selected_posts = ( blog_items[max(0, current_index - 1) : current_index] - + blog_items[current_index + 1 : current_index + 3] + + blog_items[current_index + 1 : current_index + 2] ) 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-3 [&>*]:min-w-[320px] w-full mb-4 blog-grid", + 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", ) @@ -192,6 +192,11 @@ def page(document, route) -> rx.Component: class_name="text-m-slate-12 dark:text-m-slate-3 font-[575] hover:text-primary-10 dark:hover:text-primary-9 text-xs", ), rx.el.div(class_name="w-4 h-px bg-m-slate-5 dark:bg-m-slate-10"), + rx.el.span( + meta["tag"], + class_name="text-m-slate-12 dark:text-m-slate-3 font-[575] text-xs", + ), + rx.el.div(class_name="w-4 h-px bg-m-slate-5 dark:bg-m-slate-10"), rx.moment( str(meta["date"]), format="MMM DD, YYYY", @@ -259,10 +264,5 @@ def page(document, route) -> rx.Component: ), class_name=ui.cn( "flex flex-col mx-auto max-lg:px-6 w-full relative", - rx.cond( - HostingBannerState.is_banner_visible, - "lg:pt-[7rem] pt-[11rem]", - "lg:pt-[4rem] pt-[8rem]", - ), ), ) diff --git a/pcweb/pages/customers/views/footer.py b/pcweb/pages/customers/views/footer.py index c8c35bf9d..5ce5e5150 100644 --- a/pcweb/pages/customers/views/footer.py +++ b/pcweb/pages/customers/views/footer.py @@ -65,7 +65,7 @@ def menu_socials() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -116,7 +116,7 @@ def news_letter() -> rx.Component: "Get updates", class_name="font-small text-slate-9", ), - newletter_input(), + newsletter_input(), class_name="flex flex-col items-start gap-4 self-stretch p-10", ), ) diff --git a/pcweb/pages/framework/views/footer_index.py b/pcweb/pages/framework/views/footer_index.py index ddc411d55..41cd011c5 100644 --- a/pcweb/pages/framework/views/footer_index.py +++ b/pcweb/pages/framework/views/footer_index.py @@ -92,7 +92,7 @@ def menu_socials() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -144,7 +144,7 @@ def newsletter() -> rx.Component: "Get updates", class_name="font-small text-secondary-11", ), - newletter_input(), + newsletter_input(), class_name="flex flex-col items-center lg:items-start gap-4 self-stretch p-10 lg:border-r border-slate-3", ), ) diff --git a/pcweb/pages/framework/views/os_newsletter.py b/pcweb/pages/framework/views/os_newsletter.py index 25796d7a9..d8205fff1 100644 --- a/pcweb/pages/framework/views/os_newsletter.py +++ b/pcweb/pages/framework/views/os_newsletter.py @@ -34,7 +34,7 @@ def os_card() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -123,7 +123,7 @@ def newsletter_card() -> rx.Component: ), class_name="flex flex-col gap-2", ), - newletter_input(), + newsletter_input(), id="newsletter", class_name="flex flex-col gap-8 w-full p-10 pb-12 lg:!border-r !border-slate-3 items-center lg:items-start text-center lg:text-start scroll-mt-72", ) diff --git a/pcweb/templates/marketing_page.py b/pcweb/templates/marketing_page.py index 3df41a651..f03970508 100644 --- a/pcweb/templates/marketing_page.py +++ b/pcweb/templates/marketing_page.py @@ -2,7 +2,9 @@ from typing import Callable import reflex as rx +import reflex_ui as ui +from pcweb.components.hosting_banner import HostingBannerState from pcweb.route import Route DEFAULT_TITLE = "The platform to build and scale enterprise apps" @@ -72,7 +74,14 @@ def wrapper(*children, **props) -> rx.Component: footer(), 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", + class_name=ui.cn( + "flex flex-col w-full relative h-full justify-center items-center", + rx.cond( + HostingBannerState.is_banner_visible, + "mt-28", + "mt-16", + ), + ), ), class_name="flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", ) diff --git a/pcweb/views/bottom_section/newsletter.py b/pcweb/views/bottom_section/newsletter.py index e595fcfe2..54d15c289 100644 --- a/pcweb/views/bottom_section/newsletter.py +++ b/pcweb/views/bottom_section/newsletter.py @@ -4,7 +4,7 @@ from pcweb.signup import IndexState -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -91,7 +91,7 @@ def newsletter_card() -> rx.Component: ), class_name="flex flex-col items-center text-center gap-2", ), - newletter_input(), + newsletter_input(), class_name="flex flex-col gap-4 w-full items-center", )