Cart <mv-cart>
Carrinho completo (drawer ou página): linhas de produto horizontais com quantidade e totais ao vivo, uma barra “Only $12.40 away from free shipping”, cupons validados com mensagens de erro (porcentagem, valor fixo ou frete grátis, hook de servidor assíncrono), economia em preços promocionais, frete, total com impostos incluídos, remoção com “Undo” por 5 segundos, um estado vazio e um botão de pagamento que mostra o total.
Categoria Blocos Tipo Bloco (seção pronta) (<mv-cart>) Status estável Também instala button , input , like-button , number-field , price , product-card , quantity , rating , progress Keywords cart, panier, mini-cart, drawer, order-summary, promo-code, free-shipping, e-commerce, block
When to use
A store needs a cart drawer or cart page with live totals, quantity changes and undoable removal A free-shipping progress bar and validated promo codes should encourage larger orders The order summary must show savings, shipping and tax-inclusive totals before checkout
Avoid when
The shopper must enter address, shipping method and payment in the same flow → use Checkout instead A single cart line or product summary is needed elsewhere on the page → use Product Card instead
Obtenha Cart. Incluído em todos os planos: o Pro pack traz todos os 317 componentes, com a CLI e o servidor MCP usados abaixo.
Ver planos
Instalação
node scripts/add.mjs cart --out ./src/marvelous
Agente de IA com o servidor MCP do Marvelous UI: install_components({ slugs: ["cart"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Arquivos copiados (dependências incluídas): tokens/tokens.css, core/base.css, core/dismiss.js, core/dom.js, core/element.js, core/motion.js, components/cart/cart.js, components/cart/cart.css, components/button/button.css, components/input/input.css, components/like-button/like-button.js, components/like-button/like-button.css, components/number-field/number-field.js, components/number-field/number-field.css, components/price/price.js, components/price/price.css, components/product-card/product-card.js, components/product-card/product-card.css, components/quantity/quantity.js, components/quantity/quantity.css, core/form.js, components/rating/rating.js, components/rating/rating.css, components/progress/progress.css.
Uso
Marcação de referência, para usar como ponto de partida e personalizar com atributos, data-* e variáveis CSS:
<div id="ct-demo" style="width:min(100%,64rem);margin-inline:auto;display:grid;gap:2.5rem">
<style>
#ct-demo .bar { display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:.75rem 1rem; border:1px solid var(--mv-border); border-radius:var(--mv-radius-xl); background:var(--mv-surface) }
#ct-demo .brand { font-weight:600; letter-spacing:.14em; text-transform:uppercase; font-size:.8125rem }
#ct-demo .cart-btn { position:relative }
#ct-demo .cart-btn .mv-badge { position:absolute; top:-.4rem; right:-.4rem; min-width:1.25rem; height:1.25rem; padding:0 .3rem; justify-content:center; font-size:.6875rem; border-radius:999px; font-variant-numeric:tabular-nums }
#ct-demo h2.sec { margin:0 0 1rem; font-size:1.375rem; letter-spacing:-.01em }
#ct-demo h2.sec small { color:var(--mv-fg-muted); font-weight:400; font-size:.875rem; margin-inline-start:.5rem }
#ct-demo .log { margin:1rem 0 0; color:var(--mv-fg-muted); font-size:.8125rem }
#ct-demo-drawer .mv-product-card-title a, #ct-demo .mv-product-card-title a { color:inherit; text-decoration:none }
</style>
<mv-dialog>
<div class="bar">
<span class="brand">Halcyon Home</span>
<button class="mv-button cart-btn" data-variant="outline" data-mv-open aria-label="Open cart, 4 items"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 8h14l-1.2 12H6.2L5 8Z"/><path d="M9 8V6a3 3 0 0 1 6 0v2"/></svg> Cart <span class="mv-badge" data-mirror aria-hidden="true">4</span></button>
</div>
<dialog data-variant="sheet" data-side="right" id="ct-demo-drawer">
<mv-cart free-shipping="60" shipping="4.9" shipping-label="Ground" promo-codes="WELCOME10=10%;THANKS5=5;FREESHIP=shipping" id="ct-drawer">
<header class="mv-cart-drawer-header">
<h2 class="mv-dialog-title">Your cart <span data-cart-count style="color:var(--mv-fg-muted);font-weight:400">4</span></h2>
<button class="mv-dialog-close" data-mv-close aria-label="Close cart"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
</header>
<div class="mv-cart" data-layout="drawer">
<div class="mv-cart-lines">
<mv-product-card data-id="pull-merinos" data-title="Merino Crewneck Sweater" data-price="89" data-compare-at="112">
<article class="mv-product-card" data-variant="horizontal">
<a class="mv-product-card-media" href="#pull-merinos" tabindex="-1" aria-hidden="true"><img class="mv-product-card-image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3ClinearGradient id='bg' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='oklch(0.95 0.012 250)'/%3E%3Cstop offset='1' stop-color='oklch(0.87 0.025 255)'/%3E%3C/linearGradient%3E%3CradialGradient id='fl'%3E%3Cstop offset='0' stop-color='black' stop-opacity='.22'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='0'/%3E%3C/radialGradient%3E%3ClinearGradient id='sh' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='white' stop-opacity='.22'/%3E%3Cstop offset='.55' stop-color='white' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='.16'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23bg)'/%3E%3Cdefs%3E%3Cpattern id='k' width='12' height='10' patternUnits='userSpaceOnUse'%3E%3Cpath d='M0 0l6 8 6-8' fill='none' stroke='black' stroke-opacity='.09' stroke-width='1.6'/%3E%3C/pattern%3E%3C/defs%3E%3Cellipse cx='200' cy='420' rx='150' ry='18' fill='url(%23fl)' opacity='1'/%3E%3Cpath d='M160 118 Q200 142 240 118 L276 128 Q300 140 310 176 L336 318 L300 331 L268 222 L267 388 Q200 397 133 388 L132 222 L100 331 L64 318 L90 176 Q100 140 124 128 Z' fill='oklch(0.34 0.05 262)'/%3E%3Cpath d='M160 118 Q200 142 240 118 L276 128 Q300 140 310 176 L336 318 L300 331 L268 222 L267 388 Q200 397 133 388 L132 222 L100 331 L64 318 L90 176 Q100 140 124 128 Z' fill='url(%23k)'/%3E%3Cpath d='M160 118 Q200 142 240 118 L276 128 Q300 140 310 176 L336 318 L300 331 L268 222 L267 388 Q200 397 133 388 L132 222 L100 331 L64 318 L90 176 Q100 140 124 128 Z' fill='url(%23sh)'/%3E%3Cpath d='M160 118 Q200 152 240 118 Q200 136 160 118Z' fill='black' fill-opacity='.22'/%3E%3Cpath d='M154 116 Q200 160 246 116' fill='none' stroke='black' stroke-opacity='.12' stroke-width='5'/%3E%3Cpath d='M133 370 Q200 379 267 370 L267 388 Q200 397 133 388 Z' fill='black' fill-opacity='.1'/%3E%3Cpath d='M64 318 L100 331 L103 316 L68 303 Z' fill='black' fill-opacity='.1'/%3E%3Cpath d='M336 318 L300 331 L297 316 L332 303 Z' fill='black' fill-opacity='.1'/%3E%3Cpath d='M132 222 L133 380 M268 222 L267 380' stroke='black' stroke-opacity='.08' stroke-width='3'/%3E%3C/svg%3E" alt=""></a>
<div class="mv-product-card-body">
<div class="mv-product-card-head">
<h3 class="mv-product-card-title"><a href="#pull-merinos">Merino Crewneck Sweater</a></h3>
<mv-price data-role="total" value="89" compare-at="112" discount="none" data-size="sm"></mv-price>
</div>
<p class="mv-product-card-meta">Midnight blue · Size M</p>
<p class="mv-product-card-meta" data-role="unit" hidden>$89 each</p>
<div class="mv-product-card-actions">
<mv-quantity value="1" stock="3" data-size="sm" caption="none" aria-label="Quantity, Merino Crewneck Sweater"></mv-quantity>
<button type="button" class="mv-product-card-remove" data-remove><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></svg><span class="mv-product-card-remove-text">Remove</span><span class="mv-sr-only"> Merino Crewneck Sweater</span></button>
</div>
</div>
</article>
</mv-product-card>
<mv-product-card data-id="mug-sauge" data-title="Glazed Stoneware Mug" data-price="24.9">
<article class="mv-product-card" data-variant="horizontal">
<a class="mv-product-card-media" href="#mug-sauge" tabindex="-1" aria-hidden="true"><img class="mv-product-card-image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3ClinearGradient id='bg' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='oklch(0.96 0.015 145)'/%3E%3Cstop offset='1' stop-color='oklch(0.89 0.03 150)'/%3E%3C/linearGradient%3E%3CradialGradient id='fl'%3E%3Cstop offset='0' stop-color='black' stop-opacity='.22'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='0'/%3E%3C/radialGradient%3E%3ClinearGradient id='sh' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='white' stop-opacity='.22'/%3E%3Cstop offset='.55' stop-color='white' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='.16'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23bg)'/%3E%3Cellipse cx='200' cy='392' rx='130' ry='16' fill='url(%23fl)' opacity='1'/%3E%3Cpath d='M290 220 q64 0 64 60 q0 60-64 60' fill='none' stroke='oklch(0.7 0.06 150)' stroke-width='22' stroke-linecap='round'/%3E%3Cpath d='M110 170 L290 170 L284 370 Q200 392 116 370 Z' fill='oklch(0.86 0.03 75)'/%3E%3Cpath d='M110 170 L290 170 L287 270 C250 286 236 256 206 280 C176 300 150 266 113 284 Z' fill='oklch(0.7 0.06 150)'/%3E%3Cpath d='M110 170 L290 170 L284 370 Q200 392 116 370 Z' fill='url(%23sh)'/%3E%3Cellipse cx='200' cy='170' rx='90' ry='16' fill='oklch(0.36 0.05 55)'/%3E%3Cellipse cx='200' cy='170' rx='90' ry='16' fill='none' stroke='oklch(0.7 0.06 150)' stroke-width='6' style='filter:brightness(1.1)'/%3E%3C/svg%3E" alt=""></a>
<div class="mv-product-card-body">
<div class="mv-product-card-head">
<h3 class="mv-product-card-title"><a href="#mug-sauge">Glazed Stoneware Mug</a></h3>
<mv-price data-role="total" value="49.8" discount="none" data-size="sm"></mv-price>
</div>
<p class="mv-product-card-meta">Sage · 12 oz</p>
<p class="mv-product-card-meta" data-role="unit">$24.90 each</p>
<div class="mv-product-card-actions">
<mv-quantity value="2" max="10" data-size="sm" caption="none" aria-label="Quantity, Glazed Stoneware Mug"></mv-quantity>
<button type="button" class="mv-product-card-remove" data-remove><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></svg><span class="mv-product-card-remove-text">Remove</span><span class="mv-sr-only"> Glazed Stoneware Mug</span></button>
</div>
</div>
</article>
</mv-product-card>
<mv-product-card data-id="bougie-figuier" data-title="Fig Leaf Scented Candle" data-price="36">
<article class="mv-product-card" data-variant="horizontal">
<a class="mv-product-card-media" href="#bougie-figuier" tabindex="-1" aria-hidden="true"><img class="mv-product-card-image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3ClinearGradient id='bg' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='oklch(0.97 0.012 20)'/%3E%3Cstop offset='1' stop-color='oklch(0.9 0.03 15)'/%3E%3C/linearGradient%3E%3CradialGradient id='fl'%3E%3Cstop offset='0' stop-color='black' stop-opacity='.22'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='0'/%3E%3C/radialGradient%3E%3ClinearGradient id='sh' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='white' stop-opacity='.22'/%3E%3Cstop offset='.55' stop-color='white' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='.16'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23bg)'/%3E%3Cdefs%3E%3ClinearGradient id='gls' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='white' stop-opacity='.55'/%3E%3Cstop offset='.3' stop-color='white' stop-opacity='.1'/%3E%3Cstop offset='1' stop-color='white' stop-opacity='.35'/%3E%3C/linearGradient%3E%3CradialGradient id='fg'%3E%3Cstop offset='0' stop-color='oklch(0.95 0.12 90)' stop-opacity='.8'/%3E%3Cstop offset='1' stop-color='oklch(0.9 0.12 80)' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='200' cy='404' rx='120' ry='14' fill='url(%23fl)' opacity='1'/%3E%3Crect x='120' y='200' width='160' height='200' rx='18' fill='oklch(0.8 0.07 15)'/%3E%3Crect x='120' y='200' width='160' height='200' rx='18' fill='url(%23sh)'/%3E%3Crect x='120' y='200' width='160' height='200' rx='18' fill='url(%23gls)'/%3E%3Cellipse cx='200' cy='214' rx='72' ry='10' fill='oklch(0.95 0.02 85)'/%3E%3Cpath d='M200 214 v-18' stroke='oklch(0.25 0.01 60)' stroke-width='3'/%3E%3Crect x='144' y='270' width='112' height='70' rx='4' fill='oklch(0.97 0.01 90)'/%3E%3Ctext x='200' y='302' text-anchor='middle' font-family='Georgia,serif' font-size='18' fill='oklch(0.3 0.02 60)'%3EFig Leaf%3C/text%3E%3Ctext x='200' y='322' text-anchor='middle' font-family='Arial,sans-serif' font-size='9' letter-spacing='2' fill='oklch(0.45 0.02 60)'%3EPLANT WAX %C2%B7 6 OZ%3C/text%3E%3C/svg%3E" alt=""></a>
<div class="mv-product-card-body">
<div class="mv-product-card-head">
<h3 class="mv-product-card-title"><a href="#bougie-figuier">Fig Leaf Scented Candle</a></h3>
<mv-price data-role="total" value="36" discount="none" data-size="sm"></mv-price>
</div>
<p class="mv-product-card-meta">6 oz · Plant-based wax</p>
<p class="mv-product-card-meta" data-role="unit" hidden>$36 each</p>
<div class="mv-product-card-actions">
<mv-quantity value="1" max="10" data-size="sm" caption="none" aria-label="Quantity, Fig Leaf Scented Candle"></mv-quantity>
<button type="button" class="mv-product-card-remove" data-remove><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></svg><span class="mv-product-card-remove-text">Remove</span><span class="mv-sr-only"> Fig Leaf Scented Candle</span></button>
</div>
</div>
</article>
</mv-product-card>
</div>
<div class="mv-cart-empty" hidden>
<span class="mv-cart-empty-icon"><svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 8h14l-1.2 12H6.2L5 8Z"/><path d="M9 8V6a3 3 0 0 1 6 0v2"/></svg></span>
<h3>Your cart is empty</h3>
<p>Browse our new arrivals. Free shipping on orders over $60.</p>
<a class="mv-button" href="#boutique">Shop now</a>
</div>
<div class="mv-cart-summary">
<button type="button" class="mv-button mv-cart-checkout" data-checkout><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg> Checkout · <span data-role="total"></span></button>
<p class="mv-cart-note">Taxes included · Free returns within 30 days</p>
</div>
</div>
</mv-cart>
</dialog>
</mv-dialog>
<section aria-labelledby="ct-page-title">
<h2 class="sec" id="ct-page-title">My cart <small>Cart page</small></h2>
<mv-cart free-shipping="200" shipping="5.9" shipping-label="Standard, 2-4 days" tax-rate="0.2" promo-codes="WELCOME10=10%;THANKS5=5;FREESHIP=shipping" id="ct-page">
<div class="mv-cart" data-layout="page">
<div class="mv-cart-lines">
<mv-product-card data-id="baskets-cuir" data-title="Pebbled Leather Sneakers" data-price="129">
<article class="mv-product-card" data-variant="horizontal">
<a class="mv-product-card-media" href="#baskets-cuir" tabindex="-1" aria-hidden="true"><img class="mv-product-card-image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3ClinearGradient id='bg' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='oklch(0.95 0.01 90)'/%3E%3Cstop offset='1' stop-color='oklch(0.87 0.02 85)'/%3E%3C/linearGradient%3E%3CradialGradient id='fl'%3E%3Cstop offset='0' stop-color='black' stop-opacity='.22'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='0'/%3E%3C/radialGradient%3E%3ClinearGradient id='sh' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='white' stop-opacity='.22'/%3E%3Cstop offset='.55' stop-color='white' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='.16'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23bg)'/%3E%3Cellipse cx='200' cy='322' rx='170' ry='14' fill='url(%23fl)' opacity='1'/%3E%3Cg transform='translate(-10 10)'%3E%3Cpath d='M62 300 L358 300 C362 320 350 336 328 336 L86 336 C68 336 58 320 62 300 Z' fill='oklch(0.97 0.005 90)'/%3E%3Cpath d='M62 318 L360 318' stroke='black' stroke-opacity='.08' stroke-width='3'/%3E%3Cpath d='M68 300 C66 252 106 236 150 232 L206 198 C226 186 252 188 264 204 L292 240 C324 250 348 262 352 286 L354 300 Z' fill='oklch(0.93 0.02 90)'/%3E%3Cpath d='M68 300 C66 252 106 236 150 232 L206 198 C226 186 252 188 264 204 L292 240 C324 250 348 262 352 286 L354 300 Z' fill='url(%23sh)'/%3E%3Cpath d='M292 240 C324 250 348 262 352 286 L354 300 L318 300 C318 278 306 256 292 240Z' fill='black' fill-opacity='.08'/%3E%3Cpath d='M140 292 C184 258 236 250 306 264' fill='none' stroke='white' stroke-opacity='.85' stroke-width='9' stroke-linecap='round'/%3E%3Cpath d='M214 206 l26 36 M226 200 l26 36 M200 214 l28 34' stroke='oklch(0.97 0.005 90)' stroke-width='5' stroke-linecap='round'/%3E%3Cpath d='M206 198 C226 186 252 188 264 204' fill='none' stroke='black' stroke-opacity='.18' stroke-width='5'/%3E%3Ccircle cx='96' cy='262' r='5' fill='white' fill-opacity='.8'/%3E%3C/g%3E%3C/svg%3E" alt=""></a>
<div class="mv-product-card-body">
<div class="mv-product-card-head">
<h3 class="mv-product-card-title"><a href="#baskets-cuir">Pebbled Leather Sneakers</a></h3>
<mv-price data-role="total" value="129" discount="none" data-size="sm"></mv-price>
</div>
<p class="mv-product-card-meta">Ecru · Size 9</p>
<p class="mv-product-card-meta" data-role="unit" hidden>$129 each</p>
<div class="mv-product-card-actions">
<mv-quantity value="1" stock="4" data-size="sm" caption="none" aria-label="Quantity, Pebbled Leather Sneakers"></mv-quantity>
<button type="button" class="mv-product-card-remove" data-remove><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></svg><span class="mv-product-card-remove-text">Remove</span><span class="mv-sr-only"> Pebbled Leather Sneakers</span></button>
</div>
</div>
</article>
</mv-product-card>
<mv-product-card data-id="cabas-toile" data-title="Heavy Canvas Tote" data-price="45" data-compare-at="59">
<article class="mv-product-card" data-variant="horizontal">
<a class="mv-product-card-media" href="#cabas-toile" tabindex="-1" aria-hidden="true"><img class="mv-product-card-image" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 500'%3E%3Cdefs%3E%3ClinearGradient id='bg' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='oklch(0.97 0.02 85)'/%3E%3Cstop offset='1' stop-color='oklch(0.9 0.04 80)'/%3E%3C/linearGradient%3E%3CradialGradient id='fl'%3E%3Cstop offset='0' stop-color='black' stop-opacity='.22'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='0'/%3E%3C/radialGradient%3E%3ClinearGradient id='sh' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='white' stop-opacity='.22'/%3E%3Cstop offset='.55' stop-color='white' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='black' stop-opacity='.16'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='400' height='500' fill='url(%23bg)'/%3E%3Cellipse cx='200' cy='418' rx='140' ry='16' fill='url(%23fl)' opacity='1'/%3E%3Cpath d='M150 170 C150 100 250 100 250 170' fill='none' stroke='oklch(0.62 0.06 60)' stroke-width='12' stroke-linecap='round'/%3E%3Cpath d='M168 172 C168 118 232 118 232 172' fill='none' stroke='oklch(0.55 0.06 60)' stroke-width='10' stroke-linecap='round'/%3E%3Cpath d='M102 170 L298 170 L318 410 Q200 420 82 410 Z' fill='oklch(0.74 0.13 75)'/%3E%3Cpath d='M102 170 L298 170 L318 410 Q200 420 82 410 Z' fill='url(%23sh)'/%3E%3Crect x='140' y='150' width='20' height='40' rx='4' fill='oklch(0.55 0.06 60)'/%3E%3Crect x='240' y='150' width='20' height='40' rx='4' fill='oklch(0.55 0.06 60)'/%3E%3Ctext x='200' y='300' text-anchor='middle' font-family='Georgia,serif' font-size='30' letter-spacing='8' fill='black' fill-opacity='.55'%3ELUMEN%3C/text%3E%3Ctext x='200' y='326' text-anchor='middle' font-family='Arial,sans-serif' font-size='10' letter-spacing='4' fill='black' fill-opacity='.45'%3EATELIER %C2%B7 PARIS%3C/text%3E%3Cpath d='M102 170 L298 170' stroke='black' stroke-opacity='.15' stroke-width='6'/%3E%3C/svg%3E" alt=""></a>
<div class="mv-product-card-body">
<div class="mv-product-card-head">
<h3 class="mv-product-card-title"><a href="#cabas-toile">Heavy Canvas Tote</a></h3>
<mv-price data-role="total" value="45" compare-at="59" discount="none" data-size="sm"></mv-price>
</div>
<p class="mv-product-card-meta">Ochre · Organic cotton</p>
<p class="mv-product-card-meta" data-role="unit" hidden>$45 each</p>
<div class="mv-product-card-actions">
<mv-quantity value="1" max="10" data-size="sm" caption="none" aria-label="Quantity, Heavy Canvas Tote"></mv-quantity>
<button type="button" class="mv-product-card-remove" data-remove><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></svg><span class="mv-product-card-remove-text">Remove</span><span class="mv-sr-only"> Heavy Canvas Tote</span></button>
</div>
</div>
</article>
</mv-product-card>
</div>
<div class="mv-cart-empty" hidden>
<span class="mv-cart-empty-icon"><svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 8h14l-1.2 12H6.2L5 8Z"/><path d="M9 8V6a3 3 0 0 1 6 0v2"/></svg></span>
<h3>Your cart is empty</h3>
<p>Browse our new arrivals. Free shipping on orders over $60.</p>
<a class="mv-button" href="#boutique">Shop now</a>
</div>
<aside class="mv-cart-summary" data-variant="card" aria-labelledby="ct-sum-title">
<h3 class="mv-cart-summary-title" id="ct-sum-title">Order summary</h3>
<button type="button" class="mv-button mv-cart-checkout" data-checkout>Check out</button>
<p class="mv-cart-note"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg> Secure payment · Visa, PayPal, Apple Pay</p>
</aside>
</div>
</mv-cart>
<p class="log" id="ct-log" aria-live="polite">Try the codes WELCOME10, THANKS5 or FREESHIP.</p>
</section>
<section aria-labelledby="ct-empty-title">
<h2 class="sec" id="ct-empty-title">Empty cart <small>Empty state</small></h2>
<mv-cart>
<div class="mv-cart" data-layout="page">
<div class="mv-cart-lines"></div>
<div class="mv-cart-empty">
<span class="mv-cart-empty-icon"><svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 8h14l-1.2 12H6.2L5 8Z"/><path d="M9 8V6a3 3 0 0 1 6 0v2"/></svg></span>
<h3>Your cart is empty</h3>
<p>Browse our new arrivals. Free shipping on orders over $60.</p>
<a class="mv-button" href="#boutique">Shop now</a>
</div>
</div>
</mv-cart>
</section>
<script type="module">
const drawer = document.getElementById("ct-drawer");
const badge = document.querySelector("#ct-demo [data-mirror]");
const opener = badge.closest("button");
drawer.addEventListener("mv-change", (e) => {
const n = e.detail.count;
badge.textContent = n;
opener.setAttribute("aria-label", `Open cart, ${n} item${n === 1 ? "" : "s"}`);
});
const log = document.getElementById("ct-log");
const fmt = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
for (const c of document.querySelectorAll("#ct-demo mv-cart")) c.addEventListener("mv-checkout", (e) => {
e.preventDefault();
log.textContent = `mv-checkout: ${e.detail.count} item(s), total ${fmt.format(e.detail.total)}`;
});
</script>
</div>
API
Attributes
Name Tipo Default Description currency / localestring USD / en-USAmount formatting (Intl.NumberFormat). free-shippingnumber Free-shipping threshold (after discounts): turns on the progress bar. shippingnumber 0Shipping cost below the threshold. shipping-labelstring Shipping method shown (“Shipping · Ground”). tax-ratenumber Included tax rate (0.2): adds an “Includes tax (20%)” row. promo-codesstring Client-side accepted codes: “WELCOME10=10%;THANKS5=5;FREESHIP=shipping”. Shows the promo code field. promoboolean Shows the promo code field without local codes (use with validatePromo). undonumber 5000Delay (ms) during which a removal can be undone. data-layoutpage | drawer On .mv-cart: page (lines + summary that sticks beyond a 46rem container) or drawer (scrolling lines, fixed footer). data-variantcard On .mv-cart-summary: summary as a card; on .mv-cart-lines: compact (no dividers).
Properties
Name Tipo Description linesMvProductCard[] Active lines (excluding pending removals). totalsobject { count, subtotal, original, savings, discount, shipping, freeByThreshold, total, tax, promo }. validatePromo(code, totals) => Promise<{ ok, type, value, label?, message? }> Server-side code validation; type percent (0.1), amount or shipping.
Methods
Name Description applyPromo(code)Validates and applies a code; resolves { ok, message }. removePromo()Removes the applied code.
Events
Name Description mv-changedetail: totals, on every quantity change, removal, undo or code. mv-promodetail: { code, applied, type, value }. mv-checkoutClick on [data-checkout]; cancelable; detail: totals.
Content structure
Name Description .mv-cart > .mv-cart-linesmv-product-card lines (data-price, data-compare-at, mv-quantity, [data-remove]). .mv-cart-summarySummary: generated totals, promo code and bar, [data-checkout] (its [data-role=total] receives the total), .mv-cart-note. .mv-cart-emptyEmpty state, shown when no lines are left. [data-cart-count]Any element inside mv-cart: receives the item count.
CSS classes
Name Description mv-cart / -lines / -summary / -summary-title / -progress / -totals / -row / -total / -promo / -promo-message / -undo / -checkout / -note / -empty / -empty-icon / -drawer-headerStructure.
Accessibility
Totals in a description list (<dl>). Removal: the line is hidden and replaced by “… was removed. Undo”, which takes focus; announced via role=status; focus returns to the quantity after undo. Promo code: labeled field, aria-live message linked with aria-describedby, aria-invalid on error, aria-busy button while verifying; the code is removed with a button named “Remove code …”. Sale prices are announced as “instead of”. The shipping bar is decorative; the text carries the information.