Review — <mv-reviews>

Customer reviews: summary (average rating, stars, 5→1 histogram where each bar filters, fit gauge), sort (recent, helpful, rating), “With photos” filter, active filter chips, “Show 4 more reviews” pagination; <mv-review> card with “Verified purchase”, purchased variant, collapsed text with “Read more”, photo lightbox, “helpful” votes (one per visitor, undoable), reporting and merchant reply; validated “Write a review” form that adds the review pending moderation.

CategoryBlocks
TypeBlock (ready-made section) (<mv-reviews>)
Statusstable
Also installsavatar, button, field, input, rating, select, textarea
Keywordsreview, reviews, avis, ratings, histogram, ugc, testimonials, e-commerce, block

When to use

Avoid when

Install

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

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

Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/form.js, core/motion.js, components/review/review.js, components/review/review.css, components/avatar/avatar.css, components/avatar/avatar.js, components/button/button.css, components/field/field.css, components/input/input.css, components/rating/rating.js, components/rating/rating.css, components/select/select.css, components/textarea/textarea.css, components/textarea/char-count.js.

Usage

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

<div id="rv-demo" style="width:min(100%,68rem);margin-inline:auto;display:grid;gap:3rem">
  <mv-reviews page-size="4" id="rv-reviews">
    <section class="mv-reviews" aria-labelledby="rv-title">
      <div class="mv-reviews-summary">
        <h2 class="mv-reviews-heading" id="rv-title">Customer reviews</h2>
        <div class="mv-reviews-score">
          <span class="mv-reviews-average"></span>
          <mv-rating class="mv-reviews-stars" readonly precision="0.1"></mv-rating>
          <span class="mv-reviews-total"></span>
        </div>
        <ol class="mv-reviews-histogram" aria-label="Rating breakdown"></ol>
        <div class="mv-reviews-fit" role="img" aria-label="Fit: runs slightly large">
          <span class="mv-reviews-fit-title">Fit</span>
          <span class="mv-reviews-fit-scale" style="--mv-fit:0.62"></span>
          <span class="mv-reviews-fit-labels"><span>Small</span><span>True to size</span><span>Large</span></span>
        </div>
        <mv-dialog>
          <button class="mv-button" data-variant="outline" data-mv-open style="width:100%">Write a review</button>
          <dialog style="--mv-dialog-width:32rem">
            <button class="mv-dialog-close" data-mv-close aria-label="Close"><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 class="mv-dialog-header">
              <h2 class="mv-dialog-title">Share your thoughts</h2>
              <p class="mv-dialog-description">Merino crew-neck sweater · published after review within 48 hours.</p>
            </header>
            <form class="mv-reviews-form" novalidate>
              <div class="mv-field">
                <span class="mv-label" id="rv-note-label" data-required>Your rating</span>
                <mv-rating name="note" required data-size="lg" aria-labelledby="rv-note-label" label="Your rating"></mv-rating>
                <p class="mv-field-error"></p>
              </div>
              <div class="mv-field">
                <label class="mv-label" for="rv-review-title" data-required>Title</label>
                <input class="mv-input" id="rv-review-title" name="title" required maxlength="80" placeholder="Sum it up in one sentence" data-error-missing="Give your review a title.">
                <p class="mv-field-error"></p>
              </div>
              <div class="mv-field">
                <div class="mv-field-header"><label class="mv-label" for="rv-body" data-required>Your review</label><span class="mv-reviews-counter" data-role="counter" aria-hidden="true"></span></div>
                <textarea class="mv-textarea" id="rv-body" name="review" required minlength="30" rows="4" placeholder="Fabric, fit, color, care…" data-error-missing="Tell us about your experience."></textarea>
                <p class="mv-field-error"></p>
              </div>
              <div class="mv-field-row">
                <div class="mv-field">
                  <label class="mv-label" for="rv-name" data-required>First name and initial</label>
                  <input class="mv-input" id="rv-name" name="name" required autocomplete="name" placeholder="Alex D." data-error-missing="Tell us how to sign your review.">
                  <p class="mv-field-error"></p>
                </div>
                <div class="mv-field">
                  <label class="mv-label" for="rv-variant">Size purchased</label>
                  <select class="mv-select" id="rv-variant" name="variant"><option value="">Not specified</option><option>XS</option><option>S</option><option>M</option><option>L</option><option>XL</option></select>
                </div>
              </div>
              <div class="mv-dialog-footer">
                <button type="button" class="mv-button" data-variant="outline" data-mv-close>Cancel</button>
                <button type="submit" class="mv-button">Post review</button>
              </div>
            </form>
          </dialog>
        </mv-dialog>
      </div>

      <div class="mv-reviews-main">
        <div class="mv-reviews-toolbar">
          <span data-reviews-count aria-live="polite"></span>
          <div class="mv-reviews-toolbar-group">
            <button type="button" data-reviews-filter="photos"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 8h3l2-3h6l2 3h3v11H4z"/><circle cx="12" cy="13" r="3.5"/></svg>With photos</button>
            <label class="mv-sr-only" for="rv-sort">Sort reviews</label>
            <select class="mv-select" id="rv-sort" data-reviews-sort>
              <option value="recent">Most recent</option>
              <option value="helpful">Most helpful</option>
              <option value="high">Highest rated</option>
              <option value="low">Lowest rated</option>
            </select>
          </div>
        </div>
        <div class="mv-reviews-list">
          <mv-review data-id="r1" data-rating="5" data-date="2026-09-12" helpful="24" unhelpful="1">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">EW</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Emma W. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">New York · <time class="mv-review-date" datetime="2026-09-12"></time></span>
                </div>
                <mv-rating value="5" readonly data-size="sm" label="Rating: 5 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Sand</b></span><span>Size purchased: <b>M</b></span><span>Usual size: <b>M</b></span></p>
              <h3 class="mv-review-title">The sweater I never take off</h3>
              <div class="mv-review-body"><p>Dense but lightweight knit, zero itch even worn right against the skin. The sand color is exactly what you see in the photos — a warm beige that goes with everything.</p><p>Three cold hand washes later, no pilling and it keeps its shape perfectly. I’ll probably grab it in midnight for the winter.</p></div>
              <ul class="mv-review-photos" aria-label="Photos by Emma W."><li><button type="button" class="mv-review-photo" aria-label="Enlarge photo 1: Sand sweater laid flat"><img 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 80)'/%3E%3Cstop offset='1' stop-color='oklch(0.91 0.025 75)'/%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.8 0.05 75)'/%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="Sand sweater laid flat"></button></li><li><button type="button" class="mv-review-photo" aria-label="Enlarge photo 2: Close-up of the ribbed knit"><img 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 80)'/%3E%3Cstop offset='1' stop-color='oklch(0.91 0.025 75)'/%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='44' height='36' patternUnits='userSpaceOnUse'%3E%3Cpath d='M2 2l20 30 20-30' fill='none' stroke='black' stroke-opacity='.14' stroke-width='7' stroke-linecap='round'/%3E%3Cpath d='M2 2l20 30 20-30' fill='none' stroke='white' stroke-opacity='.12' stroke-width='2' transform='translate(0 -3)'/%3E%3C/pattern%3E%3C/defs%3E%3Crect width='400' height='500' fill='oklch(0.8 0.05 75)'/%3E%3Crect width='400' height='500' fill='url(%23k)'/%3E%3Crect width='400' height='500' fill='url(%23sh)'/%3E%3Cg transform='rotate(-6 250 330)'%3E%3Crect x='190' y='300' width='150' height='74' rx='6' fill='oklch(0.97 0.01 90)'/%3E%3Crect x='190' y='300' width='150' height='74' rx='6' fill='none' stroke='black' stroke-opacity='.12'/%3E%3Ctext x='265' y='333' text-anchor='middle' font-family='Georgia,serif' font-size='20' letter-spacing='3' fill='oklch(0.3 0.02 60)'%3EMERINO%3C/text%3E%3Ctext x='265' y='356' text-anchor='middle' font-family='Arial,sans-serif' font-size='9' letter-spacing='1.2' fill='oklch(0.45 0.02 60)'%3E100%25 WOOL %C2%B7 18.5 %C2%B5m%3C/text%3E%3C/g%3E%3C/svg%3E" alt="Close-up of the ribbed knit"></button></li></ul>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
              <div class="mv-review-reply">
                <p class="mv-review-reply-author">Reply from Harbor Knitwear <span>· Customer care</span></p>
                <p>Thanks, Emma, for such a detailed review! Midnight is one of our best sellers — it’s back in stock on September 30.</p>
              </div>
            </article>
          </mv-review>
          <mv-review data-id="r2" data-rating="4" data-date="2026-09-08" helpful="11" unhelpful="2">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">DK</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Daniel K. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">London · <time class="mv-review-date" datetime="2026-09-08"></time></span>
                </div>
                <mv-rating value="4" readonly data-size="sm" label="Rating: 4 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Midnight</b></span><span>Size purchased: <b>L</b></span><span>Usual size: <b>L</b></span></p>
              <h3 class="mv-review-title">Great quality, runs a bit large</h3>
              <div class="mv-review-body"><p>The merino is really soft and the ribbed trims are flawless. I usually wear an L and find it runs slightly large in the shoulders; an M would have fit closer.</p><p>Delivered in 2 days, plastic-free packaging. Recommended — size down if you like a slim fit.</p></div>
              <ul class="mv-review-photos" aria-label="Photos by Daniel K."><li><button type="button" class="mv-review-photo" aria-label="Enlarge photo 1: Midnight sweater on a hanger"><img 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%3Crect x='0' y='0' width='400' height='500' fill='white' fill-opacity='.18'/%3E%3Cpath d='M200 58 q0-16 12-16 q12 0 12 12 q0 10-12 14 L200 78' fill='none' stroke='oklch(0.55 0.02 60)' stroke-width='5' stroke-linecap='round'/%3E%3Cpath d='M200 78 L96 124 L304 124 Z' fill='none' stroke='oklch(0.6 0.08 60)' stroke-width='8' stroke-linejoin='round'/%3E%3Cg transform='translate(40 30) scale(.8)'%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='.2'/%3E%3Cpath d='M133 372 Q200 380 267 372 L267 388 Q200 397 133 388 Z' fill='black' fill-opacity='.1'/%3E%3C/g%3E%3Cpath d='M300 470 c-4-60 6-110 30-150 c-2 50-10 100-12 150z' fill='oklch(0.55 0.08 150)' opacity='.55'/%3E%3Cpath d='M318 470 c8-52 30-86 58-104 c-14 38-30 72-40 104z' fill='oklch(0.5 0.08 150)' opacity='.5'/%3E%3Crect x='292' y='452' width='64' height='48' rx='6' fill='oklch(0.72 0.06 55)' opacity='.9'/%3E%3C/svg%3E" alt="Midnight sweater on a hanger"></button></li></ul>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
            </article>
          </mv-review>
          <mv-review data-id="r3" data-rating="5" data-date="2026-09-02" helpful="8" unhelpful="0">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">PS</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Priya S. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">Toronto · <time class="mv-review-date" datetime="2026-09-02"></time></span>
                </div>
                <mv-rating value="5" readonly data-size="sm" label="Rating: 5 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Sage</b></span><span>Size purchased: <b>S</b></span><span>Usual size: <b>S</b></span></p>
              <h3 class="mv-review-title">Gorgeous color</h3>
              <div class="mv-review-body"><p>The sage green is stunning in person, even better than in the photos. Perfect with raw denim.</p></div>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
            </article>
          </mv-review>
          <mv-review data-id="r4" data-rating="3" data-date="2026-08-27" helpful="5" unhelpful="3">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">MJ</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Marcus J. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">Chicago · <time class="mv-review-date" datetime="2026-08-27"></time></span>
                </div>
                <mv-rating value="3" readonly data-size="sm" label="Rating: 3 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Ecru</b></span><span>Size purchased: <b>XL</b></span><span>Usual size: <b>XL</b></span></p>
              <h3 class="mv-review-title">Pretty but delicate</h3>
              <div class="mv-review-body"><p>Lovely fabric and a comfortable fit, but a thread snagged on the sleeve after two weeks (caught on a bag, nothing dramatic). Customer care offered an exchange right away, which I appreciated.</p><p>Three stars because at this price I expected a knit that holds up a bit better day to day.</p></div>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
              <div class="mv-review-reply">
                <p class="mv-review-reply-author">Reply from Harbor Knitwear <span>· Customer care</span></p>
                <p>Hi Marcus, thanks for your honesty. Extra-fine merino is more delicate than regular wool, so we now include care tips in every package.</p>
              </div>
            </article>
          </mv-review>
          <mv-review data-id="r5" data-rating="5" data-date="2026-08-19" helpful="17" unhelpful="0">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">SR</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Sofia R. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">Paris · <time class="mv-review-date" datetime="2026-08-19"></time></span>
                </div>
                <mv-rating value="5" readonly data-size="sm" label="Rating: 5 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Brick</b></span><span>Size purchased: <b>M</b></span><span>Usual size: <b>S</b></span></p>
              <h3 class="mv-review-title">A gift that landed</h3>
              <div class="mv-review-body"><p>Bought it for my sister and she loves it. The gift wrapping is beautiful, with a little handwritten note. The brick color is warm without being loud.</p></div>
              <ul class="mv-review-photos" aria-label="Photos by Sofia R."><li><button type="button" class="mv-review-photo" aria-label="Enlarge photo 1: Brick sweater folded in its gift box"><img 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 45)'/%3E%3Cstop offset='1' stop-color='oklch(0.89 0.035 40)'/%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='392' rx='150' ry='16' fill='url(%23fl)' opacity='1'/%3E%3Crect x='96' y='292' width='208' height='96' rx='16' fill='oklch(0.58 0.13 38)' style='filter:brightness(.9)'/%3E%3Crect x='96' y='292' width='208' height='96' rx='16' fill='url(%23k)'/%3E%3Crect x='96' y='292' width='208' height='96' rx='16' fill='url(%23sh)'/%3E%3Cpath d='M104 356h192' stroke='black' stroke-opacity='.12' stroke-width='3'/%3E%3Crect x='110' y='190' width='180' height='112' rx='16' fill='oklch(0.58 0.13 38)'/%3E%3Crect x='110' y='190' width='180' height='112' rx='16' fill='url(%23k)'/%3E%3Crect x='110' y='190' width='180' height='112' rx='16' fill='url(%23sh)'/%3E%3Cpath d='M160 190 Q200 232 240 190' fill='black' fill-opacity='.14'/%3E%3Cpath d='M166 190 Q200 222 234 190' fill='none' stroke='black' stroke-opacity='.18' stroke-width='5'/%3E%3Cpath d='M118 268h164' stroke='black' stroke-opacity='.1' stroke-width='3'/%3E%3C/svg%3E" alt="Brick sweater folded in its gift box"></button></li></ul>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
            </article>
          </mv-review>
          <mv-review data-id="r6" data-rating="4" data-date="2026-08-10" helpful="3" unhelpful="0">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">KT</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Kenji T. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">Tokyo · <time class="mv-review-date" datetime="2026-08-10"></time></span>
                </div>
                <mv-rating value="4" readonly data-size="sm" label="Rating: 4 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Midnight</b></span><span>Size purchased: <b>M</b></span><span>Usual size: <b>M</b></span></p>
              <h3 class="mv-review-title">Great value for money</h3>
              <div class="mv-review-body"><p>Very solid — warm without being bulky. Ideal under a jacket in spring or fall.</p></div>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
            </article>
          </mv-review>
          <mv-review data-id="r7" data-rating="2" data-date="2026-07-28" helpful="6" unhelpful="4">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">NK</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Nadia K. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">Berlin · <time class="mv-review-date" datetime="2026-07-28"></time></span>
                </div>
                <mv-rating value="2" readonly data-size="sm" label="Rating: 2 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Ochre</b></span><span>Size purchased: <b>S</b></span><span>Usual size: <b>S</b></span></p>
              <h3 class="mv-review-title">Disappointed by the color</h3>
              <div class="mv-review-body"><p>The ochre leans much more mustard than in the photos. The fabric is nice, but I sent it back. The refund came through in 4 days.</p></div>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
            </article>
          </mv-review>
          <mv-review data-id="r8" data-rating="5" data-date="2026-07-15" helpful="9" unhelpful="1">
            <article class="mv-review" tabindex="-1">
              <header class="mv-review-header">
                <span class="mv-avatar" data-variant="tinted" aria-hidden="true"><span class="mv-avatar-fallback">LM</span></span>
                <div class="mv-review-author">
                  <span class="mv-review-name">Lucas M. <span class="mv-review-verified"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12.5 11 14.5 15.5 10"/><circle cx="12" cy="12" r="9"/></svg>Verified purchase</span></span>
                  <span class="mv-review-meta">Sydney · <time class="mv-review-date" datetime="2026-07-15"></time></span>
                </div>
                <mv-rating value="5" readonly data-size="sm" label="Rating: 5 out of 5"></mv-rating>
              </header>
              <p class="mv-review-variant"><span>Color: <b>Sand</b></span><span>Size purchased: <b>L</b></span><span>Usual size: <b>L</b></span></p>
              <h3 class="mv-review-title">My third one, still just as good</h3>
              <div class="mv-review-body"><p>I already own two from a previous season and the quality hasn’t changed. You can tell it’s made with care.</p></div>
              <footer class="mv-review-footer"><button type="button" data-report>Report</button></footer>
            </article>
          </mv-review>
        </div>
        <button type="button" class="mv-button" data-variant="outline" data-reviews-more><span data-role="label">Show more reviews</span></button>
      </div>
    </section>
  </mv-reviews>
