Mediquo documentation home

Button

Starts an action, or navigates when it is a link.

mq-button is the console's Button as a design-system primitive. It ships from @mediquo/ui as a custom element and as a React component.

Install

// Custom element — the import registers <mq-button>
import "@mediquo/ui/components/button";
// React
import { Button } from "@mediquo/ui/react/button";

Variants and sizes

variant is one of primary, secondary, light, negative, outline-negative, ghost, ghost-negative, link and outline.

size is one of xs, sm, md (the default) and lg, or one of the square icon sizes icon-xs, icon-sm, icon, icon-lg and icon-xl. full-width stretches the button to its container.

The console reaches links through Radix's asChild. A custom element cannot adopt its child's tag, so href replaces it: the button renders a real <a> and keeps link semantics — middle-click, "open in new tab", and announced as a link rather than a button that happens to navigate.

<Button variant="outline" href="/citas">
  Ver mis citas
</Button>

target="_blank" gets rel="noopener noreferrer" unless you pass your own rel.

Forms

type defaults to "button", not the HTML default "submit", so a button dropped into a form does not submit it by accident. The control lives in a shadow root, where it cannot reach an outer <form> on its own; the element is form-associated and forwards type="submit" and type="reset" to its form.

Accessibility

  • Icon-only buttons need label. The icon* sizes render no text, so label becomes the aria-label — the component warns in the console when it is missing. Mark the icon itself aria-hidden.
  • loading keeps focus where it is. Unlike disabled, a loading button stays in the tab order, so focus is not thrown back to the page mid-action. It ignores clicks and reports aria-busy and aria-disabled; the spinner is aria-hidden.
  • Toggle and disclosure state are opt-in: pressed sets aria-pressed, expanded sets aria-expanded and haspopup sets aria-haspopup. Left unset, the attributes are absent — aria-pressed="false" would announce an ordinary button as an unpressed toggle.
  • A disabled link drops its href, so it leaves the tab order and cannot navigate, and keeps role="link" with aria-disabled so it is still announced as a link.
  • The focus ring is a 2px outline with an offset, which holds the 3:1 contrast a focus indicator needs on every fill. Forced-colours mode draws a border so the shape survives, and the transitions are dropped under prefers-reduced-motion: reduce.

Styling

The component uses --mq-* tokens with literal fallbacks. --mq-button-radius overrides the corner radius; part="control" reaches the inner <button> or <a>, and part="spinner" the loading indicator.

Differences from the console

  • ghost-destructive is gone: it drew the same red text on the same red hover as ghost-negative. Use ghost-negative.
  • ghost and outline use the primary text colour instead of the secondary grey, which fell under 4.5:1 contrast on a white background.