beta

Settings — <mv-settings>

Settings page: section navigation with scroll tracking, profile (photo preview, validated fields), notification switches, security, an “unsaved changes” bar and a danger zone with type-to-confirm.

CategoryBlocks
TypeWeb Component (<mv-settings>)
Statusbeta
Also installsavatar, button, checkbox, dialog, field, input, switch
Keywordssettings, preferences, profile, account, form, danger-zone, scrollspy

When to use

Avoid when

Install

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

AI agent with the Marvelous UI MCP server: install_components({ slugs: ["settings"], 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/motion.js, core/observe.js, components/settings/settings.js, components/settings/settings.css, components/avatar/avatar.css, components/avatar/avatar.js, components/button/button.css, components/checkbox/checkbox.css, components/dialog/dialog.js, components/dialog/dialog.css, components/field/field.css, components/input/input.css, components/switch/switch.css.

Usage

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

<mv-settings id="st-demo" style="height:720px;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);overflow:hidden">
  <div class="mv-settings-layout">
    <nav class="mv-settings-nav" aria-label="Settings sections">
      <p class="mv-settings-nav-title">Settings</p>
      <ul>
        <li><a href="#st-demo-profile"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg>Profile</a></li>
        <li><a href="#st-demo-notifs"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>Notifications</a></li>
        <li><a href="#st-demo-security"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>Security</a></li>
        <li><a href="#st-demo-danger" data-tone="danger"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0zM12 9v4M12 17h.01"/></svg>Danger zone</a></li>
      </ul>
    </nav>

    <main class="mv-settings-main">
      <div class="mv-settings-inner">
        <header class="mv-settings-header">
          <h1>Account settings</h1>
          <p>Manage your profile, notifications and security for the Nova Studio workspace.</p>
        </header>

        <form class="mv-settings-form" novalidate>
          <section class="mv-settings-section" id="st-demo-profile">
            <div class="mv-settings-section-head">
              <h2>Profile</h2>
              <p>This information is visible to members of your workspace.</p>
            </div>
            <div class="mv-settings-card">
              <div class="mv-settings-avatar">
                <span class="mv-avatar" data-settings-avatar><img alt="Profile photo of Amara Okafor" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%2399f6e4'/%3E%3Cstop offset='1' stop-color='%230f766e'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' fill='url%28%23g%29'/%3E%3Cpath d='M10 66c1-12 10-19 22-19s21 7 22 19z' fill='%23f8fafc'/%3E%3Crect x='27' y='36' width='10' height='12' rx='4' fill='%23e7b590'/%3E%3Ccircle cx='32' cy='27' r='11' fill='%23e7b590'/%3E%3Cpath d='M20.5 26c0-8 5-13 11.5-13s11.5 5 11.5 13c-2-4-6-6.5-11.5-6.5S22.5 22 20.5 26z' fill='%237c2d12'/%3E%3C/svg%3E"><span class="mv-avatar-fallback" aria-hidden="true">AO</span></span>
                <div class="mv-settings-avatar-text">
                  <div class="mv-settings-avatar-actions">
                    <label class="mv-button mv-settings-file" data-variant="outline" data-size="sm">Change photo<input type="file" name="avatar" accept="image/*" data-settings-avatar-input aria-label="Change profile photo"></label>
                    <button class="mv-button" type="button" data-variant="ghost" data-size="sm">Remove</button>
                  </div>
                  <small>JPG, PNG or GIF · 2 MB max</small>
                </div>
              </div>
              <div class="mv-field-group">
                <div class="mv-field-row">
                  <div class="mv-field">
                    <label class="mv-label" for="st-demo-first" data-required>First name</label>
                    <input class="mv-input" id="st-demo-first" name="firstName" value="Amara" required autocomplete="given-name">
                  </div>
                  <div class="mv-field">
                    <label class="mv-label" for="st-demo-last" data-required>Last name</label>
                    <input class="mv-input" id="st-demo-last" name="lastName" value="Okafor" required autocomplete="family-name">
                  </div>
                </div>
                <div class="mv-field">
                  <label class="mv-label" for="st-demo-email" data-required>Email address</label>
                  <input class="mv-input" id="st-demo-email" name="email" type="email" value="[email protected]" required autocomplete="email" aria-describedby="st-demo-email-desc">
                  <p class="mv-field-description" id="st-demo-email-desc">Used for sign-in and notifications.</p>
                </div>
                <div class="mv-field">
                  <label class="mv-label" for="st-demo-handle">Public handle</label>
                  <div class="mv-input-group">
                    <label class="mv-input-group-addon" data-variant="filled" for="st-demo-handle">nova.app/</label>
                    <input class="mv-input" id="st-demo-handle" name="handle" value="amara-okafor" pattern="[a-z0-9\-]+">
                  </div>
                </div>
                <div class="mv-field">
                  <label class="mv-label" for="st-demo-bio" data-optional="optional">Bio</label>
                  <textarea class="mv-textarea" id="st-demo-bio" name="bio" style="--mv-textarea-min-rows:2">Art director with 12 years of experience across London and Toronto.</textarea>
                </div>
              </div>
            </div>
          </section>

          <section class="mv-settings-section" id="st-demo-notifs">
            <div class="mv-settings-section-head">
              <h2>Notifications</h2>
              <p>Choose what’s worth interrupting you for.</p>
            </div>
            <div class="mv-settings-card">
              <label class="mv-choice" data-control="end">
                <input type="checkbox" role="switch" class="mv-switch" name="notifMentions" checked>
                <span class="mv-choice-text"><span class="mv-choice-title">Mentions and replies</span><span class="mv-choice-description">When someone mentions you in a comment.</span></span>
              </label>
              <label class="mv-choice" data-control="end">
                <input type="checkbox" role="switch" class="mv-switch" name="notifOrders" checked>
                <span class="mv-choice-text"><span class="mv-choice-title">New orders</span><span class="mv-choice-description">One notification per paid order.</span></span>
              </label>
              <label class="mv-choice" data-control="end">
                <input type="checkbox" role="switch" class="mv-switch" name="notifMarketing">
                <span class="mv-choice-text"><span class="mv-choice-title">Product updates</span><span class="mv-choice-description">One newsletter a month, never more.</span></span>
              </label>
              <div class="mv-settings-row">
                <div><label class="mv-settings-row-title" for="st-demo-digest">Email digest</label><span class="mv-settings-row-description">A recap of workspace activity.</span></div>
                <select class="mv-select" id="st-demo-digest" name="digest" data-size="sm" data-native>
                  <option value="daily">Daily</option>
                  <option value="weekly" selected>Weekly</option>
                  <option value="never">Never</option>
                </select>
              </div>
            </div>
          </section>

          <section class="mv-settings-section" id="st-demo-security">
            <div class="mv-settings-section-head">
              <h2>Security</h2>
              <p>Protect access to your account.</p>
            </div>
            <div class="mv-settings-card">
              <label class="mv-choice" data-control="end">
                <input type="checkbox" role="switch" class="mv-switch" name="twoFactor" checked>
                <span class="mv-choice-text"><span class="mv-choice-title">Two-factor authentication</span><span class="mv-choice-description">A one-time code is required on every new sign-in.</span></span>
              </label>
              <div class="mv-settings-row">
                <div><span class="mv-settings-row-title">Password</span><span class="mv-settings-row-description">Changed 3 months ago.</span></div>
                <button class="mv-button" type="button" data-variant="outline" data-size="sm">Change</button>
              </div>
              <div class="mv-settings-row">
                <div><span class="mv-settings-row-title">MacBook Pro · London <span class="mv-badge" data-variant="success" data-size="sm">This device</span></span><span class="mv-settings-row-description">Chrome · active now</span></div>
              </div>
              <div class="mv-settings-row">
                <div><span class="mv-settings-row-title">iPhone 15 · New York</span><span class="mv-settings-row-description">Safari · 2 days ago</span></div>
                <button class="mv-button" type="button" data-variant="ghost" data-size="sm">Sign out</button>
              </div>
            </div>
          </section>

          <div class="mv-settings-savebar" role="region" aria-label="Pending changes">
            <span class="mv-settings-savebar-status" role="status" data-dirty="Unsaved changes" data-saved="Changes saved">Unsaved changes</span>
            <button class="mv-button" type="reset" data-variant="ghost" data-size="sm">Cancel</button>
            <button class="mv-button" type="submit" data-size="sm">Save</button>
          </div>
        </form>

        <section class="mv-settings-section" id="st-demo-danger" data-tone="danger">
          <div class="mv-settings-section-head">
            <h2>Danger zone</h2>
            <p>Irreversible actions on the workspace.</p>
          </div>
          <div class="mv-settings-card mv-settings-danger">
            <div class="mv-settings-row">
              <div><span class="mv-settings-row-title">Transfer ownership</span><span class="mv-settings-row-description">Hand the workspace over to another admin.</span></div>
              <button class="mv-button" type="button" data-variant="outline" data-size="sm">Transfer</button>
            </div>
            <div class="mv-settings-row">
              <div><span class="mv-settings-row-title">Delete workspace</span><span class="mv-settings-row-description">Projects, files and invoices will be permanently erased.</span></div>
              <mv-dialog persistent>
                <button class="mv-button" type="button" data-variant="destructive" data-size="sm" data-mv-open>Delete…</button>
                <dialog style="--mv-dialog-width:28rem">
                  <header class="mv-dialog-header">
                    <h2 class="mv-dialog-title">Delete “Nova Studio”?</h2>
                    <p class="mv-dialog-description">All 24 projects, 1.2 GB of files and the billing history will be deleted for all 12 members. This can’t be undone.</p>
                  </header>
                  <div class="mv-field" style="margin-bottom:1.25rem">
                    <label class="mv-label" for="st-demo-confirm">To confirm, type <span class="mv-settings-confirm-code">nova-studio</span></label>
                    <input class="mv-input" id="st-demo-confirm" data-settings-confirm data-expected="nova-studio" autocomplete="off" spellcheck="false">
                  </div>
                  <div class="mv-dialog-footer">
                    <button class="mv-button" type="button" data-variant="outline" data-mv-close="cancel">Cancel</button>
                    <button class="mv-button" type="button" data-variant="destructive" data-mv-close="delete" data-settings-confirm-action disabled>Delete permanently</button>
                  </div>
                </dialog>
              </mv-dialog>
            </div>
          </div>
        </section>
      </div>
    </main>
  </div>
</mv-settings>
<script type="module">
  const settings = document.getElementById("st-demo");
  settings?.addEventListener("mv-save", (e) => { e.preventDefault(); setTimeout(e.detail.done, 700); });
</script>

API

Methods

NameDescription
markSaved()Treats the current form state as saved (hides the bar).

Events

NameDescription
mv-saveValid form submitted. detail: { data, form, done }. Call preventDefault() then done() for an async save.
mv-confirmDanger-zone action confirmed. detail: { action } (the button’s data-mv-close value).

Content structure

NameDescription
.mv-settings-layout > .mv-settings-nav + .mv-settings-mainNavigation (#anchor links) and scrolling content.
.mv-settings-sectionSection targeted by a link; its h2 names it and receives focus on navigation. data-tone="danger" for the danger zone.
form.mv-settings-formTracked form: any change shows .mv-settings-savebar (reset = cancel, submit = save).
[data-settings-avatar] + input[data-settings-avatar-input]Local preview of the chosen photo (object URL, no network).
input[data-settings-confirm][data-expected]Confirmation input: enables [data-settings-confirm-action] when it matches.

CSS classes

NameDescription
mv-settings-nav-title / -inner / -headerNavigation title, content column, page header.
mv-settings-section-head / -cardSection title + description, card with divided rows.
mv-settings-row / -row-title / -row-descriptionLabel row with a trailing control.
mv-settings-avatar / -avatar-text / -avatar-actions / -fileProfile photo row; -file turns a label into a file button.
mv-settings-savebar / -savebar-statusSticky floating bar (data-state dirty | saved).
mv-settings-danger / -confirm-codeDanger-tinted card, code to type back.

Accessibility

Named navigation, current link marked aria-current; navigating moves focus to the section heading (tabindex=-1). Sections are named by their heading. Native role=switch toggles. Status bar uses role=status. Deletion: alertdialog (persistent), button disabled until the input matches. Smooth scrolling is disabled with reduced motion.