</div>

API

Attributes

NameTypeDefaultDescription
page-sizenumber4On <mv-reviews>: reviews shown per page.
sortrecent | helpful | high | lowrecentOn <mv-reviews>: current sort (synced with [data-reviews-sort]).
localestringen-USFormatting of dates, averages and percentages.
helpful / unhelpfulnumberOn <mv-review>: existing votes; renders “Was this review helpful? Yes (12) No (1)”.
linesnumber4On <mv-review>: visible lines before “Read more”.
data-rating / data-date / data-idnumber / ISO / stringOn <mv-review>: rating (otherwise read from mv-rating), date (sorting; an empty <time class=mv-review-date> gets filled), identifier used in events.
data-pendingbooleanPending review: shown first, excluded from stats and filters.
data-variantcardOn .mv-review: bordered card (outside a list).

Properties

NameTypeDescription
stats{ total, average, counts[1..5] }<mv-reviews>: stats for published reviews.
filters{ stars: number[], photos: boolean }<mv-reviews>: active filters.
rating / date / hasPhotos / vote<mv-review>: read-only.

Methods

NameDescription
toggleStar(n) / clearFilters() / showMore()<mv-reviews>: filters and pagination.
addReview({ rating, title, body, author, variant })<mv-reviews>: adds a pending card at the top of the list.
openPhoto(i)<mv-review>: opens the lightbox.

