Skip to content

Latest commit

 

History

History
458 lines (356 loc) · 24.4 KB

Checkbox.md

File metadata and controls

458 lines (356 loc) · 24.4 KB

Checkbox component specification

The Checkbox component allows a user to choose between two mutually exclusive options.

Related variant considerations

  • Toggle: should be a separate component because it has different anatomy & warrants unique themability.

  • Indeterminate/Tri State: should support groups with hierarchical checkboxes where the parent checkbox can be in a mixed state if its children checkboxes aren't all checked (and if they are all checked, then the parent is checked; same if they are all unchecked, then the parent is unchecked).

Reference implementations

https://codesandbox.io/s/checkboxes-ggpx1

Note about the Stardust example: there's some weirdness with how the theme providers are interacting with each other, the Stardust checkbox's styling is messing up as a result.

Fabric Checkbox docs

Stardust Checkbox docs

Open UI Checkbox docs

Material UI Checkbox docs

BaseUI Checkbox docs

Chakra Checkbox docs

Carbon Checkbox docs

AntD Checkbox docs

FastDNA Checkbox docs

Props

TODO: Consult the prop wizard to derive consistently defined props.

Name Type Default value

Fabric Checkbox props

https://developer.microsoft.com/en-us/fabric#/controls/web/checkbox

Name Type Notes
ariaDescribedBy string
ariaLabel string
ariaLabelledBy string
ariaPositionInset number
ariaSetSize number
boxSide 'start' or 'end' default 'start'
checked boolean
checkmarkIconProps IIconProps
className string
componentRef IRefObject
defaultChecked boolean
defaultIndeterminate boolean
disabled boolean
indeterminate boolean
inputProps React.ButtonHTMLAttributes
keytipProps IKeytipProps
label string
onChange (ev, checked) => void
onRenderLabel IRenderFunction
styles IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles>
theme ITheme

Stardust Checkbox props

Name Type Notes
accessibility Accessibility
animation AnimationProp
as React.ElementType default type is "div"
checked boolean default false
className string
defaultChecked boolean default false
design ComponentDesign
disabled boolean default false
icon ShorthandValue {}
label ShorthandValue
labelPosition enum (Values: start, end) "end"
onChange ComponentEventHandler
onClick ComponentEventHandler
styles ComponentSlotStyle
toggle boolean default false
variables any

Differences of Fabric/Stardust to resolve

Name Type Notes
accessibility Accessibility
animation AnimationProp
as React.ElementType default type is "div"
ariaDescribedBy string
ariaLabel string
ariaLabelledBy string
ariaPositionInSet number if checkbox is in a set, should be up to the user to provide a11y
ariaSetSize number
boxSide 'start' or 'end' default 'start'
checkmarkIconProps IIconProps
componentRef IRefObject
defaultIndeterminate boolean
indeterminate boolean
inputProps React.ButtonHTMLAttributes
keytipProps IKeytipProps
labelPosition enum (Values: start, end) "end"
onRenderLabel IRenderFunction
onClick ComponentEventHandler
theme ITheme
toggle boolean default false
variables any

Recommended props

Name Type
ariaDescribedBy string
ariaLabel string
ariaLabelledBy string
as keyof JSX.IntrinsicElements
checked boolean | 'mixed'
className string
defaultChecked boolean
disabled boolean
label string
name string
onChange (ev: Event, value: boolean) => void
labelPosition start or end

Note: rtl, styles, and theme come from compose or the ThemeProvider. And name has been added to support checkbox in form scenarios.

Removing the following two props because the ARIA spec dictates role='checkbox' doesn't need aria-posinset and aria-setsize. These are only valid for role='option' which is only in the case the checkbox is a part of a listbox, which is not something we need to account for in the base component API. If the user does need to provide these two props, slotProps could be used to apply additional props to any slot.

Name Concern
ariaPositionInset if checkbox is in a set, should be up to the user to provide a11y
ariaSetSize same as above

Conversion process from Fabric 7 to Fluent UI Checkbox

Fluent Checkbox recommended props interface

Name To transition or not? Property transitioned? Breaking change? Codemod/Shim created?
ariaDescribedBy User provided
ariaLabel User provided
ariaLabelledBy User provided
ariaPositionInSet Won't be transitioned
ariaSetSize Won't be transitioned
boxSide No; labelPosition
checked Yes - native
checkmarkIconProps No
className Yes - native
defaultChecked Yes - native
disabled Yes - native
keytipProps Yes - redesign
label Yes - native
onChange Yes - native
onRenderLabel No; shorthand
styles TBD
theme TBD

Props being removed:

ariaPoisitionInSet and ariaSetSize - when writing parent component, user should set these on the checkbox.

Slots

Name Considerations
root label
input actual checkbox element - what gets checked/unchecked
icon visual checkmark, sideways if indeterminate
box wraps input & icon - what actual gets the styling

TODO: indeterminate state icon - need consensus on what this should look like

