Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WB-1808: Button - Use CSS pseudo-classes for styling states (hover, focus, etc) #2404

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

jandrade
Copy link
Member

@jandrade jandrade commented Dec 19, 2024

Summary:

Changing the way we style different states. We are going to rely on CSS
pseudo-classes for styling states (:hover, :focus-visible).

Note that we have to keep some ClickableBehavior states for programmatic focus
and preserve active/pressed overrides.

Also took the opportunity to modify the primary variant to use outline + outlineOffset instead of boxShadow.

Issue: https://khanacademy.atlassian.net/browse/WB-1808

Test plan:

Navigate to /?path=/docs/packages-button--docs and verify that the buttons are
styled correctly.

Screenshot 2024-12-19 at 5 28 22 PM

…s-visible). Keep some clickable states for programmatic focus and preserve active/pressed overrides.
@jandrade jandrade self-assigned this Dec 19, 2024
Copy link

changeset-bot bot commented Dec 19, 2024

🦋 Changeset detected

Latest commit: 3aee270

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@khanacademy/wonder-blocks-button Patch
@khanacademy/wonder-blocks-banner Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

github-actions bot commented Dec 19, 2024

Size Change: +69 B (+0.07%)

Total Size: 96.4 kB

Filename Size Change
packages/wonder-blocks-button/dist/es/index.js 4.11 kB +69 B (+1.71%)
ℹ️ View Unchanged
Filename Size
packages/wonder-blocks-accordion/dist/es/index.js 3.77 kB
packages/wonder-blocks-banner/dist/es/index.js 1.53 kB
packages/wonder-blocks-birthday-picker/dist/es/index.js 1.77 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 887 B
packages/wonder-blocks-cell/dist/es/index.js 2.01 kB
packages/wonder-blocks-clickable/dist/es/index.js 3.06 kB
packages/wonder-blocks-core/dist/es/index.js 2.88 kB
packages/wonder-blocks-data/dist/es/index.js 6.24 kB
packages/wonder-blocks-dropdown/dist/es/index.js 19.1 kB
packages/wonder-blocks-form/dist/es/index.js 6.2 kB
packages/wonder-blocks-grid/dist/es/index.js 1.36 kB
packages/wonder-blocks-icon-button/dist/es/index.js 2.95 kB
packages/wonder-blocks-icon/dist/es/index.js 871 B
packages/wonder-blocks-labeled-field/dist/es/index.js 72 B
packages/wonder-blocks-layout/dist/es/index.js 1.82 kB
packages/wonder-blocks-link/dist/es/index.js 2.28 kB
packages/wonder-blocks-modal/dist/es/index.js 5.42 kB
packages/wonder-blocks-pill/dist/es/index.js 1.65 kB
packages/wonder-blocks-popover/dist/es/index.js 4.85 kB
packages/wonder-blocks-progress-spinner/dist/es/index.js 1.52 kB
packages/wonder-blocks-search-field/dist/es/index.js 1.36 kB
packages/wonder-blocks-switch/dist/es/index.js 1.92 kB
packages/wonder-blocks-testing-core/dist/es/index.js 3.74 kB
packages/wonder-blocks-testing/dist/es/index.js 1.07 kB
packages/wonder-blocks-theming/dist/es/index.js 693 B
packages/wonder-blocks-timing/dist/es/index.js 1.8 kB
packages/wonder-blocks-tokens/dist/es/index.js 2.36 kB
packages/wonder-blocks-toolbar/dist/es/index.js 905 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.99 kB
packages/wonder-blocks-typography/dist/es/index.js 1.23 kB

compressed-size-action

Copy link

codecov bot commented Dec 19, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 0.00%. Comparing base (e095558) to head (ff165fa).

Additional details and impacted files

Impacted file tree graph

@@     Coverage Diff      @@
##   main   #2404   +/-   ##
============================
============================

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e095558...ff165fa. Read the comment docs.

Copy link
Contributor

github-actions bot commented Dec 19, 2024

A new build was pushed to Chromatic! 🚀

https://5e1bf4b385e3fb0020b7073c-ckrjpvvmqt.chromatic.com/

Chromatic results:

