Exclusifbêta

Share Button <mv-share-button>

Bouton de partage qui ouvre la feuille de partage native en un toucher là où la Web Share API peut partager les données (vérifié avec navigator.canShare), et ailleurs un petit menu clavier avec « Copier le lien », « E-mail » et les réseaux que vous indiquez, sous forme de simples liens d’intention. Ce que d’autres oublient : aucune requête tierce ni aucun pistage tant que la personne n’a pas choisi de cible (ni scripts, ni pixels, ni iframes, ni icônes de marque, et les liens s’ouvrent avec noopener noreferrer) ; l’URL partagée est l’URL canonique absolue, débarrassée de utm_*, fbclid, gclid et des identifiants de clic similaires (keep-params le désactive) ; fermer la feuille native ne fait rien, en silence, et une feuille bloquée se replie sur le menu ; enfin, un événement annulable mv-share et un événement mv-shared permettent à l’application de compter elle-même les partages.

CatégorieMicro-interactions
TypeWeb Component (<mv-share-button>)
Statutbêta
Keywordsexclusive, light, share, web-share, social, copy-link, privacy, no-tracking, canonical, menu, invite

When to use

  • An article, recipe or product page needs a share button that works on phones and desktops alike
  • An invite or referral link must be easy to send by any app, by email or as a copied link
  • Shares should be counted by the app itself without loading third-party share widgets
  • Links reached from newsletters or ads must be shared without their campaign parameters

Avoid when

  • Only a copy-to-clipboard action is needed, with no other share targets → use Copy Button instead
  • The trigger opens a list of general actions rather than share targets → use Menu instead
  • The page must show live share counts or embedded posts from social networks

Installation

node scripts/add.mjs share-button --out ./src/marvelous

