Hoppa till innehållet

Formulär

Kontrollerna är stilade som element. Klasserna är för undantagen.

Kontroller

<input type="text" />
<select>…</select>
<textarea></textarea>

Chevronen är en token

En bakgrundsbild kan inte läsa currentColor, så chevronens URL är en token per tema.

Fält

Etikett, kontroll, sedan hjälptext eller fel.

Börja skriva för att söka bland 87 kunder.

<Field id="customer" label="Kund" hint="Börja skriv för att söka.">
  <input type="text" id="customer" />
</Field>

placeholder är inte en etikett

Den försvinner när man börjar skriva — precis när man behöver den.

Sammansatta

Ett affix svetsat till kontrollen. En ram runt paret, inte två som nuddar.

kr
https://
<div class="ws-input-group">
  <span class="ws-input-group__affix"><Icon name="Search" /></span>
  <input type="search" />
</div>

Val

<label class="ws-check"><input type="checkbox" /> Etikett</label>
<label class="ws-switch"><input type="checkbox" /> Etikett</label>

Switch eller kryssruta

Switch för något som slår igenom direkt. Behöver det ett Spara är det en kryssruta.

Fel

aria-invalid på kontrollen, inte en klass — CSS:en läser det skärmläsaren läser.

Fel: Måste vara mellan 0 och 24.

Fel: Adressen saknar en domän.

<Field id="hours" label="Timmar" error="Måste vara mellan 0 och 24.">
  <input type="number" id="hours" value="31" />
</Field>

Felsammanfattning

Som GOV.UK: överst i ett formulär som inte gick igenom, varje fel en länk till sitt fält. Den tar fokus när den visas, och länken flyttar fokus till fältet med etiketten synlig.

<ErrorSummary errors={[
  { href: "#hours", text: "Timmar måste vara mellan 0 och 24" },
  { href: "#email", text: "E-postadressen saknar en domän" },
]} />

<!-- utan komponenten -->
<div class="ws-error-summary" data-malm-error-summary tabindex="-1">
  <div role="alert">
    <h2 class="ws-error-summary__title">Det finns ett problem</h2>
    <ul class="ws-error-summary__list">
      <li><a href="#hours">Timmar måste vara mellan 0 och 24</a></li>
    </ul>
  </div>
</div>

Felmeddelandet i sammanfattningen och vid fältet är samma mening. Ge sidans titel Fel: först, så att det hörs innan något annat.

Teckenräknare

Som GOV.UK: hur många tecken som är kvar, under fältet. Över gränsen blir räknaren ett fel, men texten går att skriva klart och korta sedan.

Vad gjordes, och för vem?

Du kan skriva upp till 200 tecken

<Field id="summary" label="Sammanfattning" hint="Vad gjordes, och för vem?" maxlength={200}>
  <textarea id="summary" rows="4"></textarea>
</Field>

Layout

Fältrader kollapsar på en container query, inte en viewport-query.

<div class="ws-field-row">…</div>
Hur mycket av raden

ws-field--wide tar hela fältraden, för en textruta eller den enda inställning som behöver platsen. ws-field--narrow är för ett kort värde — ett antal dagar, en kod — som i full bredd läses som en mening.

<div class="ws-field-row">
  <label class="ws-field ws-field--narrow">…</label>
  <label class="ws-field ws-field--wide">…<textarea></textarea></label>
</div>

Tidfält

Dagens ena kontroll: stor, högerställd mot sin enhet, och tabulär så att en kolumn av dem linjerar.

Ett dygn är 24 timmar.

h
<Field id="hours" label="Arbetad tid" hint="Ett dygn är 24 timmar.">
  <span class="ws-input-group ws-input-group--hours">
    <input type="number" id="hours" step="0.5" min="0" max="24" value="7.5" />
    <span class="ws-input-group__affix">h</span>
  </span>
</Field>

Grupp av val

Flera kontroller under en fråga, som GOV.UK: legend är frågan, och ledtråden och felet beskriver hela gruppen. ws-field__value är ett värde där ett fält annars skulle stå.

Behörigheter

Välj minst en.

Kategori

Fel: Välj en kategori.

Kund-ID

K-1042

<Fieldset id="scopes" legend="Behörigheter" hint="Välj minst en.">
  <label class="ws-check"><input type="checkbox" name="scope" value="read" /> Läsa</label>
  …
</Fieldset>

<Fieldset legend="Länkar" gap={4}>…fält…</Fieldset>

<div class="ws-field">
  <span class="ws-field__label">Kund-ID</span>
  <p class="ws-field__value">K-1042</p>
</div>

Filter

Ett filter är en del av adressen. Ett fält med form är ett eget GET-formulär som bär sidans övriga parametrar, och en lista med autosubmit skickar det när den ändras; utan skript finns knappen Visa. Flera värden i ett filter är MultiSelect med picker: knappen säger det enda valet, eller filtret och hur många.

<Field id="f-kund" label="Kund" class="ws-field--auto" form={{ hidden: [["period", "2026-10-01"]] }}>
  <select id="f-kund" name="kund" data-malm-autosubmit data-malm-drop-empty>
    <option value="">Alla</option>…
  </select>
</Field>
field({ …, form: { hidden }, controlHtml: select({ autosubmit: true, options, attrs: { id, name } }) })

<form method="get" class="ws-cluster">
  <MultiSelect picker id="f-ansvarig" label="Ansvarig" name="ansvarig"
               options={personer} selected={["anna", "bo"]} />
</form>

Fil

Plattformens filfält, med knappen ritad som en sekundär knapp. Filnamnet och dialogen är webbläsarens.

<Field id="logo" label="Logotyp"><input type="file" id="logo" name="logo" accept="image/*" /></Field>

Sökfält med förslag

Skriv, se vad som matchar, välj ett. En riktig combobox: piltangenter, Enter, Esc — och hörbar.

<div class="ws-combobox" id="ticket" data-malm-combobox>
  <label class="ws-sr-only" for="ticket-input">Sök ärende</label>
  <input type="text" id="ticket-input" placeholder="Sök ärende" data-combobox-input />
  <div class="ws-popover-panel ws-combobox__panel" data-combobox-panel hidden>
    <ul class="ws-combobox__list" data-combobox-list>
      <li role="none"><ul class="ws-combobox__section" role="group" aria-labelledby="g1">
        <li class="ws-combobox__group" role="presentation" id="g1" data-combobox-group>Ärenden</li>
        <li class="ws-combobox__option" role="option" data-value="ticket:2853"
            data-search="2853 Whitespace Ny webbplats">…</li>
      </ul></li>
    </ul>
    <p class="ws-combobox__empty" data-combobox-empty hidden>Inget hittat</p>
  </div>
</div>

<!-- valet är produktens: -->
combobox.addEventListener("malm:select", (event) => event.detail.value);

Flera värden

Valen som taggar och ett fält att söka bland resten. Under ligger en riktig <select multiple>: det är den formuläret skickar, och den som syns utan skript. Backsteg i ett tomt fält tar bort den sista. Samma MultiSelect som filtrets, utan picker.

Sök och välj flera.

<Field id="repos" label="Ytterligare GitHub-repositories" hint="Sök och välj flera." optional>
  <MultiSelect id="repos" name="github_repo_ids" placeholder="Sök repository"
                 options={repos} selected={valda} />
</Field>

<!-- produkten styr valen genom selecten -->
option.toggleAttribute("data-combobox-excluded", true); option.selected = false;
refreshCombobox(combobox);