Form Molecules

Crate ReNew Updates

APPROVED. Keir has approved. Dev implementation in progress.

  • Toggle Switch is approved by Keir.
  • Compound Quantity is approved by Keir.
  • Button Group is approved by Keir.

Toggle Switch

The Code:

Notes/Usage
  • Size of switch can be controlled just by overriding font-size on .button-toggle. Default value is 10px. This will scale the entire switch.
  • Code underneath is a <button aria-pressed> for maximum cross-screen-reader support. Styling is based on state using aria-pressed value as hook.
  • Accessibility
    • Color contrast ratios met for all states.
    • Focus state styling included.
    • Compatible with High Constrast Mode.
    • Compatible with text-only zoom to at least 200%.
    • All roles and states clearly communicated to screen readers.
    • Keyboard-only compatible.
New Variables

$toggle-switch-track-background-color: #fff;
$toggle-switch-track-border-color: #666;
$toggle-switch-track-width: 4.4em;
$toggle-switch-track-height: 1.6em;

$toggle-switch-thumb-width: 2.4em;
$toggle-switch-thumb-height: 2.4em;

$toggle-switch-thumb-off-background-color: #ccc;
$toggle-switch-thumb-off-text-color: transparent;
$toggle-switch-thumb-off-border-color: #666;
$toggle-switch-thumb-off-border-width: .1em;

$toggle-switch-thumb-on-background-color: #222;
$toggle-switch-thumb-on-text-color: transparent;
$toggle-switch-thumb-on-border-color: #222;
$toggle-switch-thumb-on-border-width: 1.2em;

HTML

<button id="toggle-switch-example" type="button" aria-pressed="false" class="button-toggle" onclick="toggleSwitch();">
    <span class="sr-only">Short Descriptive Name</span>
    <span class="toggle-track"></span>
</button>

SCSS

