Skip to content

Conversation

amyleadem
Copy link
Contributor

@amyleadem amyleadem commented Jan 9, 2024

Summary

Added styles for indeterminate checkboxes. Checkboxes will now display as indeterminate when you set input.indeterminate = true via JavaScript or add the data-indeterminate attribute.

Tip

This is only a style addition and does not affect checkbox functionality. Originally contributed by @lpsinger in #5549.

Breaking change

This is not a breaking change.

Related issue

Resolves #918

Related pull requests

Preview link

Default checkbox (w/ indeterminate toggle) →
Indeterminate checkbox →
Indeterminate checkbox tile →

Problem statement

USWDS Checkboxes do not support the :indeterminate CSS pseudo-class which can be set via JS.

More info about indeterminate on MDN Docs

Solution

Add USWDS styles for indeterminate checkboxes.

Note from #5549

The browser platform provides built-in support for indeterminate checkboxes, with the JavaScript input.indeterminate property mapped to the CSS :indeterminate selector.

However, since there is no HTML attribute to control the input.indeterminate property, I've also implemented a data-indeterminate attribute.

Major changes

  • Add indeterminate checkbox styles.
  • Migrate selected input styles to mixin.

Mixins were required to

  1. Ensure radio buttons were not affected
  2. Reduced duplicate styles
  3. Properly use $input-active-color within -checkbox-and-radio-colors mixin

Testing and review

Checkbox testing

  1. Visit checkbox story
  2. Turn on indeterminate story control or visit indeterminate story
  3. Inspect styles
  4. Inspect styles in high contrast mode
  5. Confirm indeterminate styles are properly displaying
  6. Repeat steps for tile variant

Radio testing

Radio groups without a selected item receive the :indeterminate pseudo-class, which caused visual regressions in early testing.

  1. Visit radio story.
  2. Inspect element and remove checked attribute from selected item.
  3. Confirm no regression in visual styles.
  4. Repeat steps for tile variant.

Testing checklist

  • Indeterminate styles display appropriately
  • High contrast styles display appropriately in light and dark modes
  • No visual regression in radio buttons

lpsinger and others added 25 commits November 16, 2023 18:50
Support [indeterminate checkboxes] through both the CSS
`:indeterminate` selector and also the `data-indeterminate`
attribute.

Fixes #918.

