Exclusivo

Error Page <mv-error-page>

Páginas 404, 500 y 503 listas para usar en el registro editorial: el código de estado compuesto como una cifra, un título de dos tonos, una ficha técnica con reglas (ID de solicitud para copiar, ventana de mantenimiento en la zona horaria de quien visita), búsqueda del sitio o registro de alertas, y enlaces útiles; diseño dividido opcional con una lámina ilustrada.

CategoríaBloques
TipoBloque (sección lista para usar) (<mv-error-page>)
Estadoestable
También instalabutton, copy-button, input
Keywordsexclusive, error, 404, 500, 503, not found, server error, maintenance, status page, request id, landing, editorial

When to use

  • A site or app needs its 404 page with site search and links back to the main sections
  • A server error page must offer a retry, a status page link and a request ID that users can copy for support
  • A planned maintenance page should show when the service returns in the visitor's own time zone and offer an email alert
  • An error page should sit beside a sober illustration or background motif instead of a plain column

Avoid when

  • A list, table or search inside a working page simply has no results → use Empty instead
  • One request failed but the rest of the page still works, so a banner or inline message is enough → use Alert instead
  • The section opens a marketing page rather than replacing a page that failed → use Hero instead

Instalación

node scripts/add.mjs error-page --out ./src/marvelous

