Add CartButton, a toggle that puts an item in the cart - #48
Merged
Conversation
A 30px pill wearing Button's outline variant, level with NavIcon and the sm buttons, holding PlusCircle at rest and CheckCircle once the item is in. Base UI's Toggle owns the pressed state, controlled through `pressed` or not through `defaultPressed`, and its aria-pressed and keyboard handling; the name stays "Add to cart" and the state says whether it is in. On hover or focus the words appear beside the plus in a Base UI Tooltip whose positioner is offset by the pill's own width, so the popup is the pill drawn again over the trigger and extended to the right. Nothing in the row changes width. Base UI flips it to the left when there is no room, and `data-side` reverses the order so the glyph stays over the trigger. The popup is aria-hidden and forwards its click to the trigger. `labelled` keeps the words in the pill for a prominent placement and disables the tooltip. Exported from the package, shown in Actions, and in components.css as kb-cart-button--pill on top of kb-button--btn and kb-button--outline, which precede it in the sheet and in the bundle, so the pill's radius wins at equal specificity. State is Base UI's data-pressed, per the class-name rule.
Button's `.btn svg { display: block }` outranked the bare `.check` rule, so
the unpressed pill showed both glyphs; the glyph rules now anchor to `.pill`.
Button's `[data-pressed]` press animation is the Toggle's on state here, so
the in-cart pill stayed at 97%; the pill resets the transform.
Colours: primary ink and the primary tint border while the action is open,
neutral ink and border once the item is in. The popup takes `data-pressed`
from React to follow, since it is portaled.
Comments, prop docs, and the showcase paragraphs reworded. The claim that
the name never changes held only for the icon form, and the forwardRef
reason given was popup positioning, which the Trigger's own ref handles;
Button's render-element reason is the one that applies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it is
A 30px pill wearing Button's
outlinevariant, level with NavIcon and the sm buttons, holdingPlusCircleat rest andCheckCircleonce the item is in. It composes Button's classes rather than wrapping the component, the way Field composes Input, so a nestedrenderdoes not runuseButtontwice.pressed/defaultPressed/onPressedChange,aria-pressed, keyboard. The icon form keeps one name, "Add to cart", andaria-pressedcarries the state; the labelled form is named by its visible words, which change with the state.--ct-primary) and the primary tint border (--bo-primary) while the action is open; neutral ink (--c-ink3) and border (--c-border2) once the item is in. The ground is outline's: transparent at rest, raised on hover.--ct-primaryclears WCAG AA on every ground in both themes.[data-side='left']reverses the order so the glyph stays over the trigger. The popup isaria-hiddenand forwards its click to the trigger;data-instant="focus"redraws the focus ring on it, since it covers the trigger's.labelled: words stay in the pill for a prominent placement; the tooltip is disabled.Two Button rules the pill has to override
.btn svg { display: block }outranks a bare.check { display: none }, so the unpressed pill showed both glyphs. The glyph rules anchor to.pill..btn[data-pressed] { transform: scale(0.97) }is Button's press animation. On a Toggle,data-pressedis the on state, so the in-cart pill sat at 97%. The pill resets the transform. No Button emitsdata-pressed, so that half of Button's selector could go in a follow-up.Where it lands
import { CartButton } from '@kbase/design-system'components.css)kb-button--btn kb-button--outline kb-cart-button--pillon a<button>,data-pressedfor the state,data-labelledfor the words.kb-button--*precede it in the sheet and in the bundle, so the pill's rules win at equal specificity. The popup is React-only.Checks
typecheck,lint,vitest(189 passed),build:design-system,gen_portal_css.pywith the repo's Sass — all green locally at 9cf7207. The Python wheel job was not run here.One note for review
The mirror of
pressedinuseStateexists only because the popup is portaled and cannot read the trigger'sdata-pressed. The Toggle is still the source of truth for the trigger; the mirror feeds the popup's glyph, words, and its owndata-pressed.