o-forms CircleCI

FT-branded styles for form elements.



Each form field is made up of at least 3 elements:

  • A containing element defined by .o-forms.
  • Its label, defined by .o-forms__label.
  • One or more form controls e.g. input, select, textarea elements, defined by .o-forms__text, .o-forms__select, .o-forms__textarea etc.
<div class="o-forms">
    <label for="o-forms-demo-text" class="o-forms__label">Text input example</label>
    <input type="text" id="o-forms-demo-text" class="o-forms__text" required />

Additonal field information

It is possible to add additional information to form fields with .o-forms__additional-info. Additonal information is not limited to a text input, it could also apply against a select, textarea, checkbox, etc.

<div class="o-forms">
    <label for="o-forms-demo-additional" class="o-forms__label">Text input example</label>
    <div class="o-forms__additional-info">Additional info follows the label if required.</div>
    <input type="text" id="o-forms-demo-additional" class="o-forms__text" required />

Optional fields

To indicate to the user that a field is optional, add the .o-forms__label--optional class to the label of the field. Any field label can be marked optional in this way.

<div class="o-forms">
    <label for="o-forms-demo-optional" class="o-forms__label o-forms__label--optional">Optional  input example</label>
    <input type="text" id="o-forms-demo-optional" class="o-forms__text" />

Wide fields

The class .o-forms--wide can be used to show form fields without width restrictions. This is demonstrated below with a text input but can apply to any .o-forms field.

<div class="o-forms o-forms--wide">
    <label for="o-forms-full" class="o-forms__label">Text input full width</label>
    <input type="text" id="o-forms-full" class="o-forms__text o-forms__text--valid" value="Field value" />

Text inputs

The markup above demonstrates how to use text inputs. More text input examples are available on the Origami Registry.


<div class="o-forms">
    <label for="o-forms-demo-textarea" class="o-forms__label">Textarea</label>
    <textarea id="o-forms-demo-textarea" class="o-forms__textarea"></textarea>

Textarea examples

Select boxes

<div class="o-forms">
    <label for="o-forms-demo-select" class="o-forms__label">Select box</label>
    <select id="o-forms-demo-select" class="o-forms__select">
        <option value="option1" selected>Option 1</option>
        <option value="option2">Option 2</option>
        <option value="option3">Option 3</option>
        <option value="option4">Option 4</option>

Select boxes examples

Checkbox and radio controls

Unlike other inputs, multiple checkbox and radio inputs should be wrapped in .o-forms__group to ensure correct vertical spacing.

<!-- Radio -->
<fieldset class="o-forms">
    <legend class="o-forms__label">Default radio controls</legend>
    <div class="o-forms__additional-info">Prompt text follows the legend if required.</div>
    <div class="o-forms__group">
        <input type="radio" name="radio" value="1" class="o-forms__radio" id="radio11" checked="checked" />
        <label for="radio11" class="o-forms__label">Radio 1</label>
        <input type="radio" name="radio" value="2" class="o-forms__radio" id="radio12" />
        <label for="radio12" class="o-forms__label">Radio 2</label>

<!-- Checkboxes -->
<fieldset class="o-forms">
    <legend class="o-forms__label">Default checkboxes</legend>
    <div class="o-forms__group">
        <input type="checkbox" name="checkbox" value="1" class="o-forms__checkbox" id="checkbox11" checked="checked" />
        <label for="checkbox11" class="o-forms__label">Checkbox 1</label>
        <input type="checkbox" name="checkbox" value="2" class="o-forms__checkbox" id="checkbox12" />
        <label for="checkbox12" class="o-forms__label">Checkbox 2</label>
        <input type="checkbox" name="checkbox" value="3" class="o-forms__checkbox" id="checkbox13" />
        <label for="checkbox13" class="o-forms__label">Checkbox 3</label>

To display checkboxes/radios inline add .o-forms__group--inline to their group wrapper.

<!-- Inline Radio -->
<fieldset class="o-forms">
    <legend class="o-forms__label">Radio controls inline</legend>
    <div class="o-forms__group o-forms__group--inline">
        <input type="radio" name="radio" value="1" class="o-forms__radio" id="radio41" checked="checked" />
        <label for="radio41" class="o-forms__label">Radio 1</label>
        <input type="radio" name="radio" value="2" class="o-forms__radio" id="radio42" />
        <label for="radio42" class="o-forms__label">Radio 2</label>

<!-- Inline Checkboxes -->
<fieldset class="o-forms">
    <legend class="o-forms__label">Inline checkboxes</legend>
    <div class="o-forms__group o-forms__group--inline">
        <input type="checkbox" name="checkbox" value="1" class="o-forms__checkbox" id="checkbox61" checked="checked" />
        <label for="checkbox61" class="o-forms__label">Checkbox 1</label>
        <input type="checkbox" name="checkbox" value="2" class="o-forms__checkbox" id="checkbox62" />
        <label for="checkbox62" class="o-forms__label">Checkbox 2</label>

