The page navigation is complete. You may now navigate the page content as you wish.
Skip to main content

Form Layout

Form layout components are used to create consistent structure and spacing for form elements.

Form layout components are structural components that provide the scaffolding for building standardized forms across products. These components establish spacing and layout standards to give users a consistent experience.

Usage

When to use

  • To structure and organize form elements and inputs into a consistent layout
  • When creating a form of any size in our products, including forms nested within Flyout or Modal components

When not to use

  • To organize non-form content

The anatomy of the Form layout, indicating the header, sections, separator, and actions

Form

The Form component serves as the container for all Form layout components and form content. It establishes the spacing between the Form Header, Sections, Separators, and Form Footer.

Form Header

The Form component has an optional header with styled text for a Title and Description. It also includes a space for custom content to use as needed.

Form Header anatomy showing a Title, a Description, and a placeholder for additional content

Form-level error alert

Conventionally, input level validation is sufficient for error messaging within a form but overarching form-level messaging may be needed if validations happen on submission. Form-level error alerts should be applied in the case of form-level submission failures, timeouts during data submission, or when errors apply to multiple sections within the larger form.

The form-level error message will appear below the Header as its own Section and uses the Alert component.

An Error Alert component used within a Form appearing above all the fields serving as a form-level error message

Form Section

Inputs within a form are organized in Sections. Multiple Sections may be used to group content for a more scannable and organized experience. Inputs that belong under the same heading or are related to the same topic should be within a Section.

Section Header

Similar to the Form Header, the Section Header contains styled and spaced text elements, and a space for custom content to be used as needed.

Section Header anatomy showing a Title, a Description, and a placeholder for additional content

Section Multi Field Group

The Section Multi Field Group organizes closely related form inputs in a single row, setting 16px between them, as opposed to 24px when stacked. This reinforces their relationship.

Section Multi Field Group example depicting spacing between grouped fields

When the viewport width falls below the “md” breakpoint, inputs contained in a Section Multi Field Group automatically stack.

Do

Organize inputs together when they are closely related and beneficial to read together. No more than three inputs should be put within a single row.

Three credit card information fields arranged with one full width field followed by a row of two side-by-side equal width fields

Don’t

Place inputs in rows to save space or create columns within the form.

Three equal width fields for credit card information arranged in a single row

Form Separator

A Separator can be placed between Sections when a clearer visual distinction between Sections is needed.

Do

Use a Separator to add space between Sections or before a new Section Header.

Separator used to add extra visual differentiation between two Sections

Don’t

Don’t use a Separator between the last Section and Form Footer / Button Set.

Separator being uses after all the content in a form

The Form Footer typically houses Buttons to allow users to submit form information. When there's more than one Button or action, we recommend using the Button Set component for consistent spacing and layout.

Width and resizing behavior

Max-width

Sections have a default max-width of 672px, establishing a consistent line length throughout applications. It's possible to customize this max-width value if needed.

Full-width

Individual Sections can be set to full-width, if needed. Any component requiring increased width for usability, e.g., a Radio Card Group, should be moved into a separate Section to allow for full width layout, even if they would otherwise be grouped with other associated fields.

Full width Form content in comparison to constricted width content

How to use this component

The form layout is a grid, and a grid is CSS. There is no <i80-*> tag on this page: every part of it is a plain HTML element with a class from components.css, so a server-rendered template writes the structure directly.

| | | | - | - | | i80-form | the form itself: stacks its children and sets the max-width they share | | i80-form__header | the title and description at the top | | i80-form__section | a group of related fields, spaced and width-limited | | i80-form__section-header | a title and description for one section | | i80-form__section-multi-field-group | fields on one line, stacking on a narrow screen | | i80-form__section-multi-field-group-item | one item in such a row, with a width of its own | | i80-form__separator | a line between sections | | i80-form__footer | the form's actions | | i80-form-content--is-full-width | on a header, section, separator or footer: drop the max-width |

Load components.css and the classes work; i80.js is only needed for the fields inside the form. See Web Components for both.

The form

class="i80-form" on the <form> element establishes the spacing between everything in it and the max-width its sections share.