Metric Total
Captured snapshots 372
Tests with visual changes 3
Total stories 511
Inherited (not captured) snapshots [TurboSnap] 0
Tests on the build 372

@@ -27,26 +26,6 @@ import ComponentInfo from "../../.storybook/components/component-info";
import ButtonArgTypes from "./button.argtypes";
import {ThemeSwitcherContext} from "@khanacademy/wonder-blocks-theming";

/**
Copy link
Member Author

Choose a reason for hiding this comment

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

note: Got rid of this b/c it's duplicated. The source of truth is the code in button.tsx.

Comment on lines +138 to +142
chromatic: {
// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
Copy link
Member Author

Choose a reason for hiding this comment

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

note: I'm disabling a bunch of snapshots now that we can centralize visual regression tests in the All variants stories.

Copy link
Member Author

Choose a reason for hiding this comment

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

note: Removed this to now use Chromatic snapshots instead of unit tests.

Comment on lines +87 to +88
? buttonStyles.pressed
: focused && buttonStyles.focused),
Copy link
Member Author

Choose a reason for hiding this comment

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

note: Changed the style names to match what's returned from clickable-behavior.

Comment on lines -381 to -383
boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
light ? theme.color.bg.primary.default : color
}`,
Copy link
Member Author

Choose a reason for hiding this comment

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

note: Took the opportunity to switch from boxShadow to outline. This could help solving this ticket: https://khanacademy.atlassian.net/browse/WB-1712

Comment on lines +500 to +502
textUnderlineOffset: theme.font.offset.default,
textDecoration: "underline",
textDecorationThickness: theme.size.underline.hover,
Copy link
Member Author

Choose a reason for hiding this comment

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

note: Also simplified the tertiary hover styles to rely on underlines instead of using the :after hack.

@jandrade jandrade marked this pull request as ready for review December 19, 2024 22:28
@khan-actions-bot
Copy link
Contributor

Gerald

Required Reviewers
  • @Khan/wonder-blocks for changes to .changeset/rich-days-count.md, __docs__/wonder-blocks-button/button-variants.stories.tsx, __docs__/wonder-blocks-button/button.stories.tsx, packages/wonder-blocks-button/src/components/button-core.tsx, packages/wonder-blocks-button/src/themes/default.ts

Don't want to be involved in this pull request? Comment #removeme and we won't notify you of further changes.

@khan-actions-bot khan-actions-bot requested a review from a team December 19, 2024 22:28
Copy link
Contributor

github-actions bot commented Dec 19, 2024

npm Snapshot: Published

🎉 Good news!! We've packaged up the latest commit from this PR (99be186) and published all packages with changesets to npm.

You can install the packages in webapp by running:

./services/static/dev/tools/deploy_wonder_blocks.js --tag="PR2404"

Packages can also be installed manually by running:

yarn add @khanacademy/wonder-blocks-<package-name>@PR2404

Copy link
Member

@beaesguerra beaesguerra left a comment

Choose a reason for hiding this comment

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

Yay more sticker sheets! 🎉 It looks great, I left some comments/questions! I wanted to see what your thoughts were on removing things from the theme before approving

// We already have screenshots of other stories that cover more of
// the button states
disableSnapshot: true,
},
};

export const Spinner: StoryComponentType = () => (
Copy link
Member

Choose a reason for hiding this comment

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

Would it be helpful to include the spinner state in All Variants as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought about that one, but I decided to keep it separately as it could introduce flakyness to that snapshot. I'm mentioning that because this variant includes animation and I've seen that Chromatic sometimes takes screenshots at different times.

That said, maybe we could add it and see how it goes? wdyt?

Copy link
Member

Choose a reason for hiding this comment

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

Good point! I was thinking it could be helpful to see it across the different themes/states/variants etc, though I understand snapshots for animated things can be flaky! I'm okay leaving it as a separate story!

@@ -25,8 +30,6 @@ const theme = {
primary: {
default: tokens.color.white,
disabled: tokens.color.offBlack32,
// used in boxShadow
inverse: tokens.color.darkBlue,
Copy link
Member

Choose a reason for hiding this comment

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

question: When we remove things from a theme, is it considered a breaking change? It seems like it isn't since we don't export the theme from the package explicitly, though thought I'd double check!

I was also wondering if we needed to remove this from other themes, but I see now that the khanmigo theme doesn't have these set since we merge the default theme with the khanmigo theme!

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question, and you are right. Right now, as theming works, this is only available internally, so it wouldn't be a breaking change.

The other themes are built based on the shape of the default object, and that means that if we remove a key that is used in a merged theme (e.g. khanmigo), then TS would complain about it :). I created that merge theme architecture having that in mind, so it would help us to have a more robust theming approach.

Copy link
Member

Choose a reason for hiding this comment

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

Nice! Good to know this about our theming!

Comment on lines 373 to 375
outlineOffset: 2,
outlineStyle: "solid",
outlineWidth: 2,
Copy link
Member

Choose a reason for hiding this comment

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

(For my own learning): When do we include values in a theme vs setting the style directly?

Copy link
Member Author

Choose a reason for hiding this comment

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

ohhhh thanks for being curious. This should be part of the theme as it could allow us in the future to configure these (e.g. in Polaris). I'm going to use a theme variable for this.

Copy link
Member Author

Choose a reason for hiding this comment

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

@beaesguerra I've updated the styles to use theme now.

Comment on lines +423 to +440
const focusStyling = {
background: light
? theme.color.bg.secondary.inverse
: theme.color.bg.secondary.focus,
borderColor: "transparent",
outlineColor: light ? theme.color.border.primary.inverse : color,
outlineStyle: "solid",
outlineWidth: theme.border.width.focused,
};

const activePressedStyling = {
background: light ? activeColor : secondaryActiveColor,
color: light ? fadedColor : activeColor,
borderColor: "transparent",
outlineColor: light ? fadedColor : activeColor,
outlineStyle: "solid",
outlineWidth: theme.border.width.focused,
};
Copy link
Member

Choose a reason for hiding this comment

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

It'll be so nice when we can share state styling across different components 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Yess!!! 1000% to that 🙏

Comment on lines +457 to +460
[":hover:not([aria-disabled=true])" as any]: focusStyling,
[":focus-visible:not([aria-disabled=true])" as any]:
focusStyling,
[":active:not([aria-disabled=true])" as any]:
Copy link
Member

Choose a reason for hiding this comment

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

Is there something we can do to prevent having as any?

I was trying to see if I ran into similar things when working on the SelectOpener and it looks like we don't type cast it explicitly because we set newStyles in SelectOpener to Record<String, any> instead of Record<String, CSSProperties> 😅

":hover:not([aria-disabled=true])": {
borderColor: error
? tokens.color.red
: tokens.color.white50,
borderWidth: tokens.border.width.hairline,
paddingLeft: tokens.spacing.medium_16,
paddingRight: tokens.spacing.small_12,
},
},
":focus-visible:not([aria-disabled=true])": focusHoverStyling,
":active:not([aria-disabled=true])": activePressedStyling,

Copy link
Member Author

Choose a reason for hiding this comment

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

ugh yeah that's very annoying tbh. This is one of the things I'd hope Aphrodite would have better support for. We could add types, but it's hard to calculate all the possible combinations/variants for these selectors.

I guess this is a good/solid point for switching to CSS selectors (aka css-modules). Well, you know that I worry about opening the door to specificity, but that's something that we'll have to evaluate when we make the definitive decision to switch our styling approach.

Copy link
Member Author

Copy link
Member

Choose a reason for hiding this comment

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

Oooh cool! That TS example looks promising! Looks like the ordering would matter. For example, :not([aria-disabled]):hover wouldn't meet the type requirements like :hover:not([aria-disabled])

Copy link
Member

Choose a reason for hiding this comment

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

🎉 Thanks for removing snapshot tests now that we have Chromatic coverage!

@khan-actions-bot khan-actions-bot requested a review from a team December 20, 2024 20:42
Copy link
Member

@beaesguerra beaesguerra left a comment

Choose a reason for hiding this comment

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

Looks great! 🚀

@marcysutton
Copy link
Member

Tested in Chrome, Safari, and Firefox on Mac, and Chrome and Firefox on Windows. The interaction states work great! Nice work @jandrade!

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.

4 participants