Input · Textarea
Native text controls in shadcn geometry — input plus textarea. Focus rings follow --ring; errors via aria-invalid.
INSTALL
1pnpm dlx @sayagodev/mo add formOr import the files from the package (no copy):
1import "@sayagodev/mo/css/form.css";EXAMPLES
Input
1<input type="text" placeholder="Enter text" aria-label="Enter text" />2
With label & hint
Your API key is encrypted and stored securely.
1<div data-field>2 <label for="ex-api-key">API Key</label>3 <input id="ex-api-key" type="password" placeholder="sk-..." />4 <small data-hint>Your API key is encrypted and stored securely.</small>5</div>6
Disabled
This field is currently disabled.
1<div data-field>2 <label for="ex-email-disabled">Email</label>3 <input id="ex-email-disabled" type="email" placeholder="Email" disabled />4 <small data-hint>This field is currently disabled.</small>5</div>6
Required
This field must be filled out.
1<div data-field>2 <label for="ex-required">Required Field *</label>3 <input id="ex-required" placeholder="This field is required" required />4 <small data-hint>This field must be filled out.</small>5</div>6
Invalid state
This field contains validation errors.
1<div data-field aria-invalid="true">2 <label for="ex-invalid-input">Invalid Input</label>3 <input id="ex-invalid-input" placeholder="Error" aria-invalid="true" />4 <small class="error">This field contains validation errors.</small>5</div>6
Label with badge
1<div data-field>2 <label for="ex-webhook" class="flex items-center justify-between gap-2">3 Webhook URL4 <span class="badge" data-variant="secondary">Beta</span>5 </label>6 <input id="ex-webhook" type="url" placeholder="https://api.example.com/webhook" />7</div>8
Two-column fields
1<div class="row">2 <div class="col-6">3 <div data-field>4 <label for="ex-first-name">First Name</label>5 <input id="ex-first-name" placeholder="Jordan" />6 </div>7 </div>8 <div class="col-6">9 <div data-field>10 <label for="ex-last-name">Last Name</label>11 <input id="ex-last-name" placeholder="Lee" />12 </div>13 </div>14</div>15
Full form
1<form>2 <div data-field>3 <label for="ex-form-name">Name</label>4 <input id="ex-form-name" required placeholder="Evil Rabbit" />5 </div>6 <div data-field>7 <label for="ex-form-email">Email</label>8 <input id="ex-form-email" type="email" placeholder="[email protected]" />9 <small data-hint>We'll never share your email with anyone.</small>10 </div>11 <div class="row">12 <div class="col-6">13 <div data-field>14 <label for="ex-form-phone">Phone</label>15 <input id="ex-form-phone" type="tel" placeholder="+1 (555) 123-4567" />16 </div>17 </div>18 <div class="col-6">19 <div data-field>20 <label for="ex-form-country">Country</label>21 <mo-select data-placeholder="Select country">22 <button type="button" popovertarget="ex-form-country" data-select-trigger aria-label="Country"></button>23 <ul popover id="ex-form-country" class="popover" role="listbox">24 <li><button type="button" role="option" value="us" aria-selected="true">United States</button></li>25 <li><button type="button" role="option" value="uk">United Kingdom</button></li>26 <li><button type="button" role="option" value="ca">Canada</button></li>27 </ul>28 </mo-select>29 </div>30 </div>31 </div>32 <div data-field>33 <label for="ex-form-address">Address</label>34 <input id="ex-form-address" placeholder="123 Main St" />35 </div>36 <div class="hstack justify-end">37 <button type="button" class="outline">Cancel</button>38 <button type="submit">Submit</button>39 </div>40</form>41
Search + buttons
1<div data-field>2 <label for="ex-search-btn">Search</label>3 <fieldset class="group">4 <input id="ex-search-btn" placeholder="Type to search..." />5 <button type="button" class="outline">Search</button>6 </fieldset>7</div>8
Inline search
1<div class="flex items-center gap-2">2 <input type="search" placeholder="Search..." aria-label="Search" />3 <button type="button">Search</button>4</div>5
Joined group states
This field contains validation errors.
1<div class="vstack gap-4">2 <div data-field>3 <label for="ex-ig-default">Default</label>4 <input id="ex-ig-default" placeholder="Placeholder" />5 </div>6 <div data-field>7 <label for="ex-ig-disabled">Disabled</label>8 <input id="ex-ig-disabled" placeholder="This field is disabled" disabled />9 </div>10 <div data-field aria-invalid="true">11 <label for="ex-ig-invalid">Invalid</label>12 <input id="ex-ig-invalid" placeholder="This field is invalid" aria-invalid="true" />13 <small class="error">This field contains validation errors.</small>14 </div>15</div>16
Textarea
1<textarea placeholder="Type your message here." aria-label="Message"></textarea>2
Textarea + button
1<div class="vstack gap-2">2 <textarea placeholder="Type your message here." aria-label="Message"></textarea>3 <button type="button">Send message</button>4</div>5
Textarea disabled
1<div data-field>2 <label for="ex-msg-disabled">Message</label>3 <textarea id="ex-msg-disabled" placeholder="Type your message here." disabled></textarea>4</div>5
Textarea in field
Enter your message below.
1<div data-field>2 <label for="ex-msg-field">Message</label>3 <small data-hint>Enter your message below.</small>4 <textarea id="ex-msg-field" placeholder="Type your message here."></textarea>5</div>6
Textarea invalid
Please enter a valid message.
1<div data-field aria-invalid="true">2 <label for="ex-msg-invalid">Message</label>3 <textarea id="ex-msg-invalid" placeholder="Type your message here." aria-invalid="true"></textarea>4 <small class="error">Please enter a valid message.</small>5</div>6
File
Select a picture to upload.
1<div data-field>2 <label for="ex-picture">Picture</label>3 <input id="ex-picture" type="file" />4 <small data-hint>Select a picture to upload.</small>5</div>6
USAGE
1<div data-field>2 <label for="id">Label</label>3 <input id="id" />4 <small data-hint>Hint</small>5 <small class="error">Error</small> <!-- shows on aria-invalid="true" -->6</div>
API
NAMEKINDDESCRIPTION
[data-field]attributeField wrapper: column layout, gap, error wiring
aria-invalid="true"attributeReveals .error, destructive border/ring
:user-invalidattributeNative validation styling hook
fieldset.groupelementJoined controls: prefix addon + inputs/buttons
--mo-input-radiustokenShared by input/select/taginput
FILES
src/css/form.cssInstall:
mo add formTHEMING · PLAYGROUND
This is the exact component from the examples above, wearing a completely different theme. Edit the tokens and --mo-* hooks in the editor — autocomplete knows them all — and the preview obeys in real time. Your CSS always wins.
Global tokens
--background--border--destructive--foreground--input--muted--muted-foreground--primary--primary-foreground--radius--ringComponent hooks
--mo-field-gap--mo-input-padding-x--mo-input-radius--mo-slider-thumb-bg--mo-switch-thumb--mo-switch-widthPreview
Your CSS
Try it: type --mo- for the component hooks, or edit any value — the preview obeys in real time.