Textarea
Multi-line text, wired with the same label, hint and error contract as Input.
Code
The three render the same class names, the same ARIA and the same data attributes. That is checked when this site builds: a class in any of these samples that neither the markup contract nor the shipped stylesheet defines fails the build.
<div class="wsu-field">
<label class="wsu-field__label" for="reason">Reason
<span class="wsu-field__required">(required)</span></label>
<textarea class="wsu-textarea" id="reason" name="reason" required
aria-invalid="true" aria-describedby="reason-error"></textarea>
<p class="wsu-field__error" id="reason-error">
<svg class="wsu-icon" aria-hidden="true"><use href="/assets/wayne-ui/icons.svg#wsu-circle-alert"/></svg>
Tell us why you need this override.
</p>
</div>{{-- The error is read from the validation error bag automatically --}}
<x-wsu::form.textarea name="reason" label="Reason" required /><Textarea
v-model="form.reason"
name="reason"
label="Reason"
required
:error="form.errors.reason"
/>Why it works this way
- Shares fieldProps and useField with Input, Select, Checkbox and Radio, so a hint and an error are wired by aria-describedby the same way on every one of them. A hint or an error cannot end up wired only on some fields and not others.
- The stylesheet allows resizing only in the block direction. Letting someone drag a textarea wider than its container would force the page to scroll sideways, which is exactly what 1.4.10 exists to prevent; height is left to grow freely, since a taller field costs nothing.
- No rows is set by default on the Blade path. The stylesheet sets a min-block-size in rem instead, which grows with the reader's own font size where a fixed row count would not.
- autocomplete is left to the caller, the same reasoning as Input: WCAG 1.3.5 wants the correct token, and there is no token this component could guess correctly more often than it would guess wrong.
Accessibility
- A real label with for pointing at the control (1.3.1, 3.3.2).
- aria-invalid and aria-describedby are set from the same error value that renders the message, so the two cannot disagree (3.3.1).
- (required) is text in the label, not an asterisk that only a sighted reader can decode.
Success criteria in brackets refer to WCAG 2.2. Every one of them is checked by axe-core in both themes at three viewport widths before this page can be published. See Accessibility for what the gates are.
Props
Blade
| Attribute | Default | What it does |
|---|---|---|
name | required | Also the default id, and the key looked up in the error bag. |
label | null | Rendered as a real label. |
value | old input, or the slot | Falls back to old() on a failed submit, so a redisplay does not lose what was typed. |
hint | null | Above the control, wired with aria-describedby. |
rows | null | Falls back to the stylesheet's own min-block-size. |
error | the error bag | Pass a string to override. |
required | false | Adds the required attribute and the "(required)" text. |
id | a slug of name | Set it to change the id without changing name. |
bag | 'default' | Named error bag. |
Vue
| Prop | Type and default | What it does |
|---|---|---|
modelValue | String, '' | v-model. |
label / hint / error | String, '' | error sets aria-invalid and renders the message. |
placeholder | String, '' | Shown in the field. Not a substitute for label. |
rows | Number, 4 | Rows shown before the field scrolls. |
required / disabled | Boolean, false | Reflected on the control. |
In the markup contract
contract/markup-contract.json is extracted from the two static templates and every package tests against it. This is what it records for .wsu-field.
| Template | Elements | Accessibility attributes | Uses |
|---|---|---|---|
| internal apps/examples/static-html/index.html | divfieldset | tabindex | 5 |
| public apps/examples/static-html/public-app.html | div | none recorded | 2 |