[indeterminate checkboxes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes
…io-colors.scss

Co-authored-by: mahoneycm <charlie.mahoney@bixal.com>
USWDS - Checkbox: Add styling for indeterminate checkboxes
…wds/uswds into cm-checkbox-indeterminate-high-contrast
USWDS - Indeterminate Checkbox: Cleanup scss and stories
…wds/uswds into cm-checkbox-indeterminate-high-contrast
@mahoneycm mahoneycm self-assigned this Feb 9, 2024
@mahoneycm mahoneycm changed the title [DRAFT] Checkbox indeterminate feature branch [DRAFT] USWDS - Checkbox: Add indeterminate styles Feb 9, 2024
…ntrast

USWDS - Checkbox: Create high contrast styles for indeterminate checkboxes
Copy link
Contributor

@mahoneycm mahoneycm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update 3/1/24

Ok! I resolved some of the style issues and added indeterminate radio stories + controls to make it easy to test in the future!

Left some comments with additional information to help get aquianted with new updates.

Comment on lines +150 to +160
&:disabled,
&[aria-disabled="true"] {
@include format-input {
box-shadow: $tile-box-shadow--disabled;
}

@include format-label {
border-color: $tile-border-color--disabled;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabled conflict fix

Moving the checkbox:indeterminate styles caused the newer format-input styles to take priority. These styles are required to maintain disabled box shadow and tile colors.

Alternatively, we can move these styles to the disabled styles defined in lines 182-191. I opted to move them here since they weren't required to be redefined for the disabled checkboxes without indeterminate.

Comment on lines +183 to 188
&:checked[aria-disabled="true"],
&:indeterminate:disabled,
&:indeterminate[aria-disabled="true"],
&[data-indeterminate]:disabled,
&[data-indeterminate][aria-disabled="true"] {
@include format-input {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeterminate checkbox styles alternate location

As mentioned above, we could add the styles from lines 150-160 to this block but the redefinition for checked:disabled, &:checked[aria-disabled="true"] isn't necessary.

Let me know if we'd prefer to move them here!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to leave as-is if checked:disabled, &:checked[aria-disabled="true"] don't apply.

@@ -34,12 +49,19 @@
$input-border-alpha: -0;
$tile-border-color: color("gray-20");
$tile-border-color--disabled: color("gray-10");
$tile-box-shadow--disabled: 0 0 0 units($theme-input-select-border-width)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New variable

Created this new variable so we can update the disabled tile box shadow in one location. Affects checked + indeterminate checkboxes, ignores indeterminate radio

Comment on lines 12 to 16
indeterminate: {
name: "Indeterminate option (toggle to reset)",
control: { type: "boolean" },
defaultValue: false
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New storybook control

Added new storybook control to radio so it's easy to test indeterminate or "unselected` radio styles.

Toggle the control to remove selection!

Comment on lines 46 to 59

export const Indeterminate = Template.bind({});
Indeterminate.argTypes = {
indeterminate: {
defaultValue: true,
},
};

export const IndeterminateTile = TileTemplate.bind({});
IndeterminateTile.argTypes = {
indeterminate: {
defaultValue: true,
},
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeterminate radio stories

Added indeterminate radio stories since it was easy to do so using new control.

I chose to not deactivate the control so that it's easy to reset after a selection is made!

Copy link
Contributor Author

@amyleadem amyleadem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! I added a couple of comments for optional changes to add clarity, but neither needs to be a blocker.

I performed the following tests:

  • Check display of radio and checkbox on Chrome, Firefox, and Safari
  • Confirm checkbox indeterminate stories are present and meet standards
  • In standard mode, confirm styles are consistent with other checkbox states and meet standards
  • In forced colors mode, confirm styles are consistent with other checkbox states and meet standards
  • In disabled state, confirm styles are consistent with other checkbox states and meet standards
  • Confirm no regressions in radio button component
  • Confirm radio indeterminate stories are present and meet standards
  • When $theme-body-background-color is set to a dark color (I tested “ink”), confirm styles are consistent with other checkbox states and meet standards
  • Confirm running npm run prettier:js , npm run prettier:sass and npm run lint do not generate new fixes
    • Note: Found a small change and pushed it up
  • Confirm that styling data-indeterminate and indeterminate attributes make sense
  • Confirm code meets standards

@@ -9,6 +9,11 @@ export default {
control: { type: "radio" },
options: ["none", "disabled", "aria-disabled"],
},
indeterminate: {
name: "Indeterminate option (toggle to reset)",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional

Not a blocker, but I am not sure I understand what "toggle to reset" means. Wondering if it is safe to remove?

Suggested change
name: "Indeterminate option (toggle to reset)",
name: "Indeterminate option",

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I added it is because you can still select radio buttons while the indeterminate is turned on. Toggling the switch will then properly reset the indeterminate state.

Here's a quick demo:

Kapture.2024-03-04.at.12.15.04.webm

This comment was marked as resolved.

Comment on lines 140 to 141
// Unchecked radio buttons receive :indeterminate pseudo-class.
// Because of this, we need to explicetly target checkbox :indeterminate styles.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional

I'm not sure I understand this comment. Why are radio buttons referenced if this is only for checkbox styles?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this note would add clarity as to why we aren't including the styles below with lines 127-132 (which is where they were previously defined).

I wanted to document the decision here so that if future devs know to not move them in case they miss the radio:indeterminate regressions as we did in early testing.

If we don't think it's necessary, we can remove!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be helpful to speak to that in the comment.

// Adding indeterminate styles here avoids style conflicts from setting in lines 127-129.

I wanted to document the decision here so that if future devs know to not move them in case they miss the radio:indeterminate regressions as we did in early testing.

Does that mean moving the styles to 127-132 will cause similar regressions with checkbox? If so, we can just change radio → checkbox because it looks like one affects the other.

Copy link
Contributor

@mejiaj mejiaj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues with functionality, feature looks good.

Made small edits to the PR description and suggestions to improve code clarity.


@amyleadem @mahoneycm can you resolve/hide comments that no longer apply?

For example, was this resolved?

image

Is this captured in the PR description? Should we include it?

image

@@ -9,6 +9,11 @@ export default {
control: { type: "radio" },
options: ["none", "disabled", "aria-disabled"],
},
indeterminate: {
name: "Indeterminate option (toggle to reset)",

This comment was marked as resolved.

@@ -16,6 +16,21 @@
@include -checkbox-and-radio-colors($input-type: "radio", $args...);
}

@mixin selected-tile-colors($input-active-color) {

This comment was marked as resolved.

Comment on lines 140 to 141
// Unchecked radio buttons receive :indeterminate pseudo-class.
// Because of this, we need to explicetly target checkbox :indeterminate styles.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be helpful to speak to that in the comment.

// Adding indeterminate styles here avoids style conflicts from setting in lines 127-129.

I wanted to document the decision here so that if future devs know to not move them in case they miss the radio:indeterminate regressions as we did in early testing.

Does that mean moving the styles to 127-132 will cause similar regressions with checkbox? If so, we can just change radio → checkbox because it looks like one affects the other.

@mahoneycm
Copy link
Contributor

Update 3/5/24

@mejiaj I updated and resolved the comments I was able to! Here are my responses to your other comments:

Was indeterminate radio resolved?

Yes! Radio styles now maintain the expected unselected/indeterminate state styles

Is indeterminate radio + fix captured in PR description?

Yes, it was captured in the in the "Radio Testing" section but I've moved it to "Solution" for better visibility:

Radio groups without a selected item receive the :indeterminate pseudo-class, which caused visual regressions in early testing. To avoid this, styles are added for checkbox and not radio buttons

Language surrounding indeterminate option control

Quoted from this comment:

Perhaps updating the language in parenthesis might be better?

Given a label like:
a. Toggle single indeterminate option
b. View indeterminate option
c. Test indeterminate option

With this language we know a single field is available to test indeterminate.

Using only Indeterminate option I'd expect to toggle it and all options be indeterminate. Currently only the second field is available.

The thing I want to highlight is that the :indeterminate state is set for all radio options if none are selected.

From MDN:

Elements targeted by this selector are:
...
<input type="radio"> elements, when all radio buttons with the same name value in the form are unchecked 1

Because of this, I lean towards something like Toggle indeterminate state because it will remove the radio selection and enable indeterminate for all radio options.

Updated in 2636dbe

Let me know if you prefer something else.

Mixin SASSDoc comments

Added SASSDoc comments for the new mixins. I also renamed them to use active instead of selected to keep it consistent with the param/sass variable that is used to set the colors.

Resolved in a399129

Checkbox styles SASSDoc comments

I updated the SASSDoc comment to focus on the class rather than the line numbers.

// Adding indeterminate styles here avoids style conflicts from setting in .usa-#{$input-type}__input due to
// radio buttons receiving :indeterminate state when none are selected.

I opted for this because my previous changes caused the desired line range to move and I realized any references to the line numbers could easily be made incorrect.

Resolved in c31e6f9

Question about radio regression

Does that mean moving the styles to 127-132 will cause similar regressions with checkbox? If so, we can just change radio → checkbox because it looks like one affects the other. 2

No, the regression is specifically found in radio buttons. It was caused due to unchecked radio groups receiving the indeterminate style by default. Therefore, we only want checkboxes to receive these styles to maintain the standard “unchecked” radio styles.

Footnotes

  1. MDN Docs

  2. Comment

@mahoneycm mahoneycm requested a review from mejiaj March 5, 2024 18:20
Copy link
Contributor

@mejiaj mejiaj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, tested new storybookjs control & checkbox/radio variants. No issues found.

In the future we should find/create realistic test cases for this feature.

Copy link
Contributor

@thisisdano thisisdano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.

@thisisdano thisisdano merged commit 58a6d92 into develop Mar 6, 2024
@thisisdano thisisdano deleted the checkbox-indeterminate-feature-branch branch March 6, 2024 20:49
@lpsinger
Copy link
Contributor

lpsinger commented Mar 7, 2024

Wonderful! I'm excited about this landing in main!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add indeterminate checkbox styling
5 participants