Text area
Longer text in a deeper recess: notes, messages, descriptions. It grows by its corner handle.
Kit part · Forms · Inputs · Text areas
A writing pad that is still a real textarea. A sheet of ruled paper is set into a machined frame, and the rules move with the words. A small LCD in the foot counts characters or words: it lights amber near the limit and red over it, and the limit is said once, in words, when it is crossed. The pad grows with the text up to a height you choose.
Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.
Aluminium
| Part | Empty | Near the limit | Over the limit | Focus |
|---|---|---|---|---|
| Pad | 120 characters at most | 120 characters at most | 120 characters at most | 120 characters at most |
Graphite
| Part | Empty | Near the limit | Over the limit | Focus |
|---|---|---|---|---|
| Pad | 120 characters at most | 120 characters at most | 120 characters at most | 120 characters at most |
| Name | Kind | Values | What it does |
|---|---|---|---|
| <tui-log-pad> | element | max · unit · warn · rows · max-rows | The pad. max: the limit, none for no limit. unit: "characters" (default) or "words". warn: how far to the limit the meter goes amber, 0 to 1, 0.9 by default. rows and max-rows: the height it starts at and the most it grows to, in lines. |
| name · label · placeholder · value | attribute | on an empty element | An empty <tui-log-pad> builds the whole pad from these. |
| textarea.tui-log-pad__text | element | a real textarea | The sheet. Label it, name it, put it in a form. The rules and the margin are its own background. |
| --lp-lh | custom property | length | The line height, and the distance between rules: 28px. Text and rules stay locked together. |
| .tui-log-pad__foot · __limit · __meter | class | print · light | The limit in words, and the LCD that counts. The meter is hidden from assistive technology: the textarea’s description carries the limit and the live region carries the crossing. |
| [data-state] | attribute | "ok" · "warn" · "over" | On the element and on the meter, set by script. Amber glass for warn, red for over. |
| aria-invalid · setCustomValidity | state | while over | Set on the textarea while the count is past the limit, with a message a form shows. Put maxlength on the textarea if the limit should be hard. |
| count · textarea | property | number · element | What has been written, in the unit; and the textarea itself. |
| refresh() | method | none | Recount, recolour and regrow. Call it after setting textarea.value from script. |
| tui-log-pad | event | detail.count · max · unit · state | After every change. Bubbles. |
| logPadCount · logPadState · logPadMeter · logPadSays · logPadLimit · logPadHTML | function | kit.mjs | The rules, pure: what has been written, which state that is, what the LCD shows, what to say when the limit is crossed, and the markup for a page that renders one on a server. |
<tui-log-pad max="280" unit="characters" rows="4" max-rows="10">
<label class="tui-label tui-log-pad__label" for="note">Handover note</label>
<div class="tui-log-pad__pad">
<textarea class="tui-log-pad__text" id="note" name="note" rows="4"
aria-describedby="note-limit"></textarea>
<div class="tui-log-pad__foot">
<span class="tui-log-pad__limit" id="note-limit">280 characters at most</span>
<output class="tui-lcd tui-lcd--sm tui-log-pad__meter" data-state="ok"
data-ghost="888/888" aria-hidden="true">000/280</output>
</div>
</div>
<p class="tui-log-pad__say" role="status"></p>
</tui-log-pad>import { logPadHTML } from '/kit/kit.mjs'; // also defines <tui-log-pad>
// An empty <tui-log-pad name="note" label="Handover note" max="280"> builds the pad above for itself.
const pad = document.querySelector('tui-log-pad');
pad.textarea; // the real <textarea>: a form posts it under its own name
pad.count; // 112, in characters or words as the unit says
pad.refresh(); // after you set textarea.value from script
pad.addEventListener('tui-log-pad', (e) => {
e.detail.count; // 112
e.detail.state; // 'ok' | 'warn' | 'over'
});
// Over the limit the textarea is aria-invalid and its validity message is set,
// so the form will not submit until the note is back within it.
form.reportValidity();Needs kit.css and kit.mjs. How to install the kit.
<textarea>, named by its label and posted by its form, so nothing about writing, selecting, spelling or autofill is reinvented.aria-describedby, so it is read before anyone types. The LCD repeats the count for the eye and is hidden from assistive technology.aria-invalid and carries a validity message, so a form will not send it and says why.The rules are the textarea’s own background: a repeating gradient exactly one line high, with background-attachment: local, so it is attached to the scrolling text and moves with it, and a margin drawn as a second, fixed layer. The line height is a pixel value shared by the text and the rules, which is what locks the two together; the script reads it to grow the pad in whole lines, from rows up to max-rows, and it scrolls after that. The frame is a raised plate with a 6px rim round a 6px sheet, so its 12px corner is concentric. The paper is the kit’s paper tokens, the same in both finishes. The LCD is the kit’s own, backlit amber and then red by two colour mixes, and its unlit segments are always the same width as the number. The counting, the state and the sentence are pure functions, tested.
For notes with a length that matters: a handover, a commit message, a status line.
6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.
Longer text in a deeper recess: notes, messages, descriptions. It grows by its corner handle.
Text entry in a recess, the label printed above it, and a hint that says how to fix an error.
A value that changes, on grey-green liquid crystal, with a ghost of the unlit segments behind it: clocks, battery, counters.