Add form content here.
<form class="i80-form" action="/plans" method="post">
  Add form content here.
</form>

When the markup is not really a form — a read-only summary laid out the same way — put the class on a <div> instead.

This “form” is actually just a div.
<div class="i80-form">
  This “form” is actually just a div.
</div>

The header, its title and description

The examples use <h2> and <h3> for the titles, but the right level depends on the page. Pick the one that reflects where the form sits in the page's structure, to meet WCAG Success Criterion 1.3.1 Info and Relationships: what a screen reader hears should match what a reader sees.

A <div class="i80-form__header"> holds a title and a description for the whole form. Both are ordinary elements carrying the typography classes plus the contextual class:

My form title

A brief description of my form content.

<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__header">
    <h2 class="i80-text i80-typography-display-400 i80-font-weight-semibold i80-foreground-strong i80-form__header-title">My form title</h2>
    <p class="i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary i80-form__header-description">A brief description of my form content.</p>
  </div>
</form>

The heading level and size of the title

The tag is the heading level; the i80-typography-display-* class is the size. They are independent: a title can be an <h3> at display-300 if that is what the page needs. The default the design uses for a form title is display-400 semibold in the strong foreground colour.

My form title

A brief description of my form content.

<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__header">
    <h3 class="i80-text i80-typography-display-300 i80-font-weight-semibold i80-foreground-strong i80-form__header-title">My form title</h3>
    <p class="i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary i80-form__header-description">A brief description of my form content.</p>
  </div>
</form>

Sections

Put form fields in at least one section, otherwise they get neither the spacing nor the max-width.

A <div class="i80-form__section"> groups related fields, spacing them and limiting their width. A section can hold other content too — an Alert, for instance — and gives it the same max-width.

Correct the formatting of the following field: Expiration date
<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__section">
    <i80-alert type="inline" color="critical" heading="Form submission error">
      Correct the formatting of the following field:
      <i80-link-inline href="#" color="secondary">Expiration date</i80-link-inline>
    </i80-alert>
  </div>

  <div class="i80-form__section">
    <i80-text-input label="Field 1" name="field-1-name"></i80-text-input>

    <i80-radio-group legend="Field 2" name="field-2-name" layout="horizontal">
      <i80-radio label="Option 1" value="1"></i80-radio>
      <i80-radio label="Option 2" value="2"></i80-radio>
      <i80-radio label="Option 3" value="3"></i80-radio>
    </i80-radio-group>

    <i80-select label="Field 3" name="field-3-name">
      <option value="Kubernetes">Kubernetes</option>
      <option value="Other" selected>Selected</option>
    </i80-select>

    <i80-textarea label="Field 4" name="field-4-name"></i80-textarea>
  </div>
</form>

Add i80-form-content--is-full-width to let one section fill the width it is given:

<form class="i80-form" action="/connections" method="post">
  <div class="i80-form__section i80-form-content--is-full-width">
    <i80-radio-card-group legend="Create connection" name="radio-card-basic-example" alignment="center">
      <i80-radio-card label="Quick peering with Quick Links" icon="aws-color" badge="2-5 min" value="quick" checked
        description="Quick peering with quick links will provide the fastest way to connect to your providers’ network."></i80-radio-card>
      <i80-radio-card label="Manual peering using AWS CLI" icon="aws-color" badge="5-10 min" value="cli"
        description="Provide you AWS CLI template to apply connection settings."></i80-radio-card>
      <i80-radio-card label="Manual peering using HCP and AWS web console" icon="cloud" badge="30-60 min" value="console"
        description="Manually follow UI instructions to complete configuring a connection at provider side."></i80-radio-card>
    </i80-radio-card-group>
  </div>
</form>

A section's own header, title and description

As with the form title, choose the heading level that reflects the page's structure. If the form title is an <h2>, a section title is usually an <h3>.

Each section can carry its own <div class="i80-form__section-header"> with a title and a description, built the same way as the form's:

Section header title

Section header description

