Patterns

Marketing

Eleven bands that stack into a public site: navbar, hero, logo cloud, features, stats, testimonials, pricing, FAQ, newsletter, CTA and footer. A marketing page is the one screen in this section that is not an application, and it is built accordingly.

The other pattern pages are mostly composites wired together. This one is mostly layout — and that is the correct answer, not a gap. A visitor who has never signed in needs a headline and a price, not a data table.

Where the package stops

Worth being explicit, because the temptation on a page like this is to reach for a composite that was designed for a different job. Every band below leans on the package for structure, surface and colour; the arrangement is yours.

From @comitor/ui

  • NavigationMenu and Sheet — the desktop and mobile navs
  • PageContainer — one grid shared with the app screens
  • ComitorLockup — the brand mark, never a re-typed wordmark (from @comitor/ui/shell)
  • Card, Badge, Button, Accordion
  • ToggleGroup — the billing switch
  • InputGroup + Field — the email capture
  • LetterAvatar — testimonial authors

Hand-built, on purpose

  • The hero, the logo cloud and the CTA band — layout and copy, nothing reusable in them
  • The stats band: StatCard is a dashboard tile with a delta and a loading state, which a headline numeral has no use for
  • The star rating — no rating component ships, and one row of glyphs does not justify inventing one
  • Customer logos, which are assets rather than UI

Navbar

Sticky bar with the lockup, a NavigationMenu whose own documentation names public sites as its job, and the two account actions. Below md the same links move into a Sheet. Both previews are live — open the Product menu, then open the sheet.

Two traps in that file. SheetContent defaults closeLabel to the Vietnamese "Đóng", and that string is the accessible name of the close button — pass an English one. And NavigationMenuLink renders a plain anchor unless you hand it asChild with next/link; the package deliberately never imports next/* so the entry keeps resolving outside Next.

Hero

Badge, headline, one sentence, and a primary + secondary pair. The container is PageContainer at width="md" — the same component the product screens use, which is what keeps the two sides of the funnel on one grid.

Now in beta

Design systems that ship.

Components, patterns and tokens in one package — so the third screen costs what the first one did.

The code sample uses Button asChild so the CTA is a real link. Note what that costs: Radix Slot accepts exactly one child, so leftIcon and rightIcon are dropped without warning — the arrow has to move inside the <Link>. The preview above keeps rightIcon precisely because its buttons go nowhere.

Logo cloud

Social proof directly under the hero. Pure layout: the package ships no logo component and should not, since customer marks are assets on a CDN rather than UI.

Trusted by teams at world-class companies

  • Acme
  • Globex
  • Initech
  • Umbrella
  • Stark
  • Wayne

Features grid

Three benefits as real Cards rather than bordered <div>s. That single swap buys the surface, radius, border and shadow tokens, so these cards age with the system instead of drifting from it.

Ship faster

Fifty-plus components and eleven screen patterns, already assembled.

Stay consistent

One token contract across web, email and print. Contrast is measured, not guessed.

Scale easily

Six entry points, tree-shaken. Take the button, leave the charts.

The icon chip follows the colour contract the rest of the system uses: the accent tints the background (bg-primary/15) and the glyph sits on it at text-foreground. Gold-on-gold measures about 1.7:1 against the 3:1 that WCAG 1.4.11 asks of graphical objects.

Stats band

Four figures with short labels. This is the clearest case on the page for not reaching for a composite — the reasoning is in the code sample, and it is worth reading before the next time a marketing band tempts you toward a dashboard component.

Active developers
40k+
Components rendered
120M
Uptime SLA
99.9%
Average rating
4.9/5

Marked up as a description list so each figure keeps its label as data, and set in tabular-nums so the four columns line up on the digits.

Testimonials

Quote, rating, attribution. Authors use LetterAvatar, which derives both the initials and the colour behind them from the name — so the same person is the same colour here, in the app and in an exported PDF.

Rated 5 out of 5
Comitor cut our design-to-ship time in half. The tokens just work everywhere.
Sarah ChenSarah ChenHead of Design, Globex
Rated 5 out of 5
The most polished component library we have used. Accessibility is first-class.
Marcus WebbMarcus WebbEngineering Lead, Initech
Rated 5 out of 5
We replaced three internal libraries with Comitor and never looked back.
Priya NairPriya NairCTO, Stark

A quote wants <figure> semantics and Card renders a <div>, so the card look arrives through the exported cardVariants() instead. Reach for the variant function whenever you need a component's appearance on a different element — copying its class string is how two cards end up one pixel apart.

Pricing table

Three tiers with a working billing switch — flip it and the Pro column moves from $29 to $23. Both columns are per user per month; the discount is the unit price, never a yearly total dressed up as one.

Starter

For side projects and prototypes.

Free

  • All components
  • Design tokens
  • Community support
Most popular

Pro

For product teams shipping weekly.

$29/user/mo

  • Everything in Starter
  • Private component registry
  • Priority support
  • Figma sync

Enterprise

For regulated and multi-brand orgs.

Custom

  • Everything in Pro
  • Custom token themes
  • SSO and audit log
  • 99.9% uptime SLA

The recommended tier is signalled three ways — Badge variant="primary", a filled button and a border-primary-ink ring — because gold on a white card measures 1.86:1 and cannot carry the meaning alone. Note also the onValueChange guard: Radix ToggleGroup emits an empty string when you press the active item again, and without the guard the table would render a plan with no price.

FAQ

Objection handling immediately before the last conversion step. Accordion with type="single" and collapsible: one answer open at a time, and it can be closed again.

Newsletter signup

For the visitor who is not ready to convert. Live: submit an empty or malformed address to see the error, a valid one to see the confirmation.

Stay in the loop

Release notes and design essays. Roughly monthly.

InputGroup puts the icon and the submit button inside the field's own border, so one control reads as one target. Field carries the invalid state for the row and FieldError renders role="alert", which is what makes the message audible rather than merely red. noValidate hands validation to your code so the copy is yours and not the browser's.

Call to action

The last band before the footer: brand ink, gold headline, one button. It is the only surface on the page that does not follow the theme, because a brand band is a brand band in both.

Ready to ship faster?

Join the teams building with clarity and shipping with confidence.

No re-coloured button here. The default gold Button is already correct on ink, and a ring-white/10 keeps the band from dissolving into the near-black page background in dark mode.

Footer

Brand mark, three link columns, legal bar. The mark is ComitorLockup at variant="auto", which follows the theme and keeps the gold rule and slogan at their brand-book proportions — a re-typed wordmark gets none of that.

Design systems that ship.

Product

  • Features
  • Pricing
  • Changelog
  • Roadmap

Resources

  • Docs
  • Guides
  • Support
  • Status

Company

  • About
  • Blog
  • Careers
  • Contact

© 2026 Comitor. All rights reserved.

PrivacyTerms

Composing the page

Each band is one file exporting one section; the route stacks them. Note where 'use client' lives — on the three sections that own state, never on the route.

app/page.tsx
import type { Metadata } from 'next'
import { SiteHeader } from '@/components/marketing/site-header'
import { Hero } from '@/components/marketing/hero'
import { LogoCloud } from '@/components/marketing/logo-cloud'
import { Features } from '@/components/marketing/features'
import { Stats } from '@/components/marketing/stats'
import { Testimonials } from '@/components/marketing/testimonials'
import { Pricing } from '@/components/marketing/pricing'
import { Faq } from '@/components/marketing/faq'
import { Newsletter } from '@/components/marketing/newsletter'
import { CallToAction } from '@/components/marketing/call-to-action'
import { SiteFooter } from '@/components/marketing/site-footer'

export const metadata: Metadata = {
  title: 'Comitor — design systems that ship',
  description: 'Components, patterns and tokens in one package.',
}

/*
 * NO 'use client' on the page. Only three sections here own state — the header's
 * sheet and nav menu, the pricing toggle, the newsletter form — and each of those
 * files carries the directive itself. Hoisting it up here would ship the hero,
 * the logo cloud, the stats and the footer to the browser to do nothing.
 *
 * Every band is <section> + PageContainer: the section paints edge-to-edge, the
 * container clamps and pads the content. Same container the app screens use, so
 * the marketing site and the product share one left margin.
 */
