Pricing — <mv-pricing>

Pricing section styled like a spec sheet: plan panels set in one muted frame, a segmented monthly / yearly switch on native radios, a ring-marked recommended plan, usage meters and an explicit struck-through saving on yearly prices; as columns, horizontal rows or a single plan.

CategoryBlocks
TypeBlock (ready-made section) (<mv-pricing>)
Statusstable
Also installsbadge, button
Keywordspricing, tarifs, plans, abonnement, subscription, monthly, yearly, billing, landing

When to use

Install

node scripts/add.mjs pricing --out ./src/marvelous

AI agent with the Marvelous UI MCP server: install_components({ slugs: ["pricing"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Files copied (dependencies included): tokens/tokens.css, core/base.css, core/element.js, components/pricing/pricing.css, components/pricing/pricing.js, components/badge/badge.css, components/button/button.css.

Usage

Canonical markup — start from it and customize with attributes, data-* and CSS variables:

<mv-tabs id="pricing-demo" value="tiers" data-size="sm" style="gap:0;background:var(--mv-bg)">
  <style>#pricing-demo > [role="tablist"] { justify-self: center; margin: .75rem auto 0; }</style>
  <div role="tablist" aria-label="Block variant">
    <button role="tab" data-value="tiers">Plans</button>
    <button role="tab" data-value="rows">Rows</button>
    <button role="tab" data-value="single">Single plan</button>
  </div>

  <!-- ═════════ Plans grid ═════════ -->
  <div role="tabpanel" data-value="tiers">
    <mv-pricing>
      <section class="mv-pricing" aria-labelledby="pr-title">
        <div class="mv-pricing-inner">
          <header class="mv-pricing-header">
            <p class="mv-pricing-eyebrow">Pricing</p>
            <h2 class="mv-pricing-title" id="pr-title">Pay for the team you have today</h2>
            <p class="mv-pricing-lead">Start free, add members as you grow. Switch plans or cancel whenever you like.</p>
            <div class="mv-pricing-toggle" data-billing-toggle role="radiogroup" aria-label="Billing period">
              <label><input type="radio" name="pr-billing" value="monthly" checked>Monthly</label>
              <label><input type="radio" name="pr-billing" value="yearly">Yearly <span class="mv-badge" data-variant="success" data-size="sm">−20%</span></label>
            </div>
          </header>
          <div class="mv-pricing-plans">
            <article class="mv-pricing-plan" aria-labelledby="pr-solo">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="pr-solo">Solo</h3>
                <p class="mv-pricing-desc">For freelancers juggling a handful of clients.</p>
              </div>
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$0" data-yearly="$0">$0</span>
                <span class="mv-pricing-period">/ month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="Free forever" data-yearly="Free forever">Free forever</span>
              </p>
              <a class="mv-button" data-variant="outline" href="#pricing-demo">Get started for free</a>
              <div class="mv-pricing-meters">
                <div class="mv-pricing-meter" role="meter" aria-label="Active projects" aria-valuemin="0" aria-valuemax="25" aria-valuenow="3" aria-valuetext="3" style="--value:12%"><span aria-hidden="true">Active projects</span><span aria-hidden="true">3</span></div>
                <div class="mv-pricing-meter" role="meter" aria-label="Storage" aria-valuemin="0" aria-valuemax="25" aria-valuenow="2" aria-valuetext="2 GB" style="--value:8%"><span aria-hidden="true">Storage</span><span aria-hidden="true">2 GB</span></div>
              </div>
              <ul class="mv-pricing-features">
                <li>Unlimited boards and sprints</li>
                <li>Time tracking</li>
                <li>Basic client portal</li>
                <li data-off>Planning assistant</li>
                <li data-off>Automations</li>
              </ul>
            </article>
            <article class="mv-pricing-plan" data-highlight aria-labelledby="pr-team">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="pr-team">Team</h3>
                <span class="mv-badge mv-pricing-ribbon" data-variant="accent" data-size="sm">Recommended</span>
                <p class="mv-pricing-desc">For studios and product teams of up to 50 people.</p>
              </div>
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$24" data-yearly="$19">$24</span>
                <span class="mv-pricing-was" data-monthly="" data-yearly="$24" aria-hidden="true"></span>
                <span class="mv-pricing-period">/ member / month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="Billed monthly, no commitment" data-yearly="Billed $228 per member per year">Billed monthly, no commitment</span>
              </p>
              <a class="mv-button" href="#pricing-demo">Start 14-day free trial</a>
              <div class="mv-pricing-meters">
                <div class="mv-pricing-meter" role="meter" aria-label="Active projects" aria-valuemin="0" aria-valuemax="1" aria-valuenow="1" aria-valuetext="Unlimited" style="--value:100%"><span aria-hidden="true">Active projects</span><span aria-hidden="true">Unlimited</span></div>
                <div class="mv-pricing-meter" role="meter" aria-label="Storage" aria-valuemin="0" aria-valuemax="100" aria-valuenow="60" aria-valuetext="100 GB / member" style="--value:60%"><span aria-hidden="true">Storage</span><span aria-hidden="true">100 GB / member</span></div>
              </div>
              <ul class="mv-pricing-features">
                <li class="mv-pricing-features-title">Everything in Solo, plus:</li>
                <li>Unlimited projects and guests</li>
                <li>Planning assistant</li>
                <li>60 ready-made automations</li>
                <li>White-label client portal</li>
                <li>Weekly reports by email</li>
              </ul>
            </article>
            <article class="mv-pricing-plan" aria-labelledby="pr-ent">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="pr-ent">Enterprise</h3>
                <p class="mv-pricing-desc">For organizations with strict security requirements.</p>
              </div>
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$39" data-yearly="$31">$39</span>
                <span class="mv-pricing-was" data-monthly="" data-yearly="$39" aria-hidden="true"></span>
                <span class="mv-pricing-period">/ member / month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="From 50 members" data-yearly="Billed $372 per member per year">From 50 members</span>
              </p>
              <a class="mv-button" data-variant="secondary" href="#pricing-demo">Talk to sales</a>
              <div class="mv-pricing-meters">
                <div class="mv-pricing-meter" role="meter" aria-label="Active projects" aria-valuemin="0" aria-valuemax="1" aria-valuenow="1" aria-valuetext="Unlimited" style="--value:100%"><span aria-hidden="true">Active projects</span><span aria-hidden="true">Unlimited</span></div>
                <div class="mv-pricing-meter" role="meter" aria-label="Storage" aria-valuemin="0" aria-valuemax="1" aria-valuenow="1" aria-valuetext="Unlimited" style="--value:100%"><span aria-hidden="true">Storage</span><span aria-hidden="true">Unlimited</span></div>
              </div>
              <ul class="mv-pricing-features">
                <li class="mv-pricing-features-title">Everything in Team, plus:</li>
                <li>SAML SSO and SCIM provisioning</li>
                <li>Audit log, custom retention</li>
                <li>Regional data hosting</li>
                <li>99.99% uptime SLA</li>
                <li>Named account manager</li>
              </ul>
            </article>
          </div>
          <p class="mv-pricing-note">Prices exclude tax. Nonprofits and schools get 50% off. <a href="#pricing-demo">Compare every feature</a></p>
        </div>
      </section>
    </mv-pricing>
  </div>

  <!-- ═════════ Rows ═════════ -->
  <div role="tabpanel" data-value="rows">
    <mv-pricing>
      <section class="mv-pricing" data-variant="rows" aria-labelledby="prr-title">
        <div class="mv-pricing-inner">
          <header class="mv-pricing-header">
            <p class="mv-pricing-eyebrow">Pricing</p>
            <h2 class="mv-pricing-title" id="prr-title">Three plans, no surprises</h2>
            <p class="mv-pricing-lead">Every plan includes unlimited boards, sprints and time tracking.</p>
            <div class="mv-pricing-toggle" data-billing-toggle role="radiogroup" aria-label="Billing period">
              <label><input type="radio" name="prr-billing" value="monthly" checked>Monthly</label>
              <label><input type="radio" name="prr-billing" value="yearly">Yearly <span class="mv-badge" data-variant="success" data-size="sm">−20%</span></label>
            </div>
          </header>
          <div class="mv-pricing-plans">
            <article class="mv-pricing-plan" aria-labelledby="prr-solo">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="prr-solo">Solo</h3>
                <p class="mv-pricing-desc">For freelancers juggling a handful of clients.</p>
              </div>
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$0" data-yearly="$0">$0</span>
                <span class="mv-pricing-period">/ month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="Free forever" data-yearly="Free forever">Free forever</span>
              </p>
              <a class="mv-button" data-variant="outline" href="#pricing-demo">Get started for free</a>
              <ul class="mv-pricing-features">
                <li>Unlimited boards and sprints</li>
                <li>Time tracking</li>
                <li>Basic client portal</li>
                <li data-off>Planning assistant</li>
                <li data-off>Automations</li>
              </ul>
            </article>
            <article class="mv-pricing-plan" data-highlight aria-labelledby="prr-team">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="prr-team">Team</h3>
                <span class="mv-badge mv-pricing-ribbon" data-variant="accent" data-size="sm">Recommended</span>
                <p class="mv-pricing-desc">For studios and product teams of up to 50 people.</p>
              </div>
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$24" data-yearly="$19">$24</span>
                <span class="mv-pricing-was" data-monthly="" data-yearly="$24" aria-hidden="true"></span>
                <span class="mv-pricing-period">/ member / month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="Billed monthly, no commitment" data-yearly="Billed $228 per member per year">Billed monthly, no commitment</span>
              </p>
              <a class="mv-button" href="#pricing-demo">Start 14-day free trial</a>
              <ul class="mv-pricing-features">
                <li class="mv-pricing-features-title">Everything in Solo, plus:</li>
                <li>Unlimited projects and guests</li>
                <li>Planning assistant</li>
                <li>60 ready-made automations</li>
                <li>White-label client portal</li>
                <li>Weekly reports by email</li>
              </ul>
            </article>
            <article class="mv-pricing-plan" aria-labelledby="prr-ent">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="prr-ent">Enterprise</h3>
                <p class="mv-pricing-desc">For organizations with strict security requirements.</p>
              </div>
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$39" data-yearly="$31">$39</span>
                <span class="mv-pricing-was" data-monthly="" data-yearly="$39" aria-hidden="true"></span>
                <span class="mv-pricing-period">/ member / month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="From 50 members" data-yearly="Billed $372 per member per year">From 50 members</span>
              </p>
              <a class="mv-button" data-variant="secondary" href="#pricing-demo">Talk to sales</a>
              <ul class="mv-pricing-features">
                <li class="mv-pricing-features-title">Everything in Team, plus:</li>
                <li>SAML SSO and SCIM provisioning</li>
                <li>Audit log, custom retention</li>
                <li>Regional data hosting</li>
                <li>99.99% uptime SLA</li>
                <li>Named account manager</li>
              </ul>
            </article>
          </div>
          <p class="mv-pricing-note">Prices exclude tax. Nonprofits and schools get 50% off. <a href="#pricing-demo">Compare every feature</a></p>
        </div>
      </section>
    </mv-pricing>
  </div>

  <!-- ═════════ Single plan ═════════ -->
  <div role="tabpanel" data-value="single">
    <mv-pricing billing="yearly">
      <section class="mv-pricing" data-variant="single" aria-labelledby="pr1-title" style="--mv-pricing-bg:var(--mv-bg-subtle)">
        <div class="mv-pricing-inner">
          <header class="mv-pricing-header">
            <p class="mv-pricing-eyebrow">One plan</p>
            <h2 class="mv-pricing-title" id="pr1-title">Everything included, for everyone</h2>
            <p class="mv-pricing-lead">No hidden tiers or paid add-ons: every member gets all of Canopy.</p>
            <div class="mv-pricing-toggle" data-billing-toggle role="radiogroup" aria-label="Billing period">
              <label><input type="radio" name="pr1-billing" value="monthly">Monthly</label>
              <label><input type="radio" name="pr1-billing" value="yearly" checked>Yearly <span class="mv-badge" data-variant="success" data-size="sm">2 months free</span></label>
            </div>
          </header>
          <article class="mv-pricing-single" aria-labelledby="pr1-name">
            <div class="mv-pricing-single-body">
              <div class="mv-pricing-plan-head">
                <h3 class="mv-pricing-name" id="pr1-name">Canopy Pro</h3>
                <span class="mv-badge" data-variant="secondary" data-size="sm">New</span>
                <p class="mv-pricing-desc">Plan, track and bill your projects, for teams of 1 to 500 people.</p>
              </div>
              <ul class="mv-pricing-features">
                <li class="mv-pricing-features-title">What's included</li>
                <li>Unlimited projects and guests</li>
                <li>Planning assistant</li>
                <li>Time tracking and payroll export</li>
                <li>White-label client portal</li>
                <li>120 integrations</li>
                <li>SSO and audit log</li>
                <li>24/7 human support</li>
                <li>US or EU data hosting</li>
              </ul>
            </div>
            <div class="mv-pricing-single-aside">
              <p class="mv-pricing-price">
                <span class="mv-pricing-amount" data-monthly="$24" data-yearly="$20">$20</span>
                <span class="mv-pricing-was" data-monthly="" data-yearly="$24" aria-hidden="true">$24</span>
                <span class="mv-pricing-period">/ member / month</span>
                <span class="mv-pricing-billing" aria-live="polite" data-monthly="Billed monthly, no commitment" data-yearly="Billed $240 per member per year">Billed $240 per member per year</span>
              </p>
              <a class="mv-button" data-size="lg" href="#pricing-demo">Start free trial</a>
              <p class="mv-pricing-billing">14-day trial, no credit card required</p>
            </div>
          </article>
        </div>
      </section>
    </mv-pricing>
  </div>
</mv-tabs>

API

Attributes

NameTypeDefaultDescription
billingmonthly | yearlymonthlyOn <mv-pricing>: active period (reflected as an attribute, controllable from outside). Without the attribute, it is read from the checked radio.
data-variantrows | singleOn .mv-pricing: rows = one horizontal band per plan from 50rem (head · features · price + CTA; meters hidden); single = one plan (.mv-pricing-single). Default: plan columns (1 → 2 → 3).
data-highlightbooleanOn a plan: recommended (accent ring, raised); put a .mv-badge.mv-pricing-ribbon in its -plan-head.
data-monthly / data-yearlystringOn any descendant: value per period; its text is replaced (an empty value empties it). An <mv-number-ticker>, if you use one, gets its value attribute set instead.
data-billing-togglebooleanOn the radio group (or a <mv-toggle-group>) that drives the period.
data-offbooleanOn a .mv-pricing-features <li>: feature not included (dash mark, struck through).

Events

NameDescription
mv-changePeriod changed (detail.billing).

Content structure

NameDescription
.mv-pricing-header-eyebrow, -title, -lead on the left; the .mv-pricing-toggle moves to the right from 48rem.
.mv-pricing-toggleSegmented control: two <label><input type=radio></label> (inputs visually hidden, still focusable), optional discount badge.
.mv-pricing-plans > .mv-pricing-plan-plan-head (-name, optional badge, -desc), -price (-amount, optional -was, -period, -billing), button, optional -meters, -features (-features-title).
.mv-pricing-wasStruck-through reference price, shown only while billing is yearly (give it data-monthly="" data-yearly="$24").
.mv-pricing-meters > .mv-pricing-meterRead-only usage bar: two <span> (label, value) and --value (0–100%); give it role="meter" with aria-value* / aria-valuetext.
.mv-pricing-single-single-body (head + features in 2 columns) and -single-aside (price + CTA), split by a dashed rule from 44rem.
.mv-pricing-noteNote below the plans.

CSS classes

NameDescription
mv-pricingRoot section, mv-pricing query container.

CSS variables

NameDefaultDescription
--mv-pricing-columns3Columns from 60rem.
--mv-pricing-max72remMax width.
--mv-pricing-bgtransparentSection background.
--mv-pricing-framevar(--mv-bg-muted)Color of the frame around the plans and of the switch track.

Accessibility

The switch uses native radios (role=radiogroup, arrow keys, focus ring on the label). Each plan is an <article> labeled by its name. Billing text is aria-live="polite"; the struck-through price is aria-hidden (the billing line states the yearly total). Usage bars are role=meter with a text value. Works without JS (prices stay on the initial period).