Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelogApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip
ARC UIARC Radiant Components
v4.9DocsComponentsTokensSynthesizer
Getting StartedFrameworksServer RenderingDesign TokensThemingTheme SynthesizerTypographyUtilitiesAll ComponentsAccessibilityBrowser SupportChangelogContributingStatsApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip

Signature Pad

Canvas signature capture that participates in forms: freehand strokes serialize to a PNG data-URL and submit as the field value.

ComponentsSignature Pad
inputinteractive
<arc-signature-pad>

Overview

SignaturePad is a bordered drawing surface for capturing a handwritten signature. While blank, a muted "Sign here" baseline invites the first stroke and disappears the moment ink goes down. The pen line scales with stroke velocity (slow movement thickens it and fast flicks thin it), so a mouse or finger produces something that reads as handwriting rather than a polyline. The pen color follows `--text-primary` by default and accepts any CSS color, including a `var()` reference, through the `pen-color` attribute. The component is a real form control. Each completed stroke serializes the whole canvas to a PNG data-URL and submits it under `name`; a blank pad submits nothing, and `required` on a blank pad reports `valueMissing` like any other input. The stroke is the edit unit: `arc-input` and `arc-change` fire together once per completed stroke, never per point, each carrying the data-URL in `event.detail.value`. Once signed, a small ghost clear button appears in the top corner; clearing empties the value, fires `arc-clear`, and brings the placeholder back. `clear()` does the same thing from script: it wipes the canvas, empties the value, and restores the placeholder. Call it from your own "start over" control, or after a failed submit that should not leave a stale signature in a form the user is about to retry. `toDataURL(type)` exports the current image in a format other than the PNG the value carries. Signing by hand is a pointer gesture, and the pad offers no keyboard path to produce a signature. The canvas exposes `role="img"` with an accessible name that announces its signed or empty state, it stays focusable, and the clear button is keyboard-reachable. But if your form must be completable without a pointer, you need to offer an equivalent alongside the pad, such as a type-to-sign text field or a file upload. The component does not simulate one.

Guidelines

When to use

  • Give the pad a `label`: it doubles as the accessible name of the canvas
  • Set `required` when a signature is mandatory; the pad reports `valueMissing` while blank exactly like a native input
  • Offer a keyboard-accessible alternative (type-to-sign, upload) next to the pad when the form must be completable without a pointer: the pad itself is pointer-only by nature
  • Listen for `arc-change` to persist or preview the signature; each completed stroke delivers the full, current image
  • Use `readonly` to display a captured signature that still submits but can no longer be altered
  • Call `clear()` when your own flow invalidates the signature: a changed name field, a rejected submission, a switched signer

When not to use

  • Do not reflect or store the value as an attribute: a data-URL is far too large; read the `value` property or the event detail instead
  • Do not treat a signature image as proof of identity on its own; pair it with real authentication when it matters legally
  • Do not listen per-point for drawing progress; the component stays silent until a stroke completes
  • Do not hide the clear button behind your own chrome: a signer who slips needs an obvious way to start over
  • Avoid very small pads; under about 240px of width there is not enough room for a natural signing motion

Features

  • Freehand drawing with pointer capture: mouse, touch, and stylus all work, and touch never scrolls the page mid-stroke
  • Velocity-scaled pen width (up to 40% thicker or thinner around the `pen-width` base) with midpoint-smoothed curves
  • Serializes to a PNG data-URL after every completed stroke; `toDataURL(type)` exports other formats on demand
  • Full form participation: submits under `name`, `required` + blank reports `valueMissing`, `form.reset()` restores the initial state
  • The stroke is the edit unit: `arc-input` and `arc-change` fire once per stroke, never per point
  • Ghost clear button appears once signed; clearing fires `arc-clear` and restores the "Sign here" placeholder
  • `clear()` resets the pad from script: same wipe, same `arc-clear`, same placeholder as the button
  • Pen color resolves CSS custom properties at stroke time, so it follows theme changes
  • Crisp on high-DPI screens: the backing store tracks `devicePixelRatio`, and completed strokes survive a resize
  • Setting `value` from script draws the image back onto the canvas, so a saved signature can be restored for review

Preview

Usage

This component requires JavaScript. No pure HTML/CSS version is available. Use the Web Component directly or a framework wrapper.

<form>
  <arc-signature-pad
    label="Signature"
    name="signature"
    required
  ></arc-signature-pad>
</form>

<script>
  const pad = document.querySelector('arc-signature-pad');
  // Fired once per completed stroke with the PNG data-URL
  pad.addEventListener('arc-change', (e) => {
    console.log('Signature updated:', e.detail.value.slice(0, 40) + '…');
  });
  pad.addEventListener('arc-clear', () => {
    console.log('Signature cleared');
  });
</script>

API

SPEED_FULLnumber1.5
Stroke speed (CSS px per ms) at which the pen reaches its thinnest.
valuestring''
The signature as a PNG data-URL, empty string while the pad is blank. Updated after every completed stroke. Setting it from script draws the image onto the canvas (client-side only). Not reflected: a data-URL is far too large to live in an attribute.
namestring''
Form field name the data-URL submits under.
labelstring''
Label text displayed above the pad in the label typography role. Also feeds the canvas's accessible name.
disabledbooleanfalse
Disables interaction, reducing opacity and blocking pointer events. The pad leaves the tab order.
pen-colorstring''
Pen color as any CSS color, including a var() expression, resolved against the canvas at stroke time. Attribute: pen-color. Defaults to the resolved value of --text-primary.
readonlybooleanfalse
Prevents drawing and hides the clear button while the pad stays focusable and the value still submits.
requiredbooleanfalse
When true and the pad is blank, the control is invalid with valueMissing.
penWidthnumber2
Base pen width in CSS pixels. The drawn line scales with stroke velocity, up to 40% thicker on slow, deliberate movement and 40% thinner on fast flicks. Attribute: pen-width. Default 2.
formAssociatedbooleantrue
propertiesobject{ // flag(), unlike `disabled`. The exclusion in props.js is specifically // about form-associated *platform* semantics: a `disabled` content // attribute that is merely present makes the element actually disabled // per the HTML spec, and formDisabledCallback assigns the property back, // so no converter can win. Neither of these is platform-mapped: // `required` is enforced by _computeValidity() below and `readonly` by // each component's own interaction handlers, so the stock converter buys // nothing here and costs the usual bug: `required="false"` read as true, // blocking submission of a form the author meant to leave optional. // Finding #48's shape, across all 26 form controls at once. required: flag(false), readonly: flag(false), }
Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. required participates in constraint validation below; readonly reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
autoValidatesbooleantrue
Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
form
validity
validationMessage

Methods

clear()
Wipe the canvas, empty the value, and bring the placeholder back.
toDataURL(type?)type?: stringstring
The current canvas as a data-URL in the requested format (PNG by default). Unlike value, this reads the canvas directly, so a blank pad returns a blank image rather than an empty string.
checkValidity()boolean
Whether the control currently satisfies its constraints, per the native constraint-validation API. Fires invalid on the element when it does not, and reports nothing to the user.
reportValidity()boolean
As checkValidity(), but also shows the browser's validation message against the control when it fails.

Events

arc-clear
Fired when the pad is cleared, via the clear button or the clear() method.
arc-inputdetail:{ value: string }
Fired once per completed stroke with the serialized data-URL. A stroke is the edit unit: nothing fires per point while the pen is down.
arc-changedetail:{ value: string }
Fired when the pointer session ends and the value serializes. A stroke is a discrete gesture, so each stroke end fires arc-input then arc-change together.

See Also