DOM structure

General considerations:

Only use as toggle between two mutually exclusive options (binary) or in a group with shared context to offer multiple options.

Uncontrolled vs. controlled: implemented in the prototype already through useControlledState React hook. Indeterminate state: When children checkboxes aren't checked, don't check parent checkbox.

Could consider supporting an invalid state/error state but this might just be supported via styling that's passed in by the user and done through compose.

Recommended DOM

<label class="checkbox-root">
  <div class="box">
    <input class="checkbox" role="checkbox" aria-checked"false" aria-label="Fluent checkbox">
    <i class="icon"></i>
  </div>
</label>

Fabric Checkbox example DOM

<div class="ms-Checkbox-checkbox">
  <input
    type="checkbox"
    class="input-226"
    id="checkbox-268"
    aria-label="Unchecked checkbox (uncontrolled)"
    aria-checked="false"
  />
  <label class="ms-Checkbox-label label-227" for="checkbox-268">
    <div class="ms-Checkbox-checkbox checkbox-228">
      <i data-icon-name="CheckMark" aria-hidden="true" class="ms-Checkbox-checkmark checkmark-231"> </i>
    </div>
    <span aria-hidden="true" class="ms-Checkbox-text text-230">Unchecked checkbox (uncontrolled)</span>
  </label>
</div>

Stardust Checkbox example DOM

<div
  class="ui-checkbox dd ol om gz de nb on cd oo op cb oq ha hb hc hd he hf hg hh hi hj hk hl hm hn ho hp or os ot ou hu hv hw hx ov ow ox oy ic id ie if ig ih ii ij ik il im oz pa pb pc ir is it iu pd pe pf pg lu ph pi pj pk"
  aria-checked="false"
  role="checkbox"
  tabindex="0"
>
  <span
    class="ui-icon ck cb ca jm pl pm pn po pp pq pr ba bb bc bd do dp jy jz ps pt pu pv pw px gu jo gw py pz qa qb ui-checkbox__indicator"
    role="img"
    aria-hidden="true"
  >
    <svg role="presentation" focusable="false" viewBox="8 8 16 16" class="cz ql qm da cw">
      <g>
        <path
          class="ui-icon__outline cy"
          d="M14.3 21.3c-.1 0-.3 0-.4-.1l-4.8-4.8c-.2-.2-.2-.5 0-.7s.5-.2.7 0l4.4 4.4 7.9-7.9c.2-.2.5-.2.7 0s.2.5 0 .7l-8.3 8.3s-.1.1-.2.1z"
        ></path>
        <path
          class="ui-icon__filled"
          d="M23.5 11.875a.968.968 0 0 1-.289.711l-8.25 8.25c-.192.193-.43.289-.711.289s-.519-.096-.711-.289l-4.75-4.75a.965.965 0 0 1-.289-.711c0-.125.027-.25.082-.375s.129-.234.223-.328a.953.953 0 0 1 .695-.297c.135 0 .266.025.391.074.125.05.231.121.32.215l4.039 4.047 7.539-7.547a.886.886 0 0 1 .32-.215c.125-.049.255-.074.391-.074a1.004 1.004 0 0 1 .922.625.97.97 0 0 1 .078.375z"
        ></path>
      </g>
    </svg>
  </span>
  <span class="ui-text cz qk ui-checkbox__label" dir="auto">Make my profile visible</span>
</div>

MUI Checkbox example DOM

<label class="MuiFormControlLabel-root">
  <span
    class="MuiButtonBase-root MuiIconButton-root jss264 MuiCheckbox-root MuiCheckbox-colorSecondary jss265 Mui-checked MuiIconButton-colorSecondary"
    aria-disabled="false"
  >
    <span class="MuiIconButton-label">
      <input type="checkbox" class="jss267" value="checkedA" data-indeterminate="false" checked="" />
      <svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" role="presentation">
        <path
          d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
        ></path>
      </svg>
    </span>
    <span class="MuiTouchRipple-root"></span>
  </span>
  <span class="MuiTypography-root MuiFormControlLabel-label MuiTypography-body1">Secondary</span>
</label>

Behaviors

Aria spec: https://www.w3.org/TR/wai-aria-practices-1.1/#checkbox https://www.w3.org/TR/wai-aria-practices/#checkbox

Fluent UI HIG: https://microsoft.sharepoint-df.com/:w:/t/OPGUXLeads/EbBiGJ-gLPFGszdhSxb8X5IBFik0ax7wZLJc8FlDXOwDYA?e=Cy4Er3

Disabled state

Use aria-disabled. Screenreaders should let users know of the existence of the checkbox but it should be read-only. Ignore all events & no change to checked value allowed.

Checked state

aria-checked indicates whether element is checked (true) or unchecked (false) but can also be mixed which represents a tri-state (indeterminate) input in a situation with a group of other elements that have a mixture of checked and unchecked values.

Indeterminate state