Radio control examples
Checkbox examples

Styled radio buttons

To produce styled radio buttons wrap radio inputs with .o-forms__group .o-forms__group--inline-together and use the class .o-forms__radio-button on the radio input.

<!-- Styled radio buttons -->
<fieldset class="o-forms">
    <legend class="o-forms__label">Styled radio buttons with a default selection</legend>
    <div class="o-forms__group o-forms__group--inline-together">
        <input type="radio" name="radio5" value="daily" class="o-forms__radio-button" id="radio51"
               checked="checked" />
        <label for="radio51" class="o-forms__label">Daily</label>
        <input type="radio" name="radio5" value="weekly" class="o-forms__radio-button" id="radio52" />
        <label for="radio52" class="o-forms__label">Weekly</label>
        <input type="radio" name="radio5" value="monthly" class="o-forms__radio-button" id="radio53" />
        <label for="radio53" class="o-forms__label">Monthly</label>

Styled radio button examples


Suffixes are used to append content to an input, i.e. a button. Add a wrapper .o-forms__affix-wrapper to contain both the input and its suffix .o-forms__suffix. The suffix .o-forms__suffix wraps suffix content, i.e. a button.

<div class="o-forms">
    <label class="o-forms__label" for="text-suffix">Text input with suffix button</label>
    <div class="o-forms__affix-wrapper">
        <input id="text-suffix" type="text" class="o-forms__text" />
        <div class="o-forms__suffix">
            <button type="button" class="o-buttons o-buttons--secondary o-buttons--big">Go</button>

Suffixes examples

Toggles (checkbox toggles)

To produce a toggle interface wrap checkboxes in .o-forms__toggle and add the data attribute data-o-form-toggle to the checkbox input.

<!-- Toggle -->
<fieldset class="o-forms">
    <legend class="o-forms__label">Checkbox Toggle</legend>
    <div class="o-forms__group">
        <div class="o-forms__toggle">
            <input class="o-forms__toggle__checkbox" data-o-form-toggle type="checkbox" name="checkbox" value="1" id="checkboxToggle1" />
            <label for="checkboxToggle1" class="o-forms__label">Checkbox Toggle 1</label>
        <div class="o-forms__toggle">
            <input class="o-forms__toggle__checkbox" data-o-form-toggle type="checkbox" name="checkbox" value="1" id="checkboxToggle2" checked="checked" />
            <label for="checkboxToggle2" class="o-forms__label">Checkbox Toggle 2</label>

Toggle examples

Validation states

Validation styles are applied by adding .o-forms--error or .o-forms--valid to the field's containing element (typically, .o-forms). Child .o-forms__label, .o-forms__text, .o-forms__select, .o-forms__checkbox, .o-forms__radio, .o-forms__textarea elements will be styled appropriately.

An error message, defined with .o-forms__errortext, can be appended to the containing element.

<div class="o-forms o-forms--error">
    <label class="o-forms__label">Text input</label>
    <input type="text" class="o-forms__text" />
    <div class="o-forms__errortext">Please enter a valid url</div>


Wrap a group of .o-forms fields in a section .o-forms-section to highlight them and provide a global message.

<div class="o-forms-section">
    <div class="o-forms-section__message">
        <p>This is a section message.</p>
    <div class="o-forms">
        <label for="o-forms-message" class="o-forms__label">Field Label</label>
        <input type="text" id="o-forms-message" class="o-forms__text" required />

This can be used to highlight errors within a section of a form which contains multiple .o-forms fields.

<div class="o-forms-section o-forms-section--error">
    <div class="o-forms-section__message">
        <p>This is a section error message</p>
    <div class="o-forms o-forms--error">
        <label for="o-forms-section-error" class="o-forms__label">Field Label</label>
        <input type="text" id="o-forms-section-error" class="o-forms__text" required />
        <div class="o-forms__errortext">Invalid entry</div>

Add .o-forms-section--wide to remove max width restrictions.

Section examples

Additional Features

Additional features such as small text and select inputs are avalible. Please see more examples on the Origami Registry.


Silent mode

In silent mode o-forms provides mixins for each set of form fields as well as some mixins to output basic form styles in one large chunk.

The oForms mixin will output all features of o-forms. Turn off silent mode to output all o-forms features using this mixin automatically.

$o-forms-is-silent: false;
@import 'o-forms/main';


If your project does not need all o-forms features, you may reduce your project's CSS bundle size by using the following mixins to only output what you need.


  • oFormsBaseFeatures - Basic form features including .o-forms. Required by the other mixins.

