Blocks
Marketing
Hero, features, pricing, testimonials, CTA, FAQ, footer.
Simple hero
Centered hero with announcement badge, headline, supporting copy, CTA pair and a keyboard hint.
Ship beautiful interfaces in half the time
Ninety production-ready React components with theming, dark mode and accessibility built in. Copy a block, wire up your data, ship it.
Press CtrlK to explore the docsimport { Badge, Button, Heading, Kbd, Paragraph, Text } from "@zephora/react";
export function HeroSimple() {
return (
<section
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
textAlign: "center",
gap: 20,
padding: "56px 24px",
maxWidth: 760,
margin: "0 auto",
}}
>
<Badge variant="soft" size="md">Announcing Zephora 2.0</Badge>
<Heading level={1} size="4xl" style={{ margin: 0, maxWidth: 640 }}>
Ship beautiful interfaces in half the time
</Heading>
<Paragraph muted size="lg" leading="relaxed" style={{ margin: 0, maxWidth: 560 }}>
Ninety production-ready React components with theming, dark mode and
accessibility built in. Copy a block, wire up your data, ship it.
</Paragraph>
<div style={{ display: "flex", gap: 12, flexWrap: "wrap", justifyContent: "center" }}>
<Button size="lg">Get started free</Button>
<Button size="lg" variant="outline">View components</Button>
</div>
<Text muted size="sm">
Press <Kbd keys={["Ctrl", "K"]} size="sm" /> to explore the docs
</Text>
</section>
);
}Split hero with chart
Two-column hero pairing headline, CTAs and avatar social proof with an elevated analytics card.
Analytics that respect your users
Privacy-first product analytics with sub-second queries. No cookies, no fingerprinting — just the numbers you need to grow.
import {
Avatar,
AvatarGroup,
Badge,
Button,
Card,
Chart,
Heading,
Paragraph,
Text,
} from "@zephora/react";
export function HeroSplit() {
return (
<section
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
gap: 40,
alignItems: "center",
padding: "40px 24px",
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: 20, alignItems: "flex-start" }}>
<Badge variant="outline" color="info">Now with real-time dashboards</Badge>
<Heading level={1} size="4xl" style={{ margin: 0 }}>
Analytics that respect your users
</Heading>
<Paragraph muted size="lg" leading="relaxed" style={{ margin: 0 }}>
Privacy-first product analytics with sub-second queries. No cookies,
no fingerprinting — just the numbers you need to grow.
</Paragraph>
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
<Button size="lg">Start free trial</Button>
<Button size="lg" variant="ghost">Live demo</Button>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
<AvatarGroup max={4} size="sm">
<Avatar name="Mia Ford" />
<Avatar name="Leo Park" />
<Avatar name="Ana Silva" />
<Avatar name="Tom Weber" />
<Avatar name="Zoe Lang" />
<Avatar name="Raj Patel" />
</AvatarGroup>
<Text muted size="sm">Loved by 2,400+ developers</Text>
</div>
</div>
<Card variant="elevated" padding="lg">
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
<Text weight="semibold" size="sm">Weekly active users</Text>
<Badge variant="soft" color="success" size="sm">+18.2%</Badge>
</div>
<Chart
type="area"
width={420}
height={200}
showLegend={false}
data={{
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
datasets: [
{ label: "Active users", data: [120, 180, 160, 240, 310, 290, 380] },
],
}}
/>
</div>
</Card>
</section>
);
}Feature grid
Section heading with six feature cards in a responsive grid, each led by an icon tile.
Everything you need to build modern UIs
A complete toolkit — from low-level primitives to full page blocks.
Fast by default
Zero-runtime CSS and tree-shakable components keep bundles lean and interactions instant.
Accessible out of the box
WAI-ARIA compliant primitives with keyboard navigation and focus management built in.
Ready for every locale
RTL layouts and 30+ locale packs ship with every component — no extra wiring required.
Composable primitives
Headless mode drops the styling layer entirely so your design system stays in control.
TypeScript native
Strict types, autocomplete-friendly props and exhaustive variant unions on every API.
Themeable tokens
Swap a handful of CSS variables to rebrand every component in your product at once.
import { Badge, Card, Heading, Paragraph, Text } from "@zephora/react";
const features = [
{
icon: "M13 2 3 14h7l-1 8 11-13h-7l1-7Z",
title: "Fast by default",
description:
"Zero-runtime CSS and tree-shakable components keep bundles lean and interactions instant.",
},
{
icon: "M12 3 4.5 6v5c0 4.6 3 8.7 7.5 10 4.5-1.3 7.5-5.4 7.5-10V6L12 3Z",
title: "Accessible out of the box",
description:
"WAI-ARIA compliant primitives with keyboard navigation and focus management built in.",
},
{
icon: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm-9 9h18M12 3c2.5 2.6 3.8 5.7 3.8 9s-1.3 6.4-3.8 9c-2.5-2.6-3.8-5.7-3.8-9S9.5 5.6 12 3Z",
title: "Ready for every locale",
description:
"RTL layouts and 30+ locale packs ship with every component — no extra wiring required.",
},
{
icon: "m12 3 9 5-9 5-9-5 9-5Zm-9 9 9 5 9-5M3 16.5l9 5 9-5",
title: "Composable primitives",
description:
"Headless mode drops the styling layer entirely so your design system stays in control.",
},
{
icon: "m8 7-5 5 5 5m8-10 5 5-5 5",
title: "TypeScript native",
description:
"Strict types, autocomplete-friendly props and exhaustive variant unions on every API.",
},
{
icon: "M12 3v4m0 10v4M3 12h4m10 0h4M5.6 5.6l2.9 2.9m7 7 2.9 2.9m0-12.8-2.9 2.9m-7 7-2.9 2.9",
title: "Themeable tokens",
description:
"Swap a handful of CSS variables to rebrand every component in your product at once.",
},
];
export function FeatureGrid() {
return (
<section style={{ padding: "48px 24px" }}>
<div style={{ textAlign: "center", maxWidth: 620, margin: "0 auto 40px" }}>
<Badge variant="soft" size="sm">Why Zephora</Badge>
<Heading level={2} size="3xl" style={{ margin: "16px 0 12px" }}>
Everything you need to build modern UIs
</Heading>
<Paragraph muted size="lg" style={{ margin: 0 }}>
A complete toolkit — from low-level primitives to full page blocks.
</Paragraph>
</div>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
gap: 20,
}}
>
{features.map((feature) => (
<Card key={feature.title} padding="lg">
<div
style={{
width: 40,
height: 40,
borderRadius: 10,
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "var(--z-primary-soft)",
color: "var(--z-primary)",
}}
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d={feature.icon} />
</svg>
</div>
<Heading level={4} size="md" style={{ margin: "16px 0 8px" }}>
{feature.title}
</Heading>
<Text as="p" muted size="sm" style={{ margin: 0, lineHeight: 1.6 }}>
{feature.description}
</Text>
</Card>
))}
</div>
</section>
);
}Stats band
Four key metrics with trend arrows, separated by vertical dividers inside a card band.
import { Card, Divider, Statistic } from "@zephora/react";
export function StatsBand() {
return (
<section style={{ padding: "40px 24px" }}>
<Card padding="lg">
<div
style={{
display: "flex",
flexWrap: "wrap",
alignItems: "stretch",
justifyContent: "space-between",
gap: 24,
}}
>
<Statistic size="lg" title="Active users" value="48.2K" style={{ flex: "1 1 140px" }} />
<Divider orientation="vertical" />
<Statistic
size="lg"
title="Uptime"
value="99.99"
suffix="%"
trend="up"
style={{ flex: "1 1 140px" }}
/>
<Divider orientation="vertical" />
<Statistic
size="lg"
title="P95 latency"
value={87}
suffix="ms"
trend="down"
style={{ flex: "1 1 140px" }}
/>
<Divider orientation="vertical" />
<Statistic size="lg" title="GitHub stars" value="12.4K" style={{ flex: "1 1 140px" }} />
</div>
</Card>
</section>
);
}Pricing table
Three pricing tiers with a monthly/yearly billing toggle; the popular plan is elevated and badged.
Simple, transparent pricing
Start free, upgrade when your team grows. No hidden fees.
$0
per user / monthFor personal projects and quick experiments.
- Up to 3 projects
- Community support
- Basic analytics
- 1 GB storage
$19
per user / monthFor growing teams that need more power.
- Unlimited projects
- Priority support
- Advanced analytics
- 100 GB storage
- Custom domains
$49
per user / monthFor organizations with advanced needs.
- Everything in Pro
- SSO / SAML
- Audit logs
- Dedicated success manager
- 99.99% uptime SLA
import * as React from "react";
import {
Badge,
Button,
Card,
Heading,
Paragraph,
Text,
ToggleGroup,
ToggleGroupItem,
} from "@zephora/react";
const plans = [
{
name: "Starter",
monthly: "$0",
yearly: "$0",
blurb: "For personal projects and quick experiments.",
cta: "Start for free",
popular: false,
features: ["Up to 3 projects", "Community support", "Basic analytics", "1 GB storage"],
},
{
name: "Pro",
monthly: "$19",
yearly: "$15",
blurb: "For growing teams that need more power.",
cta: "Start free trial",
popular: true,
features: [
"Unlimited projects",
"Priority support",
"Advanced analytics",
"100 GB storage",
"Custom domains",
],
},
{
name: "Enterprise",
monthly: "$49",
yearly: "$39",
blurb: "For organizations with advanced needs.",
cta: "Contact sales",
popular: false,
features: [
"Everything in Pro",
"SSO / SAML",
"Audit logs",
"Dedicated success manager",
"99.99% uptime SLA",
],
},
];
export function PricingTable() {
const [billing, setBilling] = React.useState("monthly");
const yearly = billing === "yearly";
return (
<section style={{ padding: "48px 24px" }}>
<div style={{ textAlign: "center", maxWidth: 560, margin: "0 auto 28px" }}>
<Heading level={2} size="3xl" style={{ margin: "0 0 12px" }}>
Simple, transparent pricing
</Heading>
<Paragraph muted size="lg" style={{ margin: 0 }}>
Start free, upgrade when your team grows. No hidden fees.
</Paragraph>
</div>
<div style={{ display: "flex", justifyContent: "center", marginBottom: 32 }}>
<ToggleGroup
type="single"
value={billing}
onValueChange={(value) => {
if (typeof value === "string" && value) setBilling(value);
}}
aria-label="Billing period"
>
<ToggleGroupItem value="monthly">Monthly</ToggleGroupItem>
<ToggleGroupItem value="yearly">Yearly · save 20%</ToggleGroupItem>
</ToggleGroup>
</div>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(230px, 1fr))",
gap: 20,
alignItems: "stretch",
}}
>
{plans.map((plan) => (
<Card
key={plan.name}
variant={plan.popular ? "elevated" : "outline"}
padding="lg"
style={{
display: "flex",
flexDirection: "column",
gap: 16,
...(plan.popular ? { border: "1px solid var(--z-primary)" } : undefined),
}}
>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
<Text weight="semibold">{plan.name}</Text>
{plan.popular && <Badge size="sm">Most popular</Badge>}
</div>
<div>
<Heading level={3} size="3xl" style={{ margin: 0 }}>
{yearly ? plan.yearly : plan.monthly}
</Heading>
<Text muted size="sm">
per user / month{yearly ? ", billed yearly" : ""}
</Text>
</div>
<Text as="p" muted size="sm" style={{ margin: 0 }}>
{plan.blurb}
</Text>
<ul
style={{
listStyle: "none",
margin: 0,
padding: 0,
display: "flex",
flexDirection: "column",
gap: 10,
flex: 1,
}}
>
{plan.features.map((feature) => (
<li key={feature} style={{ display: "flex", gap: 8, alignItems: "flex-start" }}>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="var(--z-success)"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
style={{ flexShrink: 0, marginTop: 3 }}
>
<path d="M20 6 9 17l-5-5" />
</svg>
<Text size="sm">{feature}</Text>
</li>
))}
</ul>
<Button fullWidth variant={plan.popular ? "solid" : "outline"}>
{plan.cta}
</Button>
</Card>
))}
</div>
</section>
);
}Testimonials
Three quote cards with five-star ratings and avatar/name/role attribution rows.
Loved by teams everywhere
Here is what people building with Zephora have to say.
“Zephora replaced three internal component libraries in a single quarter. Our designers and engineers finally speak the same language.”
“The accessibility work is genuinely best-in-class. Screen-reader flows that used to take us weeks now come for free.”
“We shipped our v2 dashboard in six weeks instead of six months. The headless mode made theming completely painless.”
import { Avatar, Card, Heading, Paragraph, Rating, Text } from "@zephora/react";
const testimonials = [
{
name: "Sofia Reyes",
role: "Design Lead, Northwind",
quote:
"Zephora replaced three internal component libraries in a single quarter. Our designers and engineers finally speak the same language.",
},
{
name: "Marcus Chen",
role: "Frontend Engineer, Lumen",
quote:
"The accessibility work is genuinely best-in-class. Screen-reader flows that used to take us weeks now come for free.",
},
{
name: "Amelia Novak",
role: "CTO, Fieldstone",
quote:
"We shipped our v2 dashboard in six weeks instead of six months. The headless mode made theming completely painless.",
},
];
export function Testimonials() {
return (
<section style={{ padding: "48px 24px" }}>
<div style={{ textAlign: "center", maxWidth: 560, margin: "0 auto 36px" }}>
<Heading level={2} size="3xl" style={{ margin: "0 0 12px" }}>
Loved by teams everywhere
</Heading>
<Paragraph muted size="lg" style={{ margin: 0 }}>
Here is what people building with Zephora have to say.
</Paragraph>
</div>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
gap: 20,
alignItems: "stretch",
}}
>
{testimonials.map((item) => (
<Card
key={item.name}
padding="lg"
style={{ display: "flex", flexDirection: "column", gap: 14 }}
>
<Rating readOnly value={5} size="sm" />
<Paragraph size="sm" leading="relaxed" style={{ margin: 0, flex: 1 }}>
“{item.quote}”
</Paragraph>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<Avatar name={item.name} size="sm" />
<div>
<Text as="div" size="sm" weight="semibold">{item.name}</Text>
<Text as="div" muted size="xs">{item.role}</Text>
</div>
</div>
</Card>
))}
</div>
</section>
);
}FAQ accordion
Centered section heading above a single-open collapsible accordion with five Q&A items.
Frequently asked questions
Everything you need to know. Can’t find an answer? Ping our team.
Yes. Every component and block is MIT licensed — use them in unlimited personal and commercial projects with no attribution required.
All styling flows through a small set of CSS variables. Override the tokens once at the root and every component — including dark mode — updates automatically.
Absolutely. Pass the unstyled prop (or set it globally) to drop the built-in styles, then compose the headless primitives with your own Tailwind classes.
Every interactive component follows the WAI-ARIA authoring practices: full keyboard support, focus management, and screen-reader labelling out of the box.
Community support is available to everyone on GitHub and Discord. Pro and Enterprise plans add priority email support with a guaranteed response time.
import { Accordion, AccordionItem, Heading, Paragraph } from "@zephora/react";
const faqs = [
{
value: "license",
question: "Can I use Zephora in commercial projects?",
answer:
"Yes. Every component and block is MIT licensed — use them in unlimited personal and commercial projects with no attribution required.",
},
{
value: "theming",
question: "How does theming work?",
answer:
"All styling flows through a small set of CSS variables. Override the tokens once at the root and every component — including dark mode — updates automatically.",
},
{
value: "tailwind",
question: "Does it work with Tailwind CSS?",
answer:
"Absolutely. Pass the unstyled prop (or set it globally) to drop the built-in styles, then compose the headless primitives with your own Tailwind classes.",
},
{
value: "a11y",
question: "Are the components accessible?",
answer:
"Every interactive component follows the WAI-ARIA authoring practices: full keyboard support, focus management, and screen-reader labelling out of the box.",
},
{
value: "support",
question: "What kind of support is included?",
answer:
"Community support is available to everyone on GitHub and Discord. Pro and Enterprise plans add priority email support with a guaranteed response time.",
},
];
export function FaqSection() {
return (
<section style={{ maxWidth: 680, margin: "0 auto", padding: "48px 24px" }}>
<div style={{ textAlign: "center", marginBottom: 32 }}>
<Heading level={2} size="3xl" style={{ margin: "0 0 12px" }}>
Frequently asked questions
</Heading>
<Paragraph muted size="lg" style={{ margin: 0 }}>
Everything you need to know. Can’t find an answer? Ping our team.
</Paragraph>
</div>
<Accordion type="single" collapsible defaultValue="license">
{faqs.map((faq) => (
<AccordionItem key={faq.value} value={faq.value} title={faq.question}>
<Paragraph muted size="sm" leading="relaxed" style={{ margin: 0 }}>
{faq.answer}
</Paragraph>
</AccordionItem>
))}
</Accordion>
</section>
);
}Footer
Multi-column footer with brand blurb, link columns, and a bottom bar with copyright and social icon buttons.
import { Divider, IconButton, Paragraph, Text } from "@zephora/react";
const columns = [
{ title: "Product", links: ["Components", "Blocks", "Themes", "Changelog"] },
{ title: "Company", links: ["About", "Blog", "Careers", "Contact"] },
{ title: "Legal", links: ["Privacy", "Terms", "License"] },
];
const socials = [
{
label: "GitHub",
path: "M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.55v-2.17c-3.2.7-3.87-1.36-3.87-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.03 1.75 2.69 1.25 3.34.95.1-.74.4-1.25.72-1.54-2.55-.29-5.23-1.28-5.23-5.68 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.46.11-3.05 0 0 .96-.31 3.16 1.18a11 11 0 0 1 5.76 0c2.2-1.49 3.16-1.18 3.16-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.83 1.18 3.09 0 4.41-2.69 5.38-5.25 5.66.41.36.78 1.06.78 2.14v3.17c0 .3.2.66.8.55A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5Z",
},
{
label: "X (Twitter)",
path: "M18.9 2H22l-7.7 8.8L23 22h-6.8l-5.3-6.9L4.8 22H1.7l8.2-9.4L1 2h7l4.8 6.3L18.9 2Zm-1.2 18h1.9L7 3.9H5L17.7 20Z",
},
{
label: "LinkedIn",
path: "M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3 9h4v12H3V9Zm7 0h3.8v1.7h.05c.53-1 1.83-2.05 3.77-2.05 4.03 0 4.78 2.65 4.78 6.1V21h-4v-5.5c0-1.31-.02-3-1.83-3-1.83 0-2.11 1.43-2.11 2.9V21h-4V9Z",
},
];
export function Footer() {
return (
<footer style={{ padding: "48px 24px 24px" }}>
<div style={{ display: "flex", flexWrap: "wrap", gap: 40, justifyContent: "space-between" }}>
<div style={{ display: "flex", flexDirection: "column", gap: 12, maxWidth: 260 }}>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<div
aria-hidden="true"
style={{
width: 28,
height: 28,
borderRadius: 8,
background: "linear-gradient(135deg, var(--z-primary), var(--z-info))",
}}
/>
<Text weight="bold" size="lg">Zephora</Text>
</div>
<Paragraph muted size="sm" style={{ margin: 0 }}>
The component library for teams that ship. Built with care in the
open, MIT licensed forever.
</Paragraph>
</div>
<div style={{ display: "flex", gap: 48, flexWrap: "wrap" }}>
{columns.map((column) => (
<nav key={column.title} aria-label={column.title}>
<Text as="div" size="sm" weight="semibold" style={{ marginBottom: 12 }}>
{column.title}
</Text>
<ul
style={{
listStyle: "none",
margin: 0,
padding: 0,
display: "flex",
flexDirection: "column",
gap: 8,
}}
>
{column.links.map((link) => (
<li key={link}>
<a href="#" style={{ textDecoration: "none" }}>
<Text muted size="sm">{link}</Text>
</a>
</li>
))}
</ul>
</nav>
))}
</div>
</div>
<Divider spacing={8} />
<div
style={{
display: "flex",
flexWrap: "wrap",
alignItems: "center",
justifyContent: "space-between",
gap: 16,
}}
>
<Text muted size="sm">© 2026 Zephora Labs, Inc. All rights reserved.</Text>
<div style={{ display: "flex", gap: 8 }}>
{socials.map((social) => (
<IconButton
key={social.label}
variant="ghost"
shape="circle"
aria-label={social.label}
icon={
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d={social.path} />
</svg>
}
/>
))}
</div>
</div>
</footer>
);
}