diff --git a/packages/react/src/Button/ButtonBase.module.css b/packages/react/src/Button/ButtonBase.module.css index 7af731290da..79ffab05af8 100644 --- a/packages/react/src/Button/ButtonBase.module.css +++ b/packages/react/src/Button/ButtonBase.module.css @@ -24,8 +24,7 @@ justify-content: space-between; gap: var(--base-size-8); - /* NOTE: Uses descendant :has() - button has very few children (icon, text, kbd). Acceptable. */ - &:has([data-kbd-chord]) { + &[data-has-kbd] { padding-inline-end: var(--base-size-6); } @@ -174,8 +173,7 @@ margin-right: var(--control-large-gap); } - /* NOTE: Uses descendant :has() - button has very few children (icon, text, kbd). Acceptable. */ - &:has([data-kbd-chord]) { + &[data-has-kbd] { padding-inline-end: var(--base-size-8); } } @@ -637,11 +635,8 @@ /* * Icon-only + Counter - * NOTE: Uses descendant :has() - leadingVisual and text are nested inside - * buttonContent wrapper. This is acceptable as the search is scoped to - * this single button element's subtree (small DOM). */ - &:where([data-has-count]):has([data-component='leadingVisual']):not(:has([data-component='text'])) { + &:where([data-has-count][data-has-leading-visual]:not([data-has-text])) { /* stylelint-disable-next-line primer/spacing */ padding-inline: var(--control-medium-paddingInline-condensed); diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index d2ae5a0b6ba..ec709328470 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -59,6 +59,18 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f // Only include the loading aria-describedby if there is a loading state const ariaDescribedByIds = loading ? [loadingAnnouncementID, ariaDescribedBy] : [ariaDescribedBy] + // Check if button contains a keyboard shortcut hint and add data attribute for styling + React.useLayoutEffect(() => { + if (innerRef.current) { + const hasKbd = innerRef.current.querySelector('[data-kbd-chord]') !== null + if (hasKbd) { + innerRef.current.setAttribute('data-has-kbd', 'true') + } else { + innerRef.current.removeAttribute('data-has-kbd') + } + } + }) + if (__DEV__) { /** * The Linter yells because it thinks this conditionally calls an effect, @@ -99,6 +111,9 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f data-inactive={inactive ? true : undefined} data-loading={Boolean(loading)} data-no-visuals={!LeadingVisual && !TrailingVisual && !TrailingAction ? true : undefined} + data-has-visuals={LeadingVisual || TrailingVisual ? true : undefined} + data-has-leading-visual={LeadingVisual ? true : undefined} + data-has-text={children ? true : undefined} data-size={size} data-variant={variant} data-label-wrap={labelWrap} diff --git a/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap b/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap index 0e24734daa4..a54e95de758 100644 --- a/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap +++ b/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap @@ -4,6 +4,7 @@ exports[`Button > respects block prop 1`] = `