@zvk/marketing
v0.2.0
Published
Production-ready React sections for product-led public sites. The package provides semantic page structure, editorial typography, responsive product media, proof, pricing, comparison, lead, and conversion surfaces while inheriting the appearance system fr
Readme
@zvk/marketing
Production-ready React sections for product-led public sites. The package provides semantic page structure, editorial typography, responsive product media, proof, pricing, comparison, lead, and conversion surfaces while inheriting the appearance system from @zvk/ui.
Apps continue to own routing, content, analytics, forms, checkout, localization, image delivery, and viewport-triggered animation.
Install and styles
Import the shared styles in this order:
import "@zvk/ui/styles.css";
import "@zvk/themes/styles.css";
import "@zvk/marketing/styles.css";Use MarketingPageShell at the public-site root. Header and footer props render outside its single <main> landmark.
import {
BrowserFrame,
CTASection,
HeroSection,
MarketingFooter,
MarketingHeader,
MarketingPageShell,
StatsBand,
VideoFeature
} from "@zvk/marketing";
export function ProductPage() {
return (
<MarketingPageShell
header={
<MarketingHeader
brand={<a href="/">Northstar</a>}
navigation={
<nav aria-label="Primary navigation">
<a href="/product">Product</a>
<a href="/customers">Customers</a>
<a href="/pricing">Pricing</a>
</nav>
}
actions={<a href="/demo">Request a demo</a>}
/>
}
footer={<MarketingFooter brand="Northstar" />}
>
<HeroSection
actions={[
{ label: "Start a workspace", href: "/signup" },
{ label: "See customer results", href: "/customers", variant: "secondary" }
]}
description="Plan launches, collect approvals, and keep every release decision attached to the work."
layout="media-end"
media={
<BrowserFrame fit="contain" title="Release workspace" url="northstar.app/releases/spring">
<img alt="Release workspace with milestones, reviewers, and launch status" src="/product/release-workspace.png" />
</BrowserFrame>
}
title="A calmer way to run high-stakes product launches"
/>
<StatsBand
items={[
{ id: "cycle", value: "31%", label: "Shorter approval cycles" },
{ id: "handoffs", value: "2.4x", label: "Fewer status handoffs" },
{ id: "confidence", value: "94%", label: "Launches with named owners" }
]}
title="Evidence from the workflow"
/>
<VideoFeature
caption="A two-minute product walkthrough"
description="See how a release moves from draft to approved without losing decisions in chat."
playAction={<button type="button">Play product walkthrough</button>}
poster={<img alt="Release review screen before playback" src="/product/walkthrough-poster.png" />}
title="Follow one launch from brief to release"
/>
<CTASection
actions={[{ label: "Start a workspace", href: "/signup" }]}
description="Bring one active launch. Keep your existing tools."
title="Make the next release easier to trust"
/>
</MarketingPageShell>
);
}Composition contracts
- Every public component named
*Sectionowns its outer<section>, responsive container, spacing, tone, and heading contract. - Set
headingLevelon reusable sections; nested headings derive the next valid level. - Set
headingLevelonFeatureGridwhen it sits inside app-owned section copy; feature titles render at the next level. MarketingHeader.navigationandmobileNavigationare complete app-supplied navigation regions. Pass a labelled<nav>; the package does not wrap it in another landmark.- Surface tones are
default,subtle,brand, andcontrast. Status tones areinfo,success,warning, anddestructive. - Package-authored fallback text is customizable through each component's typed
labelsprop. - Media facades share
fit="contain" | "cover"andelevation="none" | "subtle" | "raised". Product screenshots default tocontain. VideoFeaturerenders no fake play control. Supply a semanticplayActionwhen a poster should start playback.- Comparison components retain one native table tree at every viewport and scroll inside a labelled region when space is constrained.
- Pricing, screenshot, logo, and integration grids derive the smallest useful column count from their content, then progressively reduce without jumping directly to one column.
Story recipes
Choose a page arc based on the decision visitors need to make.
Product-led
HeroSection with real media → LogoCloud → ProductStorySection → ScreenshotShowcase → VideoFeature → CTASection.
Proof-led
HeroSection → StatsBand → CaseStudyGrid → TestimonialGrid → AwardsStrip → CTASection.
Pricing-led
HeroSection → PricingSection → PlanComparisonTable → FAQSection → CTASection.
Prefer one strong product image, credible evidence, and varied content lengths over repeated card grids. Keep decorative backdrops rare and subordinate to the product or proof.
Component families
- Shell:
MarketingPageShell,MarketingHeader,MarketingFooter,AnnouncementBar. - Story:
HeroSection,MarketingSection,SectionHeader,FeatureGrid,FeatureSpotlight,BentoFeatureGrid,StepTimeline,UseCaseGrid,ProductStorySection. - Proof:
LogoCloud,StatsBand,AwardsStrip,IntegrationLogoGrid,CaseStudyGrid,TestimonialGrid. - Media:
HeroMediaFrame,BrowserFrame,DeviceFrame,ProductMockupFrame,ScreenshotShowcase,VideoFeature. - Decision:
PricingSection,ComparisonMatrix,PlanComparisonTable,FAQSection. - Conversion:
LeadCaptureShell,DemoRequestSection,CTASection.
Every component is available from the root barrel and a stable kebab-case subpath such as @zvk/marketing/hero-section. The stylesheet is exported from @zvk/marketing/styles.css.
Appearance and motion
Marketing CSS consumes semantic @zvk/ui border, elevation, material, motion, radius, and typography tokens. It follows the active appearance recipe without hardcoding a separate visual system.
Static content is static by default. State feedback uses semantic duration aliases, spatial animation is limited to transform and opacity, and reduced-motion preferences override decorative choices. Viewport entrance animation is app-owned and opt-in.
The stylesheet also supports increased contrast, forced colors, coarse-pointer targets, system reduced transparency, and the explicit data-zvk-ui-transparency="reduced" attribute.
Accessibility
- WCAG 2.2 AA is the baseline.
- Use meaningful image alternatives and labelled navigation landmarks.
- Keep focus order aligned with reading order; package focus indicators remain visible in forced colors.
- Native comparison headers remain available on narrow screens and at high zoom.
- Product media defaults to
containso screenshots are not silently cropped. - Supply real buttons or links through action slots; never make an entire card interactive when it contains secondary actions.
App-owned boundaries
Consuming apps own route components, CMS schemas, localization content, image optimization, video providers and consent, analytics, experiments, forms and validation, CRM routing, checkout, billing, entitlements, SEO metadata, and viewport observers.
Breaking migration
See MIGRATION.md for the coordinated 0.2.0 API and default changes, including search-and-replace examples.
Verification
bun run --filter @zvk/marketing build
bun run --filter @zvk/marketing typecheck
bun run --filter @zvk/marketing test
bun run --filter @zvk/marketing test:ssr
bun run --filter @zvk/marketing test:types
bun run --filter @zvk/marketing test:exports
bun run --filter @zvk/marketing verify:style-contract
bun run --filter @zvk/marketing validate:readme
bun run --filter @zvk/marketing validate:exports
bun run --filter @zvk/marketing tarball:inspect