Textarea
A text box for more than one line, with a visible label, an optional hint and an optional error. The label is required: it is what assistive technology reads.
The preview needs JavaScript. The code is in the Code tab.
<template>
<div>
<Textarea rows="3">
<span slot="label">Message</span>
<span slot="hint">Plain text, up to 500 characters.</span>
</Textarea>
<Textarea error="Write at least a few words.">
<span slot="label">Feedback</span>
</Textarea>
</div>
</template>
Installation
npx @pixelscriptio/tessera add Textareapnpm exec tessera add Textareayarn tessera add Textareabunx @pixelscriptio/tessera add Textarea- Works with Tessera 0.9.0 or newer.
Copy and paste the code into your project
Put it in
system/components.system/components/Textarea.tess <contract> { "name": "Textarea", "role": "generic", "description": "A text box for more than one line, with a visible label, an optional hint and an optional error. The label is required: it is what assistive technology reads.", "since": "0.8.0", "parts": ["root", "label", "input", "hint", "error"], "variants": { "size": ["md", "sm"] // first value is the default }, "states": ["hover", "focus-visible", "disabled"], "slots": { "label": "the visible label, required", "hint": "help under the box" }, "events": { "input": "fired with the new text on every change", "change": "fired with the text when the person leaves the box after changing it" }, "preview": { "slots": { "label": "Message", "hint": "Plain text, up to 500 characters." } } } </contract> <script> const { value, placeholder, rows, disabled, invalid, error } = props({ value: '', placeholder: '', rows: 3, disabled: false, invalid: false, error: '', }) // The box owns what is typed, starts from the prop and follows it when it changes. const text = state(value.value) effect(() => { text.value = value.value }) // Ids of its own, so the hint and the error can describe the box however many there are. const uid = `t-textarea-${Math.random().toString(36).slice(2, 9)}` function typed(event) { emit('input', event.target.value) } function committed(event) { emit('change', event.target.value) } </script> <template> <label class="root"> <span class="label"><slot name="label" /></span> <textarea class="input" :rows="rows" :placeholder="placeholder" :disabled="disabled" :aria-invalid="invalid || error ? 'true' : null" :aria-describedby="uid + '-hint ' + uid + '-error'" bind:value="text" @input="typed($event)" @change="committed($event)" ></textarea> <span class="hint" :id="uid + '-hint'"><slot name="hint" /></span> <span class="error" :id="uid + '-error'" aria-live="polite">{error}</span> </label> </template> <style> .root { display: flex; flex-direction: column; gap: $space.1; color: $color.text.default; font-family: var(--t-font-family-sans); font-size: $font.size.sm; } .label { font-size: $font.size.sm; font-weight: $font.weight.bold; } .input { padding: $space.2 $space.3; border: 1px solid $color.border.strong; border-radius: $radius.md; background: $color.surface.default; color: $color.text.default; font: inherit; resize: vertical; transition-duration: $motion.duration.fast; &:hover:not(:disabled) { border-color: $color.text.default; } &:focus-visible { outline: 2px solid $color.focus.ring; outline-offset: 2px; } &:disabled { opacity: $opacity.disabled; cursor: not-allowed; } &[aria-invalid='true'] { border-color: $color.action.danger.text; } } .root[data-size='sm'] .input { padding: $space.1 $space.2; } .hint { color: $color.text.muted; font-size: $font.size.sm; } .error { color: $color.action.danger.text; font-size: $font.size.sm; } .hint:empty, .error:empty { display: none; } </style>Check your design tokens
Your design system needs these.
tessera addreports the ones that are missing; by hand, runnpx @pixelscriptio/tessera check.color.action.danger.textcolor.border.strongcolor.focus.ringcolor.surface.defaultcolor.text.defaultcolor.text.mutedfont.size.smfont.weight.boldmotion.duration.fastopacity.disabledradius.mdspace.1space.2space.3
Usage
Using Textarea
A text box for more than one line: a message, a bio, a description.
- The label is required. It is what assistive technology reads. Do not use the placeholder as the label.
- Use the
hintslot for help, and theerrorprop for what is wrong. The error is announced when it appears, is read with the box, and turns the border red. Settingerroralso marks the box invalid, so you do not needinvalidas well. rowsis how tall it starts (3 by default). The person can drag it taller.inputfires on every change with the new text, andchangefires when the person leaves the box.
The preview needs JavaScript. The code is in the Code tab.
<template>
<div>
<Textarea rows="3">
<span slot="label">Message</span>
<span slot="hint">Plain text, up to 500 characters.</span>
</Textarea>
<Textarea error="Write at least a few words.">
<span slot="label">Feedback</span>
</Textarea>
</div>
</template>
API reference
Variants
| Variant | Values |
|---|---|
size | md default sm |
Slots
| Slot | Content |
|---|---|
label | the visible label, required |
hint | help under the box |
Events
| Event | When |
|---|---|
input | fired with the new text on every change |
change | fired with the text when the person leaves the box after changing it |
Anatomy
| Role | generic |
| Parts | root label input hint error |
| States | hover focus-visible disabled |
| Since | 0.8.0 |
Design tokens it uses
Your design system needs these. tessera add checks that it has them, and can add the ones it lacks.
color.action.danger.text color.border.strong color.focus.ring color.surface.default color.text.default color.text.muted font.size.sm font.weight.bold motion.duration.fast opacity.disabled radius.md space.1 space.2 space.3
Checksum
b9cf09701d21e05dd2ffc3b79112d8d317e2a6efaefd40e12554bfb7cfd5931e