Skip to content

Fieldset

Groups related form inputs under a common label. Pure CSS — no JavaScript or Vue component needed.


When to use

Use fieldset to show a relationship between multiple form inputs. For example:

  • An address group (name, street, postal code, city)
  • Personal details (first name, last name, email, phone)
  • Payment information (card number, expiry, CVV)
  • Any question that requires multiple inputs to answer

You do NOT need fieldset for: RadioGroup, CheckboxGroup, or DateInput — these components already include <fieldset> and <legend> internally.


Preview

Leveringsadresse

Udfyld din adresse for levering.

Bordered variant

Betalingsoplysninger

How it works

The first element inside a <fieldset> must be a <legend> that describes the group. This can be a question ("What is your current address?") or a statement ("Personal details").

If the fieldset is the only question on the page, the legend can serve as the page heading. This is good practice for screen readers — users hear the context only once.


Usage

Basic

html
<fieldset class="fieldset">
  <legend class="fieldset__legend">Adresse</legend>
  <div class="fieldset__fields">
    <div class="input-field">
      <label class="input-label" for="street">Gade</label>
      <input id="street" class="input input--md" type="text" />
    </div>
    <div class="fieldset__fields--row">
      <div class="input-field">
        <label class="input-label" for="postal">Postnummer</label>
        <input id="postal" class="input input--md" type="text" />
      </div>
      <div class="input-field">
        <label class="input-label" for="city">By</label>
        <input id="city" class="input input--md" type="text" />
      </div>
    </div>
  </div>
</fieldset>

With help text

html
<fieldset class="fieldset">
  <legend class="fieldset__legend">Kontaktoplysninger</legend>
  <p class="fieldset__hint">Vi bruger dine oplysninger til at sende kvittering.</p>
  <div class="fieldset__fields">
    ...
  </div>
</fieldset>

With error

html
<fieldset class="fieldset">
  <legend class="fieldset__legend">Betalingsoplysninger</legend>
  <p class="fieldset__error" role="alert">Udfyld venligst alle betalingsfelter.</p>
  <div class="fieldset__fields">
    ...
  </div>
</fieldset>

Bordered

html
<fieldset class="fieldset fieldset--bordered">
  <legend class="fieldset__legend">Fakturaadresse</legend>
  <div class="fieldset__fields">
    ...
  </div>
</fieldset>

Horizontal fields (short inputs side by side)

html
<div class="fieldset__fields--row">
  <div class="input-field">
    <label class="input-label" for="postal">Postnummer</label>
    <input id="postal" class="input input--md" type="text" />
  </div>
  <div class="input-field">
    <label class="input-label" for="city">By</label>
    <input id="city" class="input input--md" type="text" />
  </div>
</div>

When fieldset is already included

These components handle fieldset internally — do not wrap them in an extra fieldset:

ComponentInternal fieldsetLegend source
GTRadioGroupYeslabel prop becomes <legend>
GTCheckboxGroupYeslabel prop becomes <legend>
GTDateInputNo (uses role="group")label prop becomes group label

Do this

html
<!-- RadioGroup already has fieldset — just use the component -->
<GTRadioGroup label="Betalingsmetode" :options="[...]" />

Do NOT do this

html
<!-- ❌ Double fieldset — screen readers hear the legend twice -->
<fieldset class="fieldset">
  <legend class="fieldset__legend">Betalingsmetode</legend>
  <GTRadioGroup label="Betalingsmetode" :options="[...]" />
</fieldset>

Nesting fieldsets

Avoid nesting fieldsets. If you need sub-groups, use headings (<h3>) inside the fieldset instead:

html
<fieldset class="fieldset">
  <legend class="fieldset__legend">Leveringsoplysninger</legend>
  <div class="fieldset__fields">
    <h3 class="text-base font-medium mb-2">Modtager</h3>
    <!-- name, email fields -->

    <h3 class="text-base font-medium mb-2 mt-4">Adresse</h3>
    <!-- address fields -->
  </div>
</fieldset>

Vue usage

Fieldset is intentionally not a Vue component — it's just HTML + CSS. Use it directly in your templates:

vue
<template>
  <fieldset class="fieldset">
    <legend class="fieldset__legend">Adresse</legend>
    <div class="fieldset__fields">
      <GTAddressInput v-model="address" label="Adresse" required />
      <GTSelect label="Land" :options="countries" />
    </div>
  </fieldset>
</template>

For form validation, apply useField to individual inputs inside the fieldset — not to the fieldset itself.


Do's and don'ts

Do
Use legend as the first child — it describes the group
Do
Use for grouping related inputs (address, payment, personal details)
Do
Use fieldset__fields--row for short fields side by side
Do
Include help text in the fieldset if it applies to the whole group
Don't
Wrap RadioGroup or CheckboxGroup in extra fieldset (they have their own)
Don't
Nest fieldsets inside fieldsets
Don't
Use fieldset for a single input — use input-field instead
Don't
Use fieldset as a visual card — use GTCard for that

Classes

ClassDescription
.fieldsetBase reset (no border, no padding)
.fieldset--borderedVisual border + padding
.fieldset__legendGroup label (semibold, lg font)
.fieldset__hintHelp text for the group
.fieldset__errorError text for the group
.fieldset__fieldsVertical flex container with gap
.fieldset__fields--rowHorizontal layout for short fields

Accessibility

  • <fieldset> + <legend> provides native group labeling for screen readers
  • Screen readers announce the legend before each input in the group
  • Error text uses role="alert" for immediate announcement
  • Do not duplicate legends — RadioGroup/CheckboxGroup/DateInput already handle this

References