.button-toggle {
    box-sizing: border-box;
    display: inline-block;
    border: 0 !important; // !important to enforce in hcm
    margin: 0;
    padding: .4em;
    background: transparent;
    font-size: 10px;
    position: relative;
    .toggle-track {
        box-sizing: border-box;
        display: block;
        border: solid .1em;
        border-radius: 0.8em;
        width: $toggle-switch-track-width;
        height: $toggle-switch-track-height;
        background: $toggle-switch-track-background-color;
        color: $toggle-switch-track-border-color;
    }
    &:focus {
        outline: 0;
        .toggle-track {
            &::before {
                content: "";
                box-sizing: border-box;
                display: block;
                border: inherit;
                border-radius: 0.9em;
                position: absolute;
                top: .2em;
                right: .2em;
                bottom: .2em;
                left: .2em;
            }
        }
    }
    &::before, 
    &::after {
        content: "";
        box-sizing: border-box;
        display: block;
        border-style: solid;
        border-radius: 50%;
        width: $toggle-switch-thumb-width;
        height: $toggle-switch-thumb-height;
        line-height: $toggle-switch-thumb-height; 
        text-align: center;
        position: absolute;
        top: 0;
        transition: left .1825s ease-in-out;
        z-index: 2;
    }
    &[aria-pressed="false"] {
        &::before, 
        &::after {
            border-color: $toggle-switch-thumb-off-border-color;
            border-width: $toggle-switch-thumb-off-border-width;
            background: $toggle-switch-thumb-off-background-color;
            color: $toggle-switch-thumb-off-text-color;
            left: -.1em;
        }
        &::after {
            content: "Off";
        }
    }
    &[aria-pressed="true"] {
        &::before, 
        &::after {
            border-color: $toggle-switch-thumb-on-border-color;
            border-width: $toggle-switch-thumb-on-border-width;
            background: $toggle-switch-thumb-on-background-color;
            color: $toggle-switch-thumb-on-text-color;
            left: calc((100% + .1em) - #{$toggle-switch-thumb-width});
        }
        &::after {
            content: "On";
            border-width: calc(.2em + #{$toggle-switch-thumb-off-border-width});
            //line-height:  calc(2px + #{$toggle-switch-thumb-height} - (2px + #{$toggle-switch-thumb-off-border-width})); 
            line-height: calc(#{$toggle-switch-thumb-height} - (.3em + #{$toggle-switch-thumb-off-border-width});
        }
    }
}

Compound Quantity

Product Name

The Code:

Notes/Usage
  • JavaScript is for demo only. Production code will be more nuanced and built in React.
  • Accessibility
    • Color contrast ratios met for all states.
    • Focus state styling included.
    • Compatible with High Constrast Mode.
    • All roles and states clearly communicated to screen readers.
    • Keyboard-only compatible.
HTML

<fieldset class="compound-quantity-fieldset">
    <legend class="compund-quantity-legend">Product Name</legend>
    <div class="compound-quantity">
        <label for="input-quantity-unique-id" class="compound-quantity-label">
            Quantity
        </label>
        <button type="button" class="button button-lg button-quantity button-quantity-decrease">
            <svg class="svg-icon-minus" focusable="false" aria-hidden="true"><use xlink:href="#svg-icon-minus"></use></svg>
            <span class="sr-only">Decrease</span>
        </button>
        <input id="input-quantity-unique-id" class="field-qty input-lg" type="number" value="1" />
        <button type="button" class="button button-lg button-quantity button-quantity-increase">
            <svg class="svg-icon-plus" focusable="false" aria-hidden="true"><use xlink:href="#svg-icon-plus"></use></svg>
            <span class="sr-only">Increase</span>
        </button>
    </div>
</fieldset>

Updated Mixin

@mixin mixin-field-element-qty {
    padding-left: 5px;
    padding-right: 5px;
    text-align: center;
    -moz-appearance: textfield;
    -webkit-appearance: textfield;
    appearance: textfield;
    &::-webkit-inner-spin-button,
    &::-webkit-outer-spin-button {
        -webkit-appearance: none;
    }
    &.input-xl,
    &.input-l {
        font-size: 16px;
    }
}

SCSS

.compound-quantity-fieldset {
    .compound-quantity-legend {
        @include sr-only;
    }
    .compound-quantity {
        display: flex;
        align-items: center;
        margin-bttom: 8px;
        .compound-quantity-label {
            @include sr-only;
        }
        .button-quantity {
            margin: 0;
        }
    }
    [class*="svg-icon"] {
        font-size: 10px;
        width: 1.6em;
        height: 1.6em;
        max-width: 48px;
        max-height: 48px;
        margin: 8px;
        padding: 2px;
        color: inherit;
        stroke-width: 4;
    }
}

.button-quantity {
    @include mixin-button-oneoff(
        /* Quantity Control Buttons - Default */
        $button-oneoff-border: #f0efed,
        $button-oneoff-background: #f0efed,
        $button-oneoff-color: #222,
        /* Quantity Control Buttons - Hover */
        $button-oneoff-border-hover: #f0efed,
        $button-oneoff-background-hover: #f0efed,
        $button-oneoff-color-hover: #222,
        /* Quantity Control Buttons - Focus */
        $button-oneoff-border-focus: #f0efed,
        $button-oneoff-background-focus: #f0efed,
        $button-oneoff-color-focus: #222,
        $button-oneoff-ring-color-focus: #666,
        $button-oneoff-box-shadow-focus: none,
        /* Quantity Control Buttons - Disabled */
        $button-oneoff-border-disabled: #ccc,
        $button-oneoff-background-disabled: #fff,
        $button-oneoff-color-disabled: #ccc,
        $button-oneoff-ring-color-disabled-focus: #ccc
    );
    &.button {
        padding: 0;
    }
    &.button-lg {
        min-width: $button-lg-height;
    }
    letter-spacing: 0;
}

Button Group

Variation 1: Default

Descriptive Name of Group

Variation 2: 50-50

Descriptive Name of Group

Variation 3: 33-33-33

Descriptive Name of Group

Variation 4: 25-25-25-25

Descriptive Name of Group
The Code:
Notes/Usage
  • Multiple options for items per line
    • Default - Each item's width is determined by its content.
    • 50/50 - Group fills 100% width of the container it is placed in. Each item's width is 50% of the container.
    • 33/33/33 - Group fills 100% width of the container it is placed in. Each item's width is 33.3333% of the container.
    • 25/25/25/25 - Group fills 100% width of the container it is placed in. Each item's width is 25% of the container.
  • Individual items can be made double-, triple-, or quadruple-wide with an add-on class.
    • .button-group-item.button-group-item-double
    • .button-group-item.button-group-item-triple
    • .button-group-item.button-group-item-quad
  • Code underneath is a radio button group creating a single tab stop for the set. Use arrows keys to move between items within the set.
  • Accessibility
    • Color contrast ratios met for all states.
    • Focus state styling included.
    • Compatible with High Constrast Mode.
    • Compatible with text-only zoom to at least 200%.
    • All roles and states clearly communicated to screen readers.
    • Keyboard-only compatible.
New Variables

$button-group-gap: 5px;
$button-group-label-default-border-color: #f0efed;
$button-group-label-default-background: #f0efed;
$button-group-label-default-color: #222;
$button-group-label-default-text-transform: none;

$button-group-label-checked-border-color: #666;
$button-group-label-checked-background: #f0efed;
$button-group-label-checked-color: #222;
$button-group-label-checked-text-transform: none;

$button-group-label-disabled-border-color: #ccc;
$button-group-label-disabled-background: #fff;
$button-group-label-disabled-color: #ccc;
$button-group-label-disabled-text-transform: none;

$button-group-label-focus-ring-color: #666;

SCSS

.button-group-fieldset {
    .button-group-legend {
        @include sr-only;
    }
}
.button-group {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -#{$button-group-gap};
    .button-group-item {
        &.button-group-item-double {
            flex-grow: 2;
        }
        &.button-group-item-triple {
            flex-grow: 3;
        }
        &.button-group-item-quad {
            flex-grow: 4;
        }
        // State: Default
        .button-item-label {
            margin: 0 $button-group-gap calc(#{$button-group-gap} * 2));
            height: auto;
            width: calc(100% - (#{$button-group-gap} * 2); 
            border-width: $button-border-width;
            border-color: $button-group-label-default-border-color;
            background: $button-group-label-default-background;
            color: $button-group-label-default-color;
            text-transform: $button-group-label-default-text-transform;
        }
        // State: Checked
        .button-item-input:checked + label.button-item-label {
            outline: solid $button-border-width+2px transparent; // a11y - hcm
            border-color: $button-group-label-checked-border-color;
            background: $button-group-label-checked-background;
            color: $button-group-label-checked-color;
            text-transform: $button-group-label-checked-text-transform;
        }
        // State: Disabled
        .button-item-input:disabled + label.button-item-label {
            border-width: $button-border-width;
            border-color: $button-group-label-disabled-border-color;
            background: $button-group-label-disabled-background;
            color: $button-group-label-disabled-color;
            text-transform: $button-group-label-disabled-text-transform;
            &::before {
                // a11y - hcm
                content: "";
                border: solid 1px transparent;
                margin: 0 10px;
                width: calc(100% - 20px);
                position: absolute;
                top: 50%;
                left: 0;
                transform: translateY(-50%); 
            }
        }
        // State: Focused
        .button-item-input:focus + label.button-item-label {
            @include mixin-button-focus-ring($button-group-label-focus-ring-color);
        }
    }
    &.button-group-50-50 {
        .button-group-item {
            width: 50%;
            &.button-group-item-double {
                // creates a double wide item
                width: 100%;
            }
        }
    }
    &.button-group-33-33-33 {
        .button-group-item {
            width: 33.3333%;
            &.button-group-item-double {
                // creates a double wide item
                width: 66.66%;
            }
            &.button-group-item-triple {
                // creates a triple wide item
                width: 100%;
            }
        }
    }
    &.button-group-25-25-25-25 {
        .button-group-item {
            width: 25%;
            &.button-group-item-double {
                // creates a double wide item
                width: 50%;
            }
            &.button-group-item-triple {
                // creates a triple wide item
                width: 75%;
            }
            &.button-group-item-quad {
                // creates a triple wide item
                width: 100%;
            }
        }
    }
}

HTML - Variation 1: Default

<fieldset class="button-group-fieldset">
    <legend class="button-group-legend">Descriptive Name of Group</legend>
    <div class="button-group">
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-15" class="sr-only button-item-input" checked />
            <label for="button-group-15" class="button button-lg button-item-label">Item 1</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-16" class="sr-only button-item-input" />
            <label for="button-group-16" class="button button-lg button-item-label">Item 2</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-17" class="sr-only button-item-input" />
            <label for="button-group-17" class="button button-lg button-item-label">Item 3</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-18" class="sr-only button-item-input" disabled />
            <label for="button-group-18" class="button button-lg button-item-label">Item 4</label>
        </div>
    </div>
</fieldset>

HTML - Variation 2: 50-50

<fieldset class="button-group-fieldset">
    <legend class="button-group-legend">Descriptive Name of Group</legend>
    <div class="button-group button-group-50-50">
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-15" class="sr-only button-item-input" checked />
            <label for="button-group-15" class="button button-lg button-item-label">Item 1</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-16" class="sr-only button-item-input" />
            <label for="button-group-16" class="button button-lg button-item-label">Item 2</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-17" class="sr-only button-item-input" />
            <label for="button-group-17" class="button button-lg button-item-label">Item 3</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-18" class="sr-only button-item-input" disabled />
            <label for="button-group-18" class="button button-lg button-item-label">Item 4</label>
        </div>
        <div class="button-group-item button-group-item-double">
            <input type="radio" name="button-group-d" id="button-group-19" class="sr-only button-item-input" />
            <label for="button-group-19" class="button button-lg button-item-label">Item 5</label>
        </div>
    </div>
</fieldset>

HTML - Variation 3: 33-33-33

<fieldset class="button-group-fieldset">
    <legend class="button-group-legend">Descriptive Name of Group</legend>
    <div class="button-group button-group-33-33-33">
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-15" class="sr-only button-item-input" checked />
            <label for="button-group-15" class="button button-lg button-item-label">Item 1</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-16" class="sr-only button-item-input" />
            <label for="button-group-16" class="button button-lg button-item-label">Item 2</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-17" class="sr-only button-item-input" />
            <label for="button-group-17" class="button button-lg button-item-label">Item 3</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-18" class="sr-only button-item-input" />
            <label for="button-group-18" class="button button-lg button-item-label">Item 4</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-19" class="sr-only button-item-input" />
            <label for="button-group-19" class="button button-lg button-item-label">Item 5</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-20" class="sr-only button-item-input" disabled />
            <label for="button-group-20" class="button button-lg button-item-label">Item 6</label>
        </div>
        <div class="button-group-item button-group-item-triple">
            <input type="radio" name="button-group-d" id="button-group-21" class="sr-only button-item-input" />
            <label for="button-group-21" class="button button-lg button-item-label">Item 7</label>
        </div>
    </div>
</fieldset>

HTML - Variation 4: 25-25-25-25

<fieldset class="button-group-fieldset">
    <legend class="button-group-legend">Descriptive Name of Group</legend>
    <div class="button-group button-group-25-25-25-25">
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-15" class="sr-only button-item-input" checked />
            <label for="button-group-15" class="button button-lg button-item-label">Item 1</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-16" class="sr-only button-item-input" />
            <label for="button-group-16" class="button button-lg button-item-label">Item 2</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-17" class="sr-only button-item-input" />
            <label for="button-group-17" class="button button-lg button-item-label">Item 3</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-18" class="sr-only button-item-input" />
            <label for="button-group-18" class="button button-lg button-item-label">Item 4</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-19" class="sr-only button-item-input" />
            <label for="button-group-19" class="button button-lg button-item-label">Item 5</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-20" class="sr-only button-item-input" />
            <label for="button-group-20" class="button button-lg button-item-label">Item 6</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-21" class="sr-only button-item-input" />
            <label for="button-group-21" class="button button-lg button-item-label">Item 7</label>
        </div>
        <div class="button-group-item">
            <input type="radio" name="button-group-d" id="button-group-22" class="sr-only button-item-input" disabled />
            <label for="button-group-22" class="button button-lg button-item-label">Item 8</label>
        </div>
        <div class="button-group-item button-group-item-quad">
            <input type="radio" name="button-group-d" id="button-group-22b" class="sr-only button-item-input" />
            <label for="button-group-22b" class="button button-lg button-item-label">Item 9</label>
        </div>
        <div class="button-group-item button-group-item-double">
            <input type="radio" name="button-group-d" id="button-group-22c" class="sr-only button-item-input" />
            <label for="button-group-22c" class="button button-lg button-item-label">Item 10</label>
        </div>
        <div class="button-group-item button-group-item-double">
            <input type="radio" name="button-group-d" id="button-group-22d" class="sr-only button-item-input" />
            <label for="button-group-22d" class="button button-lg button-item-label">Item 11</label>
        </div>
    </div>
</fieldset>