Events

NameDescription
mv-change<mv-reviews>: detail { filters, sort, count, total }.
mv-submit<mv-reviews>: valid form, cancelable; detail { rating, title, body, author, variant, values }.
mv-vote<mv-review>: cancelable; detail { id, vote: 'up' | 'down' | null, previous }.
mv-report<mv-review>: cancelable; detail { id }.

Content structure

NameDescription
.mv-reviews-summary.mv-reviews-heading, .mv-reviews-score (.mv-reviews-average, mv-rating.mv-reviews-stars, .mv-reviews-total are filled in), ol.mv-reviews-histogram (generated), .mv-reviews-fit (--mv-fit 0–1), write-a-review button.
.mv-reviews-main.mv-reviews-toolbar ([data-reviews-count], [data-reviews-filter=photos], select[data-reviews-sort]), .mv-reviews-list > mv-review, [data-reviews-more].
form.mv-reviews-formAnywhere inside <mv-reviews> (e.g. in an mv-dialog): mv-rating[name=note], title, review (minlength), name, variant; [data-role=counter] counts characters.
article.mv-review.mv-review-header (avatar, .mv-review-author > .mv-review-name / .mv-review-verified / .mv-review-meta, mv-rating), .mv-review-variant, .mv-review-title, .mv-review-body, ul.mv-review-photos > button.mv-review-photo > img[data-full], .mv-review-footer ([data-report]), .mv-review-reply.

CSS classes

NameDescription
mv-review / -header / -author / -name / -verified / -meta / -variant / -title / -body / -more / -photos / -photo / -footer / -helpful / -vote / -reply / -reply-author / -status / -lightboxReview card.
mv-reviews / -summary / -heading / -score / -average / -stars / -total / -histogram / -bar / -fit / -main / -toolbar / -chips / -chip / -list / -empty / -form / -counterSection.

CSS variables

NameDefaultDescription
--mv-fit0.5Position of the fit marker (0 = small, 1 = large).
--mv-rating-colorvar(--mv-warning)Color of stars and bars.

Accessibility

Histogram bars are aria-pressed buttons (“4 stars: 2 reviews (25%). Filter.”); filter chips are named “Remove filter: …”; result count and sort are announced (role=status). “Read more” uses aria-expanded/aria-controls. Votes are aria-pressed with the total announced, only one vote active. Photos: “Enlarge photo 1: …” buttons, modal <dialog> lightbox (arrow keys, Esc, focus returned to the thumbnail). “Show more” moves focus to the first new review. Form: rating as role=slider, errors linked via aria-describedby, focus on the first invalid field.