Agent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["share-button"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/focus.js, core/position.js, components/share-button/share-button.js, components/share-button/share-button.css.

Utilisation

Démarrage rapide, le balisage minimal qui fonctionne :

<mv-share-button></mv-share-button>

Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :

<div id="mv-share-demo" style="display:grid;gap:1rem;width:100%;max-width:560px">
  <article style="padding:1.1rem 1.2rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface);box-shadow:var(--mv-shadow-sm)">
    <p style="margin:0 0 .3rem;font-size:.75rem;color:var(--mv-fg-subtle)">Journal · September 24, 2026 · 6 min read</p>
    <h3 style="margin:0;font-size:1.15rem;line-height:1.3">Autumn in the Higashiyama hills</h3>
    <p style="margin:.4rem 0 1rem;font-size:.88rem;color:var(--mv-fg-muted)">Temple gardens at first light, a tea lesson in Gion with Aiko Tanaka, and the night train to Kanazawa.</p>
    <div style="display:flex;align-items:center;justify-content:space-between;gap:.75rem;flex-wrap:wrap">
      <span style="font-size:.8rem;color:var(--mv-fg-subtle)">By Léa Moreau</span>
      <mv-share-button id="mv-share-demo-article" menu
        url="https://lumen.studio/journal/higashiyama-autumn?utm_source=newsletter&amp;utm_medium=email&amp;fbclid=IwAR2xQ"
        title="Autumn in the Higashiyama hills"
        text="Temple gardens, a tea lesson in Gion and the night train to Kanazawa."
        networks="x linkedin whatsapp bluesky"></mv-share-button>
    </div>
  </article>

  <div style="display:flex;align-items:center;gap:.9rem;padding:.75rem .9rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface)">
    <span aria-hidden="true" style="flex:none;width:3rem;height:3rem;border-radius:var(--mv-radius-md);background:linear-gradient(135deg,var(--mv-accent-subtle),var(--mv-bg-muted))"></span>
    <div style="flex:1;min-width:0">
      <strong style="display:block;font-size:.9rem">Kanazawa lacquer tray</strong>
      <span style="font-size:.8rem;color:var(--mv-fg-muted)">$68.00 · Ships in 2 days</span>
    </div>
    <mv-share-button data-variant="icon" url="https://lumen.studio/shop/lacquer-tray?ref=home&amp;gclid=Cj0KCQ" title="Kanazawa lacquer tray" networks="facebook reddit"></mv-share-button>
    <mv-share-button data-variant="ghost" menu url="https://lumen.studio/invite/7QX2" title="Join Lumen Studio" networks="whatsapp telegram">Invite a friend</mv-share-button>
  </div>

  <p id="mv-share-demo-log" style="margin:0;font-size:.78rem;color:var(--mv-fg-subtle)">Shares counted by the app: 0</p>
</div>

<script type="module">
  const root = document.getElementById("mv-share-demo");
  const log = document.getElementById("mv-share-demo-log");
  let count = 0;
  root.addEventListener("mv-shared", (e) => {
    count += 1;
    log.textContent = `Shares counted by the app: ${count} (last: ${e.detail.target})`;
  });
</script>

API

Attributes

NameTypeDescription
urlstringURL to share, absolute or relative (resolved against the page). Default: the page’s <link rel="canonical">, else the current URL.
titlestringTitle to share (email subject, Reddit and Hacker News title). Default: document.title. Like any title attribute, it also shows as the browser tooltip on the element.
textstringOptional message sent with the link (native sheet, email body, X, WhatsApp, Bluesky, Telegram, Threads); the title is used when it is missing.
networksstringSpace or comma separated share targets added to the menu, shown in the order given. Available: x, bluesky, linkedin, facebook, reddit, whatsapp, telegram, threads, hackernews. Unknown names are ignored. Copy link and Email are always there.
keep-paramsbooleanShare the URL as is. By default, campaign and click-id parameters (utm_*, mtm_*, pk_*, hsa_*, mc_*, fbclid, gclid, gbraid, wbraid, msclkid, dclid, yclid, ttclid, twclid, igshid, li_fat_id, _ga, _gl, srsltid…) are removed; every other parameter and the #hash are kept.
menubooleanAlways open the menu, even where the native sheet exists; the native sheet is then its last item (“More options”).
data-variantghost | iconghost: no border or background (toolbars). icon: round icon button, the label stays as its accessible name.
data-openbooleanSet by the component while the menu is open (for styling).

Properties

NameTypeDescription
shareData{ url, title, text? }What will be shared, with the cleaned absolute URL (read-only).
stringsobjectTranslatable labels, merged with the defaults: { share: "Share", menu: "Share" (menu accessible name), copy: "Copy link", copied: "Link copied", copyFailed: "Couldn’t copy the link", email: "Email", more: "More options" }. Network names are brand names and stay as is. Can be set before the element is defined.

Methods

NameDescription
share()Opens the native sheet (or the menu where it can’t be used). Promise<boolean>: true when the native sheet reported a share.
cleanUrl(href, { keepParams, base }?)Named export: pure, server-safe URL cleaner used by the element (pass base on the server).

Events

NameDescription
mv-shareCancelable, fired when a target is chosen and before anything happens: detail = { target, url, title, text? }, target = native | copy | email | x | linkedin | …. preventDefault() stops the action (e.g. to run your own flow).
mv-sharedThe share went through: the native sheet resolved, the link was copied, or the email or network link was opened. Same detail. Not fired when the person closes the native sheet.

Content structure

NameDescription
(text)Visible label of the default button (default “Share”).
buttonOr your own child <button> (e.g. .mv-button): the share icon is added and the styling stays yours.

CSS classes

NameDescription
mv-share-button-trigger / -label / -iconDefault button, its label and the icons.
mv-share-button-menu / -url / -sep / -item / -text / -monoThe popover menu, the cleaned URL preview (aria-hidden), its separator, the items and the neutral network monograms. Items carry data-target and, after a copy, data-state="copied | error".

CSS variables

NameDefaultDescription
--mv-share-button-heightvar(--mv-control-md)Button height (and icon button size).
--mv-share-button-menu-bgvar(--mv-surface-raised)Menu background.
--mv-share-button-highlightcolor-mix(in oklab, var(--mv-fg) 8%, transparent)Hovered or focused item background.
--mv-share-button-successvar(--mv-success)Color of the “Link copied” confirmation.

Accessibility

A real <button> labelled “Share” (visually hidden, never removed, in the icon variant). Where it opens the native sheet it is a plain button; where it opens the menu it gets aria-haspopup="menu" and aria-expanded and is the popover invoker, so the platform handles light dismiss and Escape and returns focus to it. The menu is a top-layer popover with role="menu": opening focuses the first item (ArrowDown or ArrowUp on the button open it on the first or last item); ArrowUp/ArrowDown move with wrap-around, Home/End jump, typing a letter jumps to a matching item, Enter or Space activates (links included), Tab closes the menu and moves on, Escape closes it and returns focus. The pointer moves the focus too, so only one item is ever highlighted. Items are real <button>s and <a href> links (right-click, copy link address and middle-click still work), each with a visible focus ring. The URL preview, the monograms and the icons are aria-hidden, so each item reads once by its label. “Link copied” (or “Couldn’t copy the link”) is announced once by a polite role="status" region outside the menu, then the menu closes and focus returns to the button. Clipboard API with an execCommand fallback. Forced colors: CanvasText border, Highlight on the active item. Reduced motion: no scale on the menu or the check, fades are instant. Without JavaScript nothing is rendered, so no dead control is shown.

Cette page a été traduite par IA. Signaler un problème de traduction