Optional extras:

  • oFormsRadioCheckboxFeatures - Checkbox and radio support.
  • oFormsRadioCheckboxRightModifier - Modifier styles to align checkbox/radio inputs right.
  • oFormsRadioButtonsStyledFeature - Styled radio buttons support.
  • oFormsCheckboxToggleFeature - Toggle support.
  • oFormsSuffixFeature - Suffix support.
  • oFormsSectionFeature - Section support.
  • oFormsSmallFeature - Modifier styles for small text and select inputs.
  • oFormsWideFeature - Modifier styles for form elements with no width restriction.

For more details on specific mixins browse the SassDoc documentation of the module.


o-forms provides some JavaScript to help with validating form inputs. The validation works with the browsers built-in validation API to add the appropriate o-forms classes when a user is completing or submitting a form.

By default, o-forms listens to the blur event of an input field, when a user leaves a field, the input will be validated and if an invalid input is found, the error class will be added to the input.

Additionally o-forms fires an event oForms.toggled when a toggle checkbox is clicked.

#### Constructing

An o-forms object must be constructed for every <form> element you have on your page that you want to validate with this module. You can do this for explicit elements like so:

const OForms = require('o-forms');
const formsEl = document.querySelector('#form-element');

const forms = new OForms(formsEl);

Alternatively, an o.DOMContentLoaded event can be dispatched on the document to auto-construct an o-forms object for each element with a data-o-component="o-forms" attribute:

