Playground
A text field with a label, help text, native constraint validation and an optional submit arrow. It exposes a live value property, so data-bind works as it does on a native <input>.
Examples
Label and hint
<sb-input label="Call sign" placeholder="e.g. Maverick" hint="Shown to the rest of the crew."></sb-input>
Validation
Validation runs after the first blur and then on every keystroke. required, minlength, pattern and type use the browser's own rules. Set error to replace the default message.
<sb-input label="Email" type="email" required placeholder="you@starbase.dev"></sb-input>
<sb-input label="Launch code" pattern="[0-9]{4}" error="Four digits, commander." placeholder="0000"></sb-input>
Two-way binding
Declare the signal first, because data-bind only writes to a signal that already has a value.
<div data-signals:_dest="''">
<sb-input label="Destination" data-bind:_dest__prop.value__event.input placeholder="Type a planet"></sb-input>
<p data-text="$_dest ? 'Plotting course to ' + $_dest + '…' : 'Awaiting destination'"></p>
</div>
Submit with the arrow
sb-submit fires on Enter or on the arrow button, but only when the value is valid. Send it to your backend with @post.
<div data-signals:_log="''">
<sb-input action required placeholder="Transmit a message..." data-on:sb-submit="$_log = 'Sent: ' + evt.detail.value"></sb-input>
<p data-text="$_log"></p>
</div>
With commands
Give it a name, and it emits sb-change with { name, value } when a value is committed: ready to post as a command. With confirm, it sets :state(pending) until the server's re-rendered attribute matches, and revert() goes back to the server's value when a command is rejected. See Commands and components and the Showcase.
Accessibility
The native <input> sits inside a <label>. Without a label, the placeholder becomes the accessible name. Errors are announced with role="alert", and aria-invalid follows the validation state.
Installation
Add Datastar with Rocket and the Starbase autoloader once per page, then use the tag. The autoloader imports each component the first time its tag appears, including tags added later by a Datastar morph.
<!-- Once per page: Datastar with Rocket, and the Starbase autoloader.
It loads every <sb-…> component the first time its tag appears. -->
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/autoloader.js"></script>
<!-- Optional, no flash of undefined elements: class="sb-cloak" on <html>, and -->
<style>.sb-cloak :not(:defined) { visibility: hidden }</style>
<sb-input placeholder="Your message..." action></sb-input>
<!-- In production, pin today's catalog instead of the latest: the browser then
refuses any file that changed. Add "integrity" to the import map above: the
hashes from https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/importmap.json and Datastar's, below. -->
<!--
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" },
"integrity": { "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js": "sha384-vUxZojLrF1Ar3de5h7VINqhJXBgjyZS4U49pHvGa87kum6j5Xn6JRziuARNw5ELG", "…": "…from importmap.json" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/autoloader.js" integrity="sha384-IvSCUjEWPS+wwXunU+48x96KmyWDx/Y4LeCwCL8cWFqJ/zHmTsTOsSMrzTOcpW3D"></script>
-->
<!-- Or load just this component, pinned to this version: -->
<!-- <script type="module" src="https://starbase.zweiundeins.gmbh/c/input@a54b301e44d1/input.js" integrity="sha384-OP1zdVmhpxyq4hP0KZXRF27Wg2Lpz3CBzPvJugZyRYikUhSKBLsSr5oQOqbkkqRI"></script> -->Size
Each file compressed on its own, the way it is served (gzip -9, brotli -11). Datastar and Rocket are shared by every component and not counted.
| File | Original | gzip | brotli |
|---|---|---|---|
input.js | 7.9 kB | 3.0 kB | 2.6 kB |
API reference
Props
| Attribute | Type | Default | Description |
|---|---|---|---|
value | string | "" | The value. A new value from the server replaces it; the live value is the value property. |
label | string | "" | Visible label. |
placeholder | string | "" | Placeholder text. |
type | "text" | "email" | "search" | "url" | "tel" | "password" | "text" | Input type. |
name | string | "" | Name reported in sb-change and sb-submit (e.g. the field of a command). |
required | boolean | false | Value must not be empty. |
minlength | number | 0 | Minimum length. |
pattern | string | "" | Regular expression the value must match. |
hint | string | "" | Help text below the field. |
error | string | "" | Message shown when invalid (defaults to the browser message). |
action | boolean | false | Show a submit arrow button. |
confirm | boolean | false | Server-confirmed value: :state(pending) while the local value differs from the server's value attribute (see revert()). |
Events
| Name | Description |
|---|---|
input | On every keystroke (native, re-targeted to the host). |
change | When the value is committed. |
sb-change | When the value is committed (blur or Enter). detail: { name, value }: ready for a command. |
sb-submit | Enter or arrow button with a valid value. detail: { name, value }. |