Monet
@etched/monet 0.0.1

Sensitive input

Ported

A secret, such as an API key, hidden as dots. The show button reveals it until focus leaves the field; the copy button copies it and screen readers hear "Copied". readOnly shows a secret the user may copy but not change. Native <input> attributes and ref pass through to the field.

Kumo source
import { SensitiveInput } from "@etched/monet/sensitive-input";

export function Preview() {
  return <SensitiveInput aria-label="API key" defaultValue="et_live_5c27e0d4b8a91f" />;
}

Installation

import { SensitiveInput } from "@etched/monet/sensitive-input";

Examples

API key

The value shows as dots. The show button reveals it until focus leaves the field; the copy button copies it.

import { SensitiveInput } from "@etched/monet/sensitive-input";

export function ApiKey() {
  return (
    <div className="grid">
      <SensitiveInput label="API key" defaultValue="et_live_5c27e0d4b8a91f" />
    </div>
  );
}

Sizes

import { SensitiveInput } from "@etched/monet/sensitive-input";

export function Sizes() {
  return (
    <div className="grid">
      <SensitiveInput size="sm" aria-label="Webhook secret (small)" defaultValue="whsec_7Hq2Lx9" />
      <SensitiveInput aria-label="Webhook secret" defaultValue="whsec_7Hq2Lx9" />
    </div>
  );
}

Controlled

value and onValueChange. The copy button shows only while there is a value.

Signs each webhook request.

import { useState } from "react";
import { Button } from "@etched/monet/button";
import { SensitiveInput } from "@etched/monet/sensitive-input";

export function Controlled() {
  const [secret, setSecret] = useState("whsec_7Hq2Lx9");

  return (
    <div className="grid">
      <SensitiveInput label="Signing secret" value={secret} onValueChange={setSecret} description="Signs each webhook request." />
      <div className="row">
        <Button size="sm" onClick={() => setSecret(`whsec_${Math.random().toString(36).slice(2, 10)}`)}>
          Rotate
        </Button>
        <Button size="sm" variant="ghost" onClick={() => setSecret("")}>
          Clear
        </Button>
      </div>
    </div>
  );
}

States

readOnly shows a secret the user may copy but not change. A disabled field has no buttons.

Set by the lab. Rotate it on the bench page.

This key has expired.
import { SensitiveInput } from "@etched/monet/sensitive-input";

export function States() {
  return (
    <div className="grid">
      <SensitiveInput label="Bench token" defaultValue="bt_9a1f3c" readOnly description="Set by the lab. Rotate it on the bench page." />
      <SensitiveInput label="Access key" defaultValue="AKIA4EXAMPLE" error="This key has expired." />
      <SensitiveInput label="Legacy token" defaultValue="lt_0000" disabled />
      <SensitiveInput label="New secret" placeholder="Paste the secret" required={false} />
    </div>
  );
}

Accessibility

  • Tab goes from the field to the show button, then to the copy button. Enter or Space presses them.
  • A password field, named by label or aria-label. The show button is named "Show value" (revealLabel) and has aria-pressed; copyLabel names the copy button.
  • Screen readers hear "Copied" from the field's own polite live region. If the clipboard refuses, they hear the copy keys, such as "Press Ctrl+C to copy". copiedLabel and copyKeysLabel translate them.
  • The value hides again when focus leaves the field and its buttons. A disabled field has no buttons.
  • When a page has more than one, set copyLabel to name the secret, such as "Copy API key".

API reference

PropTypeDefaultDescription
labelReactNode–Visible label. Without one, give the control an aria-label.
labelTooltipReactNode–More about the field, behind an info button after the label.
labelTooltipLabelstring"More information"Names the info button.
descriptionReactNode–Help text below the control. Hidden while an error shows.
errorFieldError–
onCopy() => void–Called after each copy.
copyLabelstring"Copy to clipboard"Names the copy button.
copiedLabelstring"Copied"What screen readers hear after a copy.
copyKeysLabel(keys: string) => string"Press ⌘C to copy"Shown when the clipboard fails, with this device's copy keys ("⌘C" or "Ctrl+C").
valuestring–
defaultValuestring–
onValueChange(value: string) => void–Called with the new text on each change.
size"sm" | "base""base"
revealLabelstring"Show value"Names the show button.
classNamestring–Class on the field's frame, for layout.