Mixed state checkbox represents a checkbox that can support a partially checked state. If none of the checkboxes in a set are checked, the mixed state checkbox isn't checked (and if all are checked then so is the mixed state) but if the set contains a mix of checked and unchecked boxes, then the tri-state is appropriate so aria-checked will be set to mixed. Here's an example: https://www.w3.org/TR/wai-aria-practices/examples/checkbox/checkbox-2/checkbox-2.html

Focus indicators

Focus indicators should not show in mouse or touch interaction; they should only appear when keyboard tabbing/directional keystrokes are pressed, and should disappear when mouse/touch interactions occur.

Keyboarding

Key Description
Tab Moves keyboard focus to the checkbox.
Space Toggles checkbox between checked and unchecked states.

Mouse input

  • mouseenter should change the styling of checkbox to hovered state (preview what it looks like to be toggled but not full styling - checkmark, but not background color for example as in current Fabric 7 checkbox).
  • mouseleave should change the styling of checkbox back to non-hovered state (so remove the preview of checked state)
  • mousedown toggle state
  • mouseup apply styling of new state

Touch

Same behavior as above except no preview of toggled state through hover.

Screenreader accessibility:

Fluent UI & Fluent UI Northstar comparison:

From accessibility point of view the main difference is in type of element used for Checkbox itself:

  • Fluent UI has <input> element with type="checkbox"

  • Fluent UI Northstar has <div> element with role="checkbox"

    Verification with screen reader doesn't show any differences between these two approaches, everything was working as expected.

Accessibility variants:

  • two-state checkbox
  • tri-state checkbox
  • disabled

root:

  • should render the native element using the as prop, defaulting to div
  • should mix in native props expected for the element type defined in as.

Input slot:

  • receives aria-checked representing checkbox state
  • receives aria-disabled="true" representing disabled checkbox

A visible label referenced by the value of aria-labelledby (id of element containing the label) set on the element with role checkbox. If there's additional static text representing that is descriptive, aria-describedby should be set to id of element containing the description. aria-label set on the element with role checkbox.

Accessibility concerns for the user

aria-label, aria-labelledby: Describe what is the purpose of the checkbox, latter points to id of element with former.

Themability and customization

Both Fluent and Teams themes and other custom themes will be made with compose and the design tokens specified below. Screenshots of themed variants will be posted here soon after that work is done like the example code below.

The Checkbox uses react-texture to provide a recomposable implementation that has no runtime performance penalties. The BaseCheckbox implementation can be used to provide new slots and default props:

const FooCheckbox = BaseCheckbox.compose({
  tokens: {},
  styles: {},
  slots: {}
});

render() {
  <FooCheckbox defaultChecked={true} onChange={console.log("checkbox clicked!")}>
    This renders as a checkbox
  </FooCheckbox >
}

Composition

TBD

Component design tokens

Tokens represent the general look and feel of the various visual slots. Tokens feed into the styling at the right times in the right slot.

Regarding naming conventions, use a camelCased name following this format: {slot}{property}{state (or none for default)}. For example: labelSizeHovered.

Common property names: size, background, color, borderRadius

Common states: hovered, pressed, focused, checked, checkedHovered, disabled

Name Considerations
boxBorderColor
boxBorderRadius
boxBorderWidth
boxColor
boxColorDisabled
boxColorFocused
boxColorHovered
boxColorPressed
boxSize
labelColor
labelColorDisabled
labelColorFocused
labelColorHovered
labelColorPressed
labelSize
iconColor
iconColorDisabled
iconColorFocused
iconColorHovered
iconColorPressed
iconSize

NOTE! Stardust does not follow this convention. Their Checkbox currently uses these tokens:

background: string
disabledBackground: string
disabledBackgroundChecked: string
toggleBackground: string
toggleBorderColor: string
toggleIndicatorColor: string
toggleIndicatorSize: string
checkedBackground: string
checkedBorderColor: string
checkedBackgroundHover: string
checkedIndicatorColor: string
checkboxCheckedColor: string
checkboxToggleCheckedBackground: string
disabledToggleBackground: string
gap: string
borderColor: string
borderColorHover: string
checkboxColor: string
checkboxToggleCheckedBorderColor: string
checkedTextColor: string
disabledColor: string
disabledBorderColor: string
disabledToggleBorderColor: string
disabledCheckboxColor: string
disabledToggleIndicatorColor: string
disabledCheckedIndicatorColor: string
textColor: string
textColorHover: string
indicatorColor: string

Considerations for different screen sizes

Won't really look or behave differently in context of different phone/tablet/desktop sizes - as in different sizes would not cause this component to look or behave differently.

Use cases

The Checkbox component may be used within a Form component by providing the name prop to indicate the name of the input element to be fed into the form action. Example: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox

Compatibility with other libraries

TODO: If this component represents a selected value, how will that be used in an HTML form? Is there a code example to illustrate?

TODO: Is it possible this component could be rendered in a focus zone? If so, should the focus model change in that case?