# Signature

> A signature pad lets users sign with a mouse, a pen, or a finger. Use it to confirm an order, accept a contract, or hand over a delivery.

Collect a drawn signature from users.

Source: https://docs.tabler.io/ui/plugins/signature

---

## Overview

The signature pad is a `canvas` element with Tabler styles around it. Drawing is handled by the [Signature Pad](https://github.com/szimek/signature_pad) library.

Use the `signature` class on the wrapper and `signature-canvas` on the canvas. The wrapper draws a solid border, and the canvas adds a dashed one, so the signing area is easy to see. The cursor turns into a crosshair over the canvas.

Try it below. Draw with the mouse, or with a finger on a touch screen.

```html
<div class="signature position-relative" style="width: 100%; max-width: 26rem;">
  <canvas id="signature-overview" class="signature-canvas" width="400" height="200" style="height: 10rem"></canvas>
</div>
```

## Installation

The library is not part of the Tabler bundle. Install it with npm:

```shell
npm install signature_pad
```

Or include it from a CDN:

```html
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.0/dist/libs/signature_pad/dist/signature_pad.umd.min.js"></script>
```

## Usage

### Markup

Wrap the canvas in a `signature` element. Set `width` and `height` on the canvas, so it has a size before the script runs.

Also set the height in CSS. Tabler styles the canvas with `width: 100%`, so without a CSS height the canvas keeps the ratio of its attributes. The resize code below then reads a new height on every run, and the pad gets shorter each time.

```html
<div class="signature position-relative">
  <canvas id="signature-default" class="signature-canvas" width="400" height="400" style="height: 16rem"></canvas>
</div>
```

### Init the pad

Create the pad after the DOM is ready. Set a transparent background, so the canvas follows the card or the page. Take the pen color from the canvas text color, so the signature also works in dark mode.

```js
document.addEventListener('DOMContentLoaded', function () {
  const canvas = document.getElementById('signature-default')

  const signaturePad = new SignaturePad(canvas, {
    backgroundColor: 'transparent',
    penColor: getComputedStyle(canvas).color,
  })
})
```

### Keep the drawing sharp

A canvas has two sizes: the CSS size and the pixel size. On a retina screen they differ, and the line looks blurry. Scale the canvas to the device pixel ratio, and do it again on resize.

```js
function resizeCanvas() {
  const ratio = Math.max(window.devicePixelRatio || 1, 1)

  canvas.width = canvas.offsetWidth * ratio
  canvas.height = canvas.offsetHeight * ratio
  canvas.getContext('2d').scale(ratio, ratio)

  // resizing clears the canvas, so put the drawing back
  signaturePad.fromData(signaturePad.toData())
}

window.addEventListener('resize', resizeCanvas)
resizeCanvas()
```

### Clear button

Place a button in the corner of the wrapper and call `clear()` on it. The wrapper needs `position-relative` for this.

```html
<div class="signature position-relative" style="width: 100%; max-width: 26rem;">
  <div class="position-absolute top-0 end-0 p-2">
    <button type="button" class="btn btn-icon" id="signature-clear-button" title="Clear signature">
      <!-- Download SVG icon from http://tabler.io/icons/icon/trash -->
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon">
        <path d="M4 7l16 0" />
        <path d="M10 11l0 6" />
        <path d="M14 11l0 6" />
        <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
        <path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
      </svg>
    </button>
  </div>
  <canvas id="signature-clear" class="signature-canvas" width="400" height="200" style="height: 10rem"></canvas>
</div>
```

```js
document.getElementById('signature-clear-button').addEventListener('click', function () {
  signaturePad.clear()
})
```

### Pen color

Change `penColor` at any time. New strokes use the new color, and the old ones keep theirs.

```js
document.getElementById('pen-color').addEventListener('input', function (event) {
  signaturePad.penColor = event.target.value
})
```

### Save the signature

Use `toDataURL()` to read the drawing. Without arguments you get a PNG. Pass `image/svg+xml` for a vector file. Check `isEmpty()` first, so you do not save a blank pad.

```js
if (signaturePad.isEmpty()) {
  // ask the user to sign first
} else {
  const png = signaturePad.toDataURL()
  const svg = signaturePad.toDataURL('image/svg+xml')
}
```

Use `fromDataURL()` to show a signature you saved earlier.

### Pad inside a modal

A canvas has no size while the modal is hidden, so the pad must start after the modal opens. Listen for the Bootstrap `shown.bs.modal` event instead of `DOMContentLoaded`.

```js
document.getElementById('modal-signature').addEventListener('shown.bs.modal', function () {
  // create the pad here
})
```

## Examples

### Signature in a form

Put the pad in a form group with a label, the same as any other field. Add the legal text under it when the signature is binding.

```html
<div class="card">
  <div class="card-body">
    <h3 class="card-title">Confirm transfer</h3>
    <div class="mb-3">
      <label class="form-label required">Signature</label>
      <div class="signature position-relative">
        <div class="position-absolute top-0 end-0 p-2">
          <button type="button" class="btn btn-icon" id="signature-form-clear" title="Clear signature">
            <!-- Download SVG icon from http://tabler.io/icons/icon/trash -->
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon">
              <path d="M4 7l16 0" />
              <path d="M10 11l0 6" />
              <path d="M14 11l0 6" />
              <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
              <path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
            </svg>
          </button>
        </div>
        <canvas id="signature-form" class="signature-canvas" width="400" height="180" style="height: 9rem"></canvas>
      </div>
    </div>
    <div class="text-secondary fs-5"> I agree that this signature is the electronic representation of my signature for all purposes when I use it on documents. </div>
    <div class="mt-4 d-flex">
      <button type="button" class="btn">Cancel</button>
      <button type="button" class="btn btn-primary ms-auto">Confirm transfer</button>
    </div>
  </div>
</div>
```

## Accessibility

- A canvas cannot be used with a keyboard. Offer a second way to sign, for example a text field where the user types their full name.
- Give the pad a real `<label>`, so users know what they sign. Link it to the field with `for` and `id`.
- Use a `<button type="button">` for the clear action. A `div` with button classes is not reachable with the keyboard.
- Tell users what happens with the drawing. A signature is personal data, so say where you store it and for how long.
- Do not rely on the dashed border alone. Add a short hint, for example `Sign inside the box`.

<script>{`
document.addEventListener('DOMContentLoaded', function () {
	if (typeof SignaturePad === 'undefined') return;

	['signature-overview', 'signature-clear', 'signature-form'].forEach(function (id) {
		const canvas = document.getElementById(id);
		if (!canvas) return;

		const signaturePad = new SignaturePad(canvas, {
			backgroundColor: 'transparent',
			penColor: getComputedStyle(canvas).color,
		});

		function resizeCanvas() {
			const ratio = Math.max(window.devicePixelRatio || 1, 1);
			canvas.width = canvas.offsetWidth * ratio;
			canvas.height = canvas.offsetHeight * ratio;
			canvas.getContext('2d').scale(ratio, ratio);
			signaturePad.fromData(signaturePad.toData());
		}

		window.addEventListener('resize', resizeCanvas);
		resizeCanvas();

		const button = document.getElementById(id + '-button') || document.getElementById(id + '-clear');
		if (button) {
			button.addEventListener('click', function () {
				signaturePad.clear();
			});
		}
	});
});
`}</script>