<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__section">
    <div class="i80-form__section-header">
      <h3 class="i80-text i80-typography-display-300 i80-font-weight-semibold i80-foreground-strong i80-form__header-title">Section header title</h3>
      <p class="i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary i80-form__header-description">Section header description</p>
    </div>
    <i80-text-input label="Field 1" name="field-1-name"></i80-text-input>
  </div>
</form>

The heading level and size of a section title

A section title is one step smaller than a form title: display-300 semibold. Change the tag for the heading level and the typography class for the size:

Section header title

Section header description

<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__section">
    <div class="i80-form__section-header">
      <h4 class="i80-text i80-typography-display-200 i80-font-weight-semibold i80-foreground-strong i80-form__header-title">Section header title</h4>
      <p class="i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary i80-form__header-description">Section header description</p>
    </div>
    <i80-text-input label="Field 1" name="field-1-name"></i80-text-input>
  </div>
</form>

Fields on one line

Wrap fields in a <div class="i80-form__section-multi-field-group"> to lay them out in a row:

<form class="i80-form" action="/people" method="post">
  <div class="i80-form__section">
    <div class="i80-form__section-multi-field-group">
      <i80-text-input label="First name" name="first-name"></i80-text-input>
      <i80-text-input label="Last name" name="last-name"></i80-text-input>
    </div>
  </div>
</form>

To give one of them a width of its own, wrap it in a <div class="i80-form__section-multi-field-group-item"> and set --i80-form-section-multi-field-group-item-width. Fields that are not wrapped share the width that is left.

<form class="i80-form" action="/addresses" method="post">
  <div class="i80-form__section">
    <div class="i80-form__section-multi-field-group">
      <i80-text-input label="City" name="city"></i80-text-input>

      <div class="i80-form__section-multi-field-group-item" style="--i80-form-section-multi-field-group-item-width: auto">
        <i80-select label="State" name="state">
          <option value="state-1">Ohio</option>
          <option value="state-2">Massachusetts</option>
          <option value="state-3">Washington</option>
          <option value="state-4">Florida</option>
          <option value="state-5">North Carolina</option>
        </i80-select>
      </div>

      <div class="i80-form__section-multi-field-group-item" style="--i80-form-section-multi-field-group-item-width: 8em">
        <i80-text-input label="Zip" name="zip"></i80-text-input>
      </div>
    </div>
  </div>
</form>

On a narrow screen

Below 768px (the “md” breakpoint) a row of fields stacks by itself; nothing needs to be written for it.

<form class="i80-form doc-form-layout-mobile-view" action="/addresses" method="post">
  <div class="i80-form__section">
    <div class="i80-form__section-multi-field-group">
      <i80-text-input label="City" name="city"></i80-text-input>

      <div class="i80-form__section-multi-field-group-item" style="--i80-form-section-multi-field-group-item-width: auto">
        <i80-select label="State" name="state">
          <option value="state-1">Ohio</option>
          <option value="state-2">Massachusetts</option>
          <option value="state-3">Washington</option>
        </i80-select>
      </div>

      <div class="i80-form__section-multi-field-group-item" style="--i80-form-section-multi-field-group-item-width: 6em">
        <i80-text-input label="Zip" name="zip"></i80-text-input>
      </div>
    </div>
  </div>
</form>

A line between sections

For more separation between sections, put an <hr> between them with the separator's classes and the form's contextual class:

First section

Second section
<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__section">
    <span class="i80-text i80-typography-body-200 i80-font-weight-regular">First section</span>
  </div>

  <hr class="i80-separator i80-separator--spacing-0 i80-form__separator">

  <div class="i80-form__section">
    <span class="i80-text i80-typography-body-200 i80-font-weight-regular">Second section</span>
  </div>
</form>

A <div class="i80-form__footer"> at the end holds the form's actions. Put the buttons in a <div class="i80-button-set"> to space them.

<form class="i80-form" action="/plans" method="post">
  <div class="i80-form__footer">
    <div class="i80-button-set">
      <i80-button type="submit">Submit</i80-button>
      <i80-button color="secondary">Cancel</i80-button>
    </div>
  </div>
</form>

Putting it all together

A form with sections at the default max-width and one full-width section:

Add policy

Please specify which policy you would like to assign to your cluster. Read more about policies in our documentation.