Agente de IA con el servidor MCP de Marvelous UI: install_components({ slugs: ["error-page"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Archivos copiados (dependencias incluidas): tokens/tokens.css, core/base.css, components/button/button.css, core/element.js, core/motion.js, components/copy-button/copy-button.js, components/copy-button/copy-button.css, components/input/input.css, components/error-page/error-page.css, components/error-page/error-page.js.

Uso

Marcado de referencia: parte de él y personalízalo con atributos, data-* y variables CSS:

<mv-tabs id="ep-demo" value="not-found" data-size="sm" style="gap:0">
  <style>
    #ep-demo > [role="tablist"] { justify-self: center; margin: .75rem auto; }
    #ep-demo .mv-error-page { --mv-error-page-min-height: 0; }
  </style>

  <div role="tablist" aria-label="Error page variant">
    <button role="tab" data-value="not-found">Not found</button>
    <button role="tab" data-value="server-error">Server error</button>
    <button role="tab" data-value="maintenance">Maintenance</button>
    <button role="tab" data-value="split">Split</button>
  </div>

  <!-- ═════════ 404: not found (default) ═════════ -->
  <div role="tabpanel" data-value="not-found">
    <mv-error-page>
      <section class="mv-error-page" data-variant="not-found" aria-labelledby="ep-nf-title">
        <div class="mv-error-page-inner">
          <p class="mv-error-page-meta">
            <span class="mv-error-page-kicker">Error 404</span>
            <span>Page not found</span>
            <span class="mv-error-page-path">/pricing/enterprize</span>
          </p>
          <p class="mv-error-page-code" aria-hidden="true">404</p>
          <div class="mv-error-page-content">
            <h1 class="mv-error-page-title" id="ep-nf-title">We can’t find that page. <span class="mv-error-page-muted">It may have moved.</span></h1>
            <p class="mv-error-page-lead">The link may be out of date, or the address has a typo. Search the site, or pick up again from one of the pages below.</p>
            <form class="mv-error-page-form" role="search" action="#ep-demo" data-demo-search>
              <label class="mv-sr-only" for="ep-nf-q">Search Ledgerline</label>
              <div class="mv-input-group" data-size="lg">
                <label class="mv-input-group-addon" for="ep-nf-q"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg></label>
                <input class="mv-input" id="ep-nf-q" name="q" type="search" placeholder="Search guides, pricing and docs" autocomplete="off">
                <button class="mv-button" data-size="sm" type="submit">Search</button>
              </div>
            </form>
            <p class="mv-error-page-note">Did you mean <a href="#ep-demo"><span class="mv-error-page-path">/pricing/enterprise</span></a>?</p>
          </div>
          <nav class="mv-error-page-links" aria-label="Helpful pages">
            <ul>
              <li><a href="#ep-demo"><span class="mv-error-page-link-label">Home</span><span class="mv-error-page-link-text">Product tour, customers and plans</span></a></li>
              <li><a href="#ep-demo"><span class="mv-error-page-link-label">Documentation</span><span class="mv-error-page-link-text">Guides, API reference and changelog</span></a></li>
              <li><a href="#ep-demo"><span class="mv-error-page-link-label">Help center</span><span class="mv-error-page-link-text">Answers from our support team, 24/7</span></a></li>
              <li><a href="#ep-demo"><span class="mv-error-page-link-label">System status</span><span class="mv-error-page-link-text">Live uptime for every region</span></a></li>
            </ul>
          </nav>
        </div>
      </section>
    </mv-error-page>
  </div>

  <!-- ═════════ 500: server error ═════════ -->
  <div role="tabpanel" data-value="server-error">
    <mv-error-page id="ep-se">
      <section class="mv-error-page" data-variant="server-error" aria-labelledby="ep-se-title">
        <div class="mv-error-page-inner">
          <p class="mv-error-page-meta">
            <span class="mv-error-page-kicker">Error 500</span>
            <span>Internal server error</span>
            <a href="#ep-demo">status.ledgerline.com →</a>
          </p>
          <p class="mv-error-page-code" aria-hidden="true">500</p>
          <div class="mv-error-page-content">
            <h1 class="mv-error-page-title" id="ep-se-title">Something broke on our side. <span class="mv-error-page-muted">Your data is safe.</span></h1>
            <p class="mv-error-page-lead">The server hit an unexpected error while loading your invoices. Our on-call engineers have been alerted, and trying again usually works within a minute.</p>
            <div class="mv-error-page-actions">
              <a class="mv-button" href="" data-retry>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 4v5h-5"/></svg>
                <span data-retry-label>Try again</span>
              </a>
              <a class="mv-button" data-variant="outline" href="#ep-demo">View system status</a>
            </div>
            <p class="mv-error-page-status" id="ep-se-status" role="status" aria-live="polite"></p>
            <p class="mv-error-page-note">Still stuck? <a href="#ep-demo">Contact support</a> and include the request ID below.</p>
          </div>
          <dl class="mv-error-page-facts">
            <div><dt>Status</dt><dd>500 Internal Server Error</dd></div>
            <div><dt>Request ID</dt><dd><code id="ep-se-ref">req_01J8X4K2Q7M9WZ</code><mv-copy-button target="#ep-se-ref" data-variant="ghost" label="Copy request ID" success-label="Request ID copied"></mv-copy-button></dd></div>
            <div><dt>Time</dt><dd><time datetime="2026-09-25T14:32:08Z">Sep 25, 2026, 2:32 PM UTC</time></dd></div>
          </dl>
        </div>
      </section>
    </mv-error-page>
  </div>

  <!-- ═════════ 503: maintenance ═════════ -->
  <div role="tabpanel" data-value="maintenance">
    <mv-error-page id="ep-mt">
      <section class="mv-error-page" data-variant="maintenance" aria-labelledby="ep-mt-title">
        <div class="mv-error-page-inner">
          <p class="mv-error-page-meta">
            <span class="mv-error-page-kicker">Scheduled maintenance</span>
            <span>Error 503</span>
            <a href="#ep-demo">status.ledgerline.com →</a>
          </p>
          <p class="mv-error-page-code" aria-hidden="true">503</p>
          <div class="mv-error-page-content">
            <h1 class="mv-error-page-title" id="ep-mt-title">We’re down for planned maintenance. <span class="mv-error-page-muted">Back shortly.</span></h1>
            <p class="mv-error-page-lead">We’re moving every workspace to faster storage. <strong>Nothing is lost</strong>, and bank feeds catch up on their own once we’re back.</p>
            <form class="mv-error-page-form" id="ep-mt-form" action="#ep-demo" novalidate>
              <label for="ep-mt-email">Get an email when we’re back</label>
              <div class="mv-input-group" data-size="lg">
                <label class="mv-input-group-addon" for="ep-mt-email"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg></label>
                <input class="mv-input" id="ep-mt-email" name="email" type="email" placeholder="[email protected]" autocomplete="email" aria-describedby="ep-mt-status">
                <button class="mv-button" data-size="sm" type="submit">Notify me</button>
              </div>
              <p class="mv-error-page-status" id="ep-mt-status" role="status" aria-live="polite"></p>
            </form>
            <p class="mv-error-page-note">Live progress on the <a href="#ep-demo">status page</a>. One email, then we delete your address.</p>
          </div>
          <dl class="mv-error-page-facts">
            <div><dt>Started</dt><dd><time id="ep-mt-start" datetime="2026-09-25T13:00:00Z">Sep 25, 1:00 PM UTC</time></dd></div>
            <div><dt>Back by</dt><dd><time id="ep-mt-end" datetime="2026-09-25T16:30:00Z" data-relative>Sep 25, 4:30 PM UTC</time></dd></div>
            <div><dt>Affected</dt><dd>Web app, API and mobile sync</dd></div>
          </dl>
        </div>
      </section>
    </mv-error-page>
  </div>

  <!-- ═════════ Split layout (combinable with every variant) ═════════ -->
  <div role="tabpanel" data-value="split">
    <mv-error-page>
      <section class="mv-error-page" data-variant="not-found" data-layout="split" aria-labelledby="ep-sp-title" style="--mv-error-page-bg:var(--mv-bg-subtle)">
        <div class="mv-error-page-inner">
          <div class="mv-error-page-main">
            <p class="mv-error-page-meta">
              <span class="mv-error-page-kicker">Error 404</span>
              <span>Careers</span>
            </p>
            <div class="mv-error-page-content">
              <h1 class="mv-error-page-title" id="ep-sp-title">This role has been filled. <span class="mv-error-page-muted">Fourteen others are open.</span></h1>
              <p class="mv-error-page-lead">The posting for <strong>Senior Product Designer, Lisbon</strong> closed on August 14. Similar roles are listed below, and new ones open every month.</p>
              <div class="mv-error-page-actions">
                <a class="mv-button" href="#ep-demo">See open roles</a>
                <a class="mv-button" data-variant="ghost" href="#ep-demo">Get job alerts →</a>
              </div>
            </div>
            <nav class="mv-error-page-links" aria-label="Similar roles">
              <ul>
                <li><a href="#ep-demo"><span class="mv-error-page-link-label">Product Designer</span><span class="mv-error-page-link-text">Berlin or remote in Europe</span></a></li>
                <li><a href="#ep-demo"><span class="mv-error-page-link-label">Design Systems Lead</span><span class="mv-error-page-link-text">Lisbon, hybrid</span></a></li>
              </ul>
            </nav>
          </div>
          <figure class="mv-error-page-visual" aria-hidden="true">
            <div class="mv-error-page-plate">
              <svg viewBox="0 0 400 300" preserveAspectRatio="xMaxYMin meet" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
                <path d="M40 150 C 90 150, 100 92, 150 92 S 220 132, 262 118 S 318 64, 340 58" stroke-dasharray="3 6"/>
                <rect x="36" y="146" width="8" height="8" fill="currentColor" stroke="none"/>
                <rect x="146" y="88" width="8" height="8" fill="currentColor" stroke="none"/>
                <rect x="258" y="114" width="8" height="8" fill="currentColor" stroke="none"/>
                <g style="color:var(--mv-error-page-accent)">
                  <rect x="328" y="46" width="24" height="24" stroke-dasharray="4 4"/>
                  <path d="M336 54l8 8M344 54l-8 8"/>
                </g>
              </svg>
              <p class="mv-error-page-code">4<span>0</span>4</p>
            </div>
            <figcaption class="mv-error-page-caption"><span><b>Fig. 404</b> · /careers/senior-product-designer-lisbon</span><span>Closed Aug 14</span></figcaption>
          </figure>
        </div>
      </section>
    </mv-error-page>
  </div>

  <script type="module">
    const root = document.querySelector("#ep-demo");

    // Search: stays on the page in this demo (a real form goes to /search).
    for (const form of root.querySelectorAll("[data-demo-search]")) form.addEventListener("submit", (e) => e.preventDefault());

    // Retry: cancel the reload here and report the attempt instead.
    const seStatus = root.querySelector("#ep-se-status");
    let seTimer;
    root.querySelector("#ep-se")?.addEventListener("mv-retry", (e) => {
      e.preventDefault();
      const control = e.detail.control;
      control.setAttribute("aria-busy", "true");
      control.setAttribute("aria-disabled", "true");
      clearTimeout(seTimer);
      seTimer = setTimeout(() => {
        control.removeAttribute("aria-busy");
        control.removeAttribute("aria-disabled");
        const at = new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit", second: "2-digit" }).format(new Date());
        seStatus.dataset.state = "error";
        seStatus.textContent = `Still unavailable at ${at}. We’ll keep the page ready for your next try.`;
      }, 900);
    });

    // Maintenance window around the current time, so the relative note reads well.
    const step = 15 * 60_000, now = Date.now();
    const start = new Date(Math.floor((now - 50 * 60_000) / step) * step);
    const end = new Date(Math.ceil((now + 2 * 3600_000) / step) * step);
    const fallback = (d) => new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", timeZone: "UTC", timeZoneName: "short" }).format(d);
    for (const [id, d] of [["#ep-mt-start", start], ["#ep-mt-end", end]]) {
      const time = root.querySelector(id);
      if (time) { time.dateTime = d.toISOString(); time.textContent = fallback(d); }
    }
    customElements.whenDefined("mv-error-page").then(() => root.querySelector("#ep-mt")?.refresh?.());

    // Alert signup: validate and confirm in the live region.
    const form = root.querySelector("#ep-mt-form");
    const status = root.querySelector("#ep-mt-status");
    const input = form?.querySelector("input");
    form?.addEventListener("submit", (e) => {
      e.preventDefault();
      const email = input.value.trim();
      if (!email || !input.checkValidity()) {
        status.dataset.state = "error";
        status.textContent = "Enter an email address like [email protected].";
        input.setAttribute("aria-invalid", "true");
        return;
      }
      input.removeAttribute("aria-invalid");
      input.value = "";
      status.dataset.state = "success";
      status.textContent = `Done. We’ll write to ${email} as soon as we’re back.`;
    });
  </script>
</mv-tabs>

API

Attributes

NameTipoDefaultDescription
data-variantnot-found | server-error | maintenancenot-foundOn .mv-error-page: the kind of error. Sets the color of the kicker mark (accent, danger, warning); the content pattern of each kind is shown in the demo.
data-layoutsplitOn .mv-error-page: text in .mv-error-page-main on one side, figure.mv-error-page-visual on the other (from 56rem). Works with every variant.
data-motifgrid | dots | nonegridOn .mv-error-page-plate: background motif, faded toward the code.
data-retrybooleanOn a control (an <a href=""> reloads even without JS): with <mv-error-page>, fires mv-retry, then reloads the page. Put the label in a [data-retry-label] child to have it replaced while reloading.
data-relativebooleanOn a <time datetime>: <mv-error-page> adds a relative note after it (“in 2 hours”), refreshed every minute.
data-formatauto | time | dateautoOn a <time datetime>: auto shows the time alone when it is today, else weekday, date and time, always with the viewer's time zone.
localestringOn <mv-error-page>: locale for dates and relative times. Default: the nearest lang attribute, then en-US.

Properties

NameTipoDescription
stringsobjectOverride the default text: retrying (“Reloading…”), soon (“any minute now”), overdue (“taking longer than planned”). Can be set before the element is defined.

Methods

NameDescription
refresh()Re-format every <time datetime> and relative note (call it after changing a datetime).

Events

NameDescription
mv-retryCancelable, fired by a [data-retry] control before the reload; detail = { control }. Call preventDefault() to retry in place (refetch in a single-page app).

Content structure

NameDescription
.mv-error-page-innerDefault layout: -meta, -code, -content, then optionally -facts and -links. From 48rem the code and facts sit left of the content.
.mv-error-page-contentCopy: -title (with -muted), -lead, then -actions, -form, -status and -note as needed.
form.mv-error-page-formSite search (role="search") or an alert signup: a label (visible or .mv-sr-only), an .mv-input-group with its button, and an optional -status.
dl.mv-error-page-factsRuled spec sheet: div > dt (mono label) + dd. Holds the status line, a request ID with <mv-copy-button>, or the maintenance window in <time> elements.
nav.mv-error-page-linksHelpful links: ul > li > a > -link-label + -link-text. 1, 2, then up to 4 columns.
figure.mv-error-page-visualSplit layout: .mv-error-page-plate (motif, optional <svg> or <img> behind, the -code) + figcaption.mv-error-page-caption.

CSS classes

NameDescription
mv-error-pageRoot section, a query container named mv-error-page.
mv-error-page-meta / -kickerMono uppercase meta line on a hairline; the kicker gets a small colored square. A trailing item (path, status link) is pushed to the end.
mv-error-page-codeThe status code set like a figure. Decorative: add aria-hidden="true", the kicker and title carry the meaning. A <span> inside greys one digit.
mv-error-page-pathMono path or identifier inside running text or the meta line; long values wrap.
mv-error-page-statusForm or retry feedback (role="status"), hidden when empty; data-state="success | error" colors it.
mv-error-page-mainSplit layout: wraps the text side (meta, content, facts, links).
mv-error-page-relativeRelative note added after a <time data-relative>.

CSS variables

NameDefaultDescription
--mv-error-page-max72remMax content width.
--mv-error-page-min-height100svhMinimum height; content is centered vertically. Set 0 inside a layout with a header.
--mv-error-page-bgvar(--mv-bg)Section background.
--mv-error-page-rulevar(--mv-border-strong)Color of the main hairlines (meta, facts, links).
--mv-error-page-accentvar(--mv-accent)Kicker mark of the not-found variant, and the accent of a plate illustration.

Accessibility

One <h1> per page: the title states the problem in words, the kicker names the error, and the big code is aria-hidden, so the meaning never rests on the number alone. Set document.title to match (“Page not found · Ledgerline”) and return the real HTTP status from the server (404, 500, 503 with Retry-After) so crawlers and assistive tech are not told the page worked. The search form has role="search" and a label; the links sit in a labeled <nav>; form and retry feedback go to a polite role="status" region, errors spelled out in text. The retry control gets aria-busy and aria-disabled while it reloads. Times are real <time datetime> elements, rewritten in the viewer's time zone with a full-date tooltip. Keyboard: native links, inputs and buttons only. The plate and its illustration are aria-hidden. Reduced motion: no entrance. Forced colors: marks and hairlines use system colors, the motif is dropped.

Esta página se tradujo con IA. Informar de un problema de traducción