document.addEventListener("DOMContentLoaded", function() {
    document.dispatchEvent(new CustomEvent('o.DOMContentLoaded'));

Validate on form submit

By default o-forms tests inputs on the blur event for each input. To defer the validation to the submit event of the form, you can pass an config object to set the testEvent when constructing o-forms:

const OForms = require('o-forms');
const formsEl = document.querySelector('#form-element');

const forms = new OForms(formsEl, { testEvent: 'submit' });

Or you can set an attribute on the <form> element to declaratively set the test event:

<form data-o-component="o-forms" data-o-forms-test-event="submit">

Apply valid state class

By default o-forms does not apply the o-forms--valid class, relying on the absence of o-forms--error to indicate an input's validity. To have o-forms apply a class for valid inputs, pass in a applyValidState boolean configuration property when constructing:

const OForms = require('o-forms');
const formsEl = document.querySelector('#form-element');

const forms = new OForms(formsEl, { applyValidState: true });

Or you can set the data-o-forms-apply-valid-state attribute to true on the <form> element:

<form data-o-component="o-forms" data-o-forms-apply-valid-state="true">

Listening to a toggle change

Listening for the oForms.toggled event we can react to the status of a toggle checkbox. This event is fired when the toggle checkbox is clicked.

document.addEventListener('oForms.toggled', (event) => {
    console.log(`${event.target.id} is ${(event.target.checked ? 'on' : 'off')}`);
}, false);


  • Checkboxes and radio controls will not receive custom styling in IE =< 8, though they'll still receive default browser styling
  • In older versions of Firefox and depending on the version of the operating system, the select dropdowns might a default system arrow and a decorated arrow
  • In some browsers (e.g. iOS 6, Android < 4.4…) there is no space between the select arrow and the right edge of the element. This is caused by a lack of CSS background-position edge offsets

There are a number of inconsistencies in how browsers handle form events, validation and auto-fill. The Membership team has documented the quirks it ran into during the development of the Sign Up app.

Migration Guide

Upgrading from v4.x.x to v5.x.x

Version 5 makes some design improvements including tightening up the spacing around checkboxes and radio buttons. It also provides many new mixins to make it easier to output o-forms features granularly.

Checkboxes & Radios

  • Wrap groups of checkboxes and radios in .o-forms__group for correct vertical spacing.
  • oFormsRadioCheckbox no longer outputs all styles for checkboxes and radios, only what is shared between them. Use oFormsRadioCheckboxFeatures instead.
  • It is no longer possible to modify the complete selector of radios, checkboxes, or their labels. The base .o-forms class may still be updated using the $class argument.

Prefix, Suffix

  • Prefixes have been removed entirely. We recommend using additional label information and feedback in form validation instead.
  • Suffix buttons now use standard o-buttons styling.
  • Check your uses of suffixs still display correctly. In the case of button suffixes it may be necessary to apply the extra o-buttons classes .o-buttons--secondary and .o-buttons--big.
  • The mixins oFormsAffixButton, oFormsAffixCheckbox, oFormsPrefixSuffix have been removed. Use oFormsSuffixFeature for suffix classes including the affix wrapper (as documented above).


  • .o-forms__checkbox-toggle has been renamed .o-forms__toggle.
  • The oFormsCheckboxToggleSize mixin has been removed due to lack of use.

Wrappers and Messages

  • Wrappers have been renamed to sections. Their class names have also been updated to conform to the BEM naming convention (as optional containers their name should not contain __ as they are not elements of a block).
    • .o-forms__wrapper becomes .o-forms-section.
    • .o-forms__wrapper--highlight becomes .o-forms-section--highlight.
    • .o-forms__wrapper--error which becomes .o-forms-section--error.
    • The oFormsMessage mixin now only outputs minimal message styles, uses should be replaced with oFormsSectionFeature.
  • Messages are now child elements of a section and must not be used independently.
    • Wrap messages within a form section .o-forms-section if they are not already. They should be the first child of the section.
    • Remove the class .o-forms__message--error. A message now infers that it is an error message based on its parent section .o-forms-section--error.

Other changes

  • oFormsFullWidth has been removed. Use oFormsWideFeature for classes to remove form max width restrictions.
  • There is a new dependancy on o-icons. Build your project to confirm that it is compatible.

Please contact us if you have any queries.

Upgrading from v3.x.x to v4.x.x

  • A dependency on o-typography v5 has been introduced. This will break any builds that use o-typography <v5. Resolution: Update to o-typography v5.
  • The o-colors dependency has been updated to ^4. This could create bower conflicts which should be resolved by updating to the newest release of o-colors.
  • The design for o-forms has changed in v4. This could create issues on your pages which make use of o-forms. Ensure that the updated design does not break the layout on your webpage.

Upgrading from v2.x.x to v3.x.x

The main change in v2 is that classes provided by o-forms now conform more strictly to the BEM naming convention. All form field classes now follow the element convention, so o-forms-text is now o-forms__text.

There is also now a main block class of o-forms which replaces the previous o-forms-group class. Full class changes are below:

  • o-forms-group becomes o-forms
  • Search templates for o-forms-xxxxx and replace with o-forms__xxxxx

An example of the changes should be:

-<div class="o-forms-group">
+<div class="o-forms">

-<label class="o-forms-label"></label>
+<label class="o-forms__label"></label>

-<input type="radio" class="o-forms-radio" />
+<input type="radio" class="o-forms__radio" />

-<input type="checkbox" class="o-forms-checkbox" />
+<input type="checkbox" class="o-forms__checkbox" />

-<input type="text" class="o-forms-text" />
+<input type="text" class="o-forms__text" />

Any modifier classes like o-forms--error have remained the same.

Upgrading from 0.x.x

1. Module name change

o-ft-forms becomes o-forms in v1.

  1. Search o-ft-forms and replace with o-forms
  2. Search oFtForms and replace with oForms

2. Web fonts and icons

In the v0.x.x of o-forms, the module loaded webfonts itself and was setting its own font-family.

The module now inherits the font-family set in your application and doesn't embed web fonts anymore.

Solution: products must load webfonts themselves (tipically, with o-fonts and o-ft-icons).

<!-- Load web fonts and icons with @font-face declarations  -->
<link rel="stylesheet" href="https://www.ft.com/__origami/service/build/v2/bundles/css?modules=o-fonts@^3.0.0,o-icons@^5.0.0" />

<!-- Set the font family on the whole document -->
    html {
        font-family: BentonSans, sans-serif;

3. Helper classes name changes

The most important change is with input elements, that now have their own classes:

-<input type="radio" class="o-ft-forms__field" />
+<input type="radio" class="o-forms-radio" />

-<input type="checkbox" class="o-ft-forms__field" />
+<input type="checkbox" class="o-forms-checkbox" />

-<input type="text" class="o-ft-forms__field" />
+<input type="text" class="o-forms-text" />
  • o-ft-forms__field-group > o-forms-group
  • o-ft-forms__field--textarea becomes o-forms-textarea
  • o-ft-forms__field--select becomes o-forms-select
  • Any o-ft-forms__xxxx becomes o-forms-xxxx
  • Search templates for o-ft-forms__global-message--error and replace with o-forms-message o-forms-message--error
  • Search templates for o-forms-error-wrapper and replace with o-forms-wrapper o-forms-wrapper--error

o-ft-forms__section is deprecated: sections (previously <fieldset class="o-ft-forms__section">) have to be styled at a product level. Since fieldsets have browser-specific styling issues, prefer <div> elements.

4. Mixins and placeholder classes

If using placeholder classes or extending styles using oFormsClass and oFormsPlaceholderOptionalSelector, use normal selectors, and include the matching mixins, documented in the SassDoc documentation of the module.


If you have any questions or comments about this component, or need help using it, please either raise an issue, visit #ft-origami or email Origami Support.


This software is published by the Financial Times under the MIT licence.