Policy OPA

Policy OPA is a governance rule that enforces specific access controls and compliance requirements within the organization‘s infrastructure.

<form class="i80-form" action="/policies" method="post">
  <div class="i80-form__header">
    <h2 class="i80-text i80-typography-display-400 i80-font-weight-semibold i80-foreground-strong i80-form__header-title">Add policy</h2>
    <p class="i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary i80-form__header-description">Please specify which policy you would like to assign to your cluster. Read more about
      policies in our <i80-link-inline href="#">documentation</i80-link-inline>.</p>
  </div>

  <div class="i80-form__section">
    <i80-text-input label="Secret ID" name="demo-secret-id"
      helper-text="Create a token within your self-managed cluster and attach only the “builtin/global-read-only” policy to it. Once complete, save the token, copy its secret ID from the token list, and insert it below."></i80-text-input>
  </div>

  <div class="i80-form__section i80-form-content--is-full-width">
    <i80-radio-card-group legend="Policy framework" name="demo-radio-card-policies-example" alignment="center">
      <i80-radio-card label="Terraform policy" icon="layers" badge="New" value="terraform" checked
        description="Write policies in HCL that directly reference Terraform resources to enforce your compliance and security requirements."></i80-radio-card>
      <i80-radio-card label="Sentinel" icon="hexagon" value="sentinel"
        description="Write policies imperatively for attribute-based access control to enforce compliance and security requirements."></i80-radio-card>
      <i80-radio-card label="Open Policy Agent (OPA)" icon="wrench" value="opa"
        description="Open-source, general-purpose policy engine that allows you to enforce fine-grained access control and decision-making across cloud-native environments."></i80-radio-card>
    </i80-radio-card-group>
  </div>

  <div class="i80-form__section">
    <i80-text-input label="Policy set name" name="policy-set-name"
      helper-text="Valid characters include ASCII letters, numbers, as well as spaces, periods (.), dashes (-), and underscores (_)."></i80-text-input>
  </div>

  <hr class="i80-separator i80-separator--spacing-0 i80-form__separator">

  <div class="i80-form__section">
    <div class="i80-form__section-header">
      <h3 class="i80-text i80-typography-display-300 i80-font-weight-semibold i80-foreground-strong i80-form__header-title">Policy OPA</h3>
      <p class="i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary i80-form__header-description">Policy OPA is a governance rule that enforces specific access controls and compliance
        requirements within the organization‘s infrastructure.</p>
    </div>

    <i80-text-input label="Query" name="query" placeholder="e.g data.policy.deny"
      helper-text="The rule expression that the policy will evaluate."></i80-text-input>
  </div>

  <div class="i80-form__footer">
    <div class="i80-button-set">
      <i80-button type="submit">Submit</i80-button>
      <i80-button color="secondary">Cancel</i80-button>
    </div>
  </div>
</form>

Setting the max-width yourself

--i80-form-section-max-width on the form sets the max-width every header, section, separator and footer shares, instead of overriding it one section at a time:

<form class="i80-form" style="--i80-form-section-max-width: 100%" action="/plans" method="post">
  <!-- Sections, the header, the footer and the separators all have a 100% max-width -->
  <div class="i80-form__section">
    <i80-text-input label="Field 1" name="field-1-name"></i80-text-input>
  </div>
</form>

An individual section can still opt out of it with i80-form-content--is-full-width:

<form class="i80-form" style="--i80-form-section-max-width: 36rem" action="/plans" method="post">
  <!-- Sections, the header, the footer and the separators all have a 36rem max-width -->
  <div class="i80-form__section">
    <i80-text-input label="Field 1" name="field-1-name"></i80-text-input>
  </div>

  <!-- Except this section, which fills the width it is given -->
  <div class="i80-form__section i80-form-content--is-full-width">
    <i80-text-input label="Field 2" name="field-2-name"></i80-text-input>
  </div>
</form>

Classes

The form layout has no element of its own: it is a set of classes in components.css that you put on ordinary HTML. Load components.css and they work — see Web Components. The fields inside a form are the <i80-*> elements documented on their own pages, or a field you assemble with the Form Primitives.

