diff --git a/apps/developer-hub/src/components/Pages/Homepage/index.module.scss b/apps/developer-hub/src/components/Pages/Homepage/index.module.scss index b5cbefd482..7e44ba9239 100644 --- a/apps/developer-hub/src/components/Pages/Homepage/index.module.scss +++ b/apps/developer-hub/src/components/Pages/Homepage/index.module.scss @@ -1,5 +1,21 @@ @use "@pythnetwork/component-library/theme"; .landing { + padding: theme.spacing(4) theme.spacing(2); + @include theme.max-width; + + @include theme.breakpoint("md") { + padding: theme.spacing(6) theme.spacing(3); + } +} + +.cards { + display: flex; + gap: theme.spacing(10); + flex-direction: column; + + @include theme.breakpoint("md") { + flex-direction: row; + } } diff --git a/apps/developer-hub/src/components/Pages/Homepage/index.tsx b/apps/developer-hub/src/components/Pages/Homepage/index.tsx index 03ac745e3a..99c687c2f0 100644 --- a/apps/developer-hub/src/components/Pages/Homepage/index.tsx +++ b/apps/developer-hub/src/components/Pages/Homepage/index.tsx @@ -1,9 +1,88 @@ +import { Lightning } from "@phosphor-icons/react/dist/ssr"; + import styles from "./index.module.scss"; +import { ProductCard } from "../../ProductCard"; export const Homepage = () => { return (

Homepage Landing Page

+
+ }, + { + label: "100+ blockchains", + icon: , + }, + { + label: "Confidence intervals", + icon: , + }, + { label: "2500+ price feeds", icon: }, + ]} + quickLinks={[ + { + label: "Getting Started", + href: "/price-feeds/v1/getting-started", + }, + { label: "API Reference", href: "/openapi/hermes" }, + { + label: "Contract Addresses", + href: "/price-feeds/v1/contract-addresses", + }, + ]} + buttonLabel="Get started" + buttonHref="/price-feeds/v1" + /> + }, + { + label: "Crypto, Equities & Indexes", + icon: , + }, + { + label: "Customizable channels and latency", + icon: , + }, + { label: "Dedicated support", icon: }, + ]} + quickLinks={[ + { + label: "Get Pyth Pro Access Token", + href: "/price-feeds/v2/acquire-an-access-token", + }, + { label: "Browse Supported Feeds", href: "/price-feeds" }, + { label: "Error Codes", href: "/price-feeds" }, + ]} + buttonLabel="Get started" + buttonHref="/price-feeds" + /> + }, + { label: "Verifiable results", icon: }, + { label: "Multiple chains", icon: }, + ]} + quickLinks={[ + { + label: "Getting Started", + href: "/entropy/create-your-first-entropy-app", + }, + { label: "Protocol Design", href: "/entropy/protocol-design" }, + { label: "Examples", href: "/entropy/examples" }, + ]} + buttonLabel="Get started" + buttonHref="/entropy" + /> +
); }; diff --git a/apps/developer-hub/src/components/ProductCard/index.module.scss b/apps/developer-hub/src/components/ProductCard/index.module.scss new file mode 100644 index 0000000000..3ade7778ec --- /dev/null +++ b/apps/developer-hub/src/components/ProductCard/index.module.scss @@ -0,0 +1,174 @@ +@use "@pythnetwork/component-library/theme"; + +.card { + flex: 1; + padding: theme.spacing(6); + border: 1px solid var(--color-fd-border); + border-radius: theme.border-radius("3xl"); + background-color: var(--color-fd-card); + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%); +} + +.content { + display: flex; + flex-direction: column; + gap: theme.spacing(8); + height: 100%; + flex: 1; +} + +.mainContent { + display: flex; + flex-direction: column; + gap: theme.spacing(12); + flex: 1; +} + +.header { + display: flex; + flex-direction: column; + gap: theme.spacing(3); + min-height: 7rem; // 88px - fixed height to keep FEATURES at consistent position + height: 7rem; // 88px - fixed height +} + +.title { + margin: 0; + font-size: 1.875rem; // 30px + font-weight: 600; + line-height: 1; // 30px + letter-spacing: -0.03em; // -0.9px + color: var(--color-fd-foreground); + white-space: nowrap; + text-overflow: ellipsis; + flex-shrink: 0; +} + +.description { + margin: 0; + font-size: 1rem; // 16px + font-weight: 400; + line-height: 1.65; + letter-spacing: -0.01em; // -0.16px + color: var(--color-fd-foreground); + opacity: 0.9; + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + flex: 1; +} + +.featuresSection { + display: flex; + flex-direction: column; + gap: theme.spacing(6); + min-height: 12rem; + max-height: 12rem; +} + +.quickLinksSection { + display: flex; + flex-direction: column; + gap: theme.spacing(6); +} + +.sectionLabel { + margin: 0; + font-size: 0.6875rem; + font-weight: 500; + line-height: 1.82; + letter-spacing: -0.01em; + color: var(--color-fd-foreground); + opacity: 0.5; + text-transform: uppercase; + white-space: pre; +} + +.features { + display: flex; + flex-direction: column; + gap: theme.spacing(2); +} + +.featureItem { + position: relative; + display: flex; + align-items: center; + gap: theme.spacing(2); + padding: 0.3125rem 0.8125rem 0.3125rem 0.5rem; + border: 1px solid var(--color-fd-border); + border-radius: 1.3125rem; // 21px + background-color: var(--color-fd-card); + justify-content: flex-start; + box-shadow: 0 0 4px 0 rgb(255 255 255 / 25%) inset; +} + +.featureIcon { + display: flex; + align-items: center; + justify-content: center; + width: 0.781rem; // 12.5px + height: 0.781rem; // 12.5px + flex-shrink: 0; + color: var(--color-fd-foreground); +} + +.featureLabel { + font-size: 0.875rem; // 14px + font-weight: 600; + line-height: 1.65; + letter-spacing: -0.01em; // -0.14px + color: var(--color-fd-foreground); + white-space: pre; +} + +.featureShadow { + position: absolute; + inset: 0; + pointer-events: none; + box-shadow: inset 0 0 4px 0 rgb(255 255 255 / 25%); + border-radius: 1.32rem; // 21px +} + +.quickLinks { + display: flex; + flex-direction: column; + gap: theme.spacing(4); +} + +.quickLink { + font-size: 1rem; // 16px + font-weight: 500; + line-height: 1.25; // 20px + letter-spacing: -0.01em; // -0.16px + color: var(--color-fd-foreground); + text-decoration: underline; + text-decoration-skip-ink: none; + text-underline-position: from-font; + text-underline-offset: 0.1875rem; // 3px + transition: opacity 200ms ease-out; + + &:hover { + opacity: 0.7; + } +} + +.buttonWrapper { + margin-top: auto; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.button { + padding: 0.625rem; // 10px + border-radius: 0.5rem; // 8px + background-color: #27253d; + color: #f8f9fc; + font-size: 1rem; // 16px + font-weight: 500; + line-height: 1.25; // 20px + letter-spacing: -0.01em; // -0.16px +} diff --git a/apps/developer-hub/src/components/ProductCard/index.tsx b/apps/developer-hub/src/components/ProductCard/index.tsx new file mode 100644 index 0000000000..b8c2043d70 --- /dev/null +++ b/apps/developer-hub/src/components/ProductCard/index.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { Lightning } from "@phosphor-icons/react/dist/ssr"; +import { Button } from "@pythnetwork/component-library/Button"; +import { clsx } from "clsx"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import type { ReactNode } from "react"; + +import styles from "./index.module.scss"; + +type Feature = { + label: string; + icon?: ReactNode; +}; + +type QuickLink = { + label: string; + href: string; +}; + +type ProductCardProps = { + title: string; + description?: string; + icon?: ReactNode; + features?: Feature[]; + quickLinks?: QuickLink[]; + buttonLabel?: string; + buttonHref?: string; + external?: boolean; + className?: string; +}; + +export function ProductCard({ + title, + description, + icon, + features, + quickLinks, + buttonLabel, + buttonHref, + external, + className, +}: ProductCardProps) { + const router = useRouter(); + + const handleButtonClick = () => { + if (!buttonHref) return; + + if (external) { + window.open(buttonHref, "_blank", "noopener,noreferrer"); + } else { + router.push(buttonHref); + } + }; + + return ( +
+
+
+
+

{title}

+ {description &&

{description}

} + {icon &&
{icon}
} +
+ + {features && features.length > 0 && ( +
+

FEATURES

+
+ {features.map((feature) => ( +
+
+ {feature.icon ?? } +
+ {feature.label} +
+
+ ))} +
+
+ )} + + {quickLinks && quickLinks.length > 0 && ( +
+

QUICK LINKS

+
+ {quickLinks.map((link) => ( + + {link.label} + + ))} +
+
+ )} +
+ + {buttonLabel && ( +
+ +
+ )} +
+
+ ); +}