diff --git a/assets/common/dark/cta.svg b/assets/common/dark/cta.svg
new file mode 100644
index 000000000..b63bc95d7
--- /dev/null
+++ b/assets/common/dark/cta.svg
@@ -0,0 +1,237 @@
+
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/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 3d25e5d6a..60f50ff2e 100644
--- a/pcweb/pages/about/__init__.py
+++ b/pcweb/pages/about/__init__.py
@@ -11,7 +11,6 @@
square_logo,
team,
)
-from pcweb.pages.framework.views.footer_index import footer_index
from pcweb.templates.marketing_page import marketing_page
@@ -35,8 +34,6 @@ def about_page() -> rx.Component:
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",
diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py
index 81d973349..2755cf1f0 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-[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 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 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",
),
- 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-8 px-8",
),
+ to=path,
+ 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",
+ )
+
+
+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: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,
+ ),
)
@@ -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 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 4c5954226..080abdbaa 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())
@@ -146,34 +146,34 @@ 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:
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="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",
+ class_name="flex flex-col gap-10 mt-20 mb-24",
)
diff --git a/pcweb/pages/customers/views/footer.py b/pcweb/pages/customers/views/footer.py
deleted file mode 100644
index 5ce5e5150..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 newsletter_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",
- ),
- newsletter_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..e8d96ef28 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,7 +10,9 @@
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.cta_card import cta_card
from pcweb.views.docs_navbar import docs_navbar
@@ -30,14 +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(),
- divider(),
+ 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/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 41cd011c5..80db108c6 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-9 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 newsletter_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-9 dark:bg-m-slate-11",
),
- 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 newsletter_input() -> rx.Component:
def newsletter() -> rx.Component:
- return (
- rx.box(
- rx.text(
- "Get updates",
- class_name="font-small text-secondary-11",
- ),
- newsletter_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",
),
+ newsletter_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 max-lg:px-4 overflow-hidden",
)
diff --git a/pcweb/pages/framework/views/os_newsletter.py b/pcweb/pages/framework/views/os_newsletter.py
index d8205fff1..6aaa17c66 100644
--- a/pcweb/pages/framework/views/os_newsletter.py
+++ b/pcweb/pages/framework/views/os_newsletter.py
@@ -75,7 +75,7 @@ def newsletter_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 f03970508..b43582ab3 100644
--- a/pcweb/templates/marketing_page.py
+++ b/pcweb/templates/marketing_page.py
@@ -60,8 +60,8 @@ 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.cta_card import cta_card
from pcweb.views.marketing_navbar import marketing_navbar
# Wrap the component in the template.
@@ -70,8 +70,8 @@ def wrapper(*children, **props) -> rx.Component:
rx.el.main(
rx.el.div(
contents(*children, **props),
- bottom_section(),
- footer(),
+ cta_card(),
+ footer_index(),
class_name="flex flex-col relative justify-center items-center w-full",
),
class_name=ui.cn(
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..43c19900b 100644
--- a/pcweb/templates/webpage.py
+++ b/pcweb/templates/webpage.py
@@ -59,8 +59,8 @@ 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.cta_card import cta_card
from pcweb.views.marketing_navbar import marketing_navbar
# Wrap the component in the template.
@@ -72,8 +72,8 @@ def wrapper(*children, **props) -> rx.Component:
rx.box(class_name="flex-grow"),
class_name="w-full z-[1]",
),
- bottom_section(),
- footer(),
+ 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/bottom_section/newsletter.py b/pcweb/views/bottom_section/newsletter.py
index 54d15c289..a51c85d7d 100644
--- a/pcweb/views/bottom_section/newsletter.py
+++ b/pcweb/views/bottom_section/newsletter.py
@@ -45,7 +45,7 @@ def newsletter_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/cta_card.py b/pcweb/views/cta_card.py
new file mode 100644
index 000000000..cb6959106
--- /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="ghost",
+ 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 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 e9b296dc7..12e0726e9 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",
@@ -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",
)
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",