export default function HomePage() {
  return (
    <>
      <SiteHeader />
      <main>
        <Hero />
        <LogoCloud />
        <Features />
        <Stats />
        <Testimonials />
        <Pricing />
        <Faq />
        <Newsletter />
        <CallToAction />
      </main>
      <SiteFooter />
    </>
  )
}

Best practices

Do

  • Wrap every band in PageContainer so the marketing site and the product share a left margin.
  • Keep one primary CTA per band; the secondary action is variant="outline".
  • Push 'use client' down to the sections that need it — most of this page is static.
  • Signal the recommended plan with badge, button and border together.
  • Give every composite with a labels or label prop English strings; the package defaults are Vietnamese.

Don't

  • Do not re-colour Button or Badge to fit a band — change the band.
  • Do not reach for an app composite because it is nearby; StatCard is not a stats band.
  • Do not mix billing units in one table without a toggle that switches both columns.
  • Do not re-type the wordmark, or rebuild the lockup out of a logo and a <span>.
  • Do not build class names from data (bg-${tone}-300) — Tailwind scans statically and purges what it cannot see.

Accessibility

  • One <h1> per page — the hero headline. Every band opens at <h2>, including the ones whose heading is sr-only, so the outline survives even where the design has no visible title. (The previews on this page use <h3> to sit under the documentation's own outline.)
  • Each band is a <section> named by aria-labelledby pointing at its heading — a <section> without an accessible name is not a landmark at all.
  • Decorative glyphs are aria-hidden; meaning that lives in a glyph is spelled out. Five gold stars carry a rating, so the rating is also written as sr-only text.
  • Accent colour tints backgrounds and never paints a glyph that sits on its own tint: gold-on-gold measures about 1.7:1 where 1.4.11 requires 3:1.
  • Pass closeLabel to SheetContent and an aria-label to every icon-only button — the menu trigger has no text of its own.
  • FieldError is role="alert" and the input points at it with aria-describedby, so the failure is announced and reachable, not just red.
  • CTAs say what they do. “Start 14-day trial” reads correctly in a list of links; “Learn more” five times does not.
  • Tab order follows reading order in every band, and the sticky navbar sits at z-40 — give anchor targets a matching scroll-margin-top or the bar will cover them.