Form

Class On What it does
i80-form &lt;form&gt;, or &lt;div&gt; when the markup is not really a form Stacks its children 32px apart and sets the max-width they share.
Custom property Default What it does
--i80-form-section-max-width 672px The max-width of every header, section, separator and footer in the form.

Form header

Class On What it does
i80-form__header &lt;div&gt; The form's title and description, 8px apart, at the shared max-width.
i80-form__header-title a heading, or &lt;div&gt; The title. Add the typography classes with it: i80-text i80-typography-display-400 i80-font-weight-semibold i80-foreground-strong is what the design uses.
i80-form__header-description &lt;p&gt; The description, with i80-text i80-typography-body-300 i80-font-weight-regular i80-foreground-primary.

The same two title and description classes are used inside a section header, one size smaller.

Form section

Class On What it does
i80-form__section &lt;div&gt; A group of related fields, 24px apart, at the shared max-width. Hidden while it is empty.
i80-form__section-header &lt;div&gt; A section's own title and description; the title is one step smaller, i80-typography-display-300.
i80-form__section-multi-field-group &lt;div&gt; Fields on one line, 16px apart, aligned along their bottom edge; stacks below 768px.
i80-form__section-multi-field-group-item &lt;div&gt; One item in that line, with a width of its own.
Custom property Default What it does
--i80-form-section-multi-field-group-item-width auto The width of one item in a line of fields.

Form separator

Class On What it does
i80-form__separator &lt;hr&gt; A line between sections. Write it with the separator's own classes: i80-separator i80-separator--spacing-0.
Class On What it does
i80-form__footer &lt;div&gt; The form's actions, at the shared max-width. Put the buttons in a &lt;div class="i80-button-set"&gt; to space them.

Filling the width

Class On What it does
i80-form-content--is-full-width a header, a section, a separator or a footer Drops the max-width, so that one part fills the space it is given.

Anatomy

Anatomy of formLayout

Element Usage
Title/Section title Optional
Description/Section description Optional
Section May consist of multiple content types, including text, fields, and multi field groups.
Form error Optional, uses the critical inline Alert
Field Required, supports any Form component
Multi Field Group Optional, supports any Form component
Separator Optional
Footer Most often consists of ButtonSet and actions

Spacing

Spacing in forms is based on a decreasing scale on an 8px grid. From largest to smallest, the spacing structure is:

  • The Form Header, Sections, Separators, and Footer are 32px apart.
  • Stacked inputs are 24px apart.
  • Side by side inputs within a Section Multi Field Group are 16px apart.
  • Text elements within the Form Header and Form Section Headers are 8px apart.

Representation of the various spacing sizes between Form layout components

Visit the Form Patterns guidelines for additional details on spacing in forms.

Conformance rating

Conformant

When used as recommended, there should not be any WCAG conformance issues with this component.

Applicable WCAG Success Criteria

This section is for reference only. This component intends to conform to the following WCAG Success Criteria:

  • 1.3.2 Meaningful Sequence (Level A):
    When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.
  • 1.4.10 Reflow (Level AA):
    Content can be presented without loss of information or functionality, and without requiring scrolling in two dimensions.
  • 1.4.12 Text Spacing (Level AA):
    No loss of content or functionality occurs by setting all of the following and by changing no other style property: line height set to 1.5; spacing following paragraphs set to at least 2x the font size; letter-spacing set at least 0.12x of the font size, word spacing set to at least 0.16 times the font size.
  • 1.4.4 Resize Text (Level AA):
    Except for captions and images of text, text can be resized without assistive technology up to 200 percent without loss of content or functionality.
  • 2.4.3 Focus Order (Level A):
    If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.
  • 2.4.7 Focus Visible (Level AA):
    Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.
  • 2.4.11 Focus Not Obscured (Minimum) (Level AA):
    When a user interface component receives keyboard focus, the component is not entirely hidden due to author-created content.

Support

If any accessibility issues have been found within this component, let us know by submitting an issue.

0.1.0

Første i80-version (fork af upstream, se core/UPSTREAM.md).


Related