UI: add LinkButton#78944
Conversation
|
Size Change: +687 B (+0.01%) Total Size: 8.21 MB 📦 View Changed
|
|
Flaky tests detected in 45696b0. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/26951289449
|
45696b0 to
92c1cd0
Compare
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Users can now use icons by using <LinkButton href="#">
<Button.Icon icon={ <svg /> } />
Link
</LinkButton>
Edit: there's now |
|
Should this support |
|
I could potentially see this being used in the Site Editor sidebar which makes me wonder if it should support a 'selected' state, or would that be a property of a higher-level menu component? |
This reverts commit 6c12505.
f5b5d76 to
6aa67da
Compare
Possibly; just not sure how we should go about the icon in that case (since buttons can have icons by other methods), so I left it for a separate PR to get this in sooner as-is without the support for now. |
Easy enough to add later when it is used there, rather than hypothetically ahead of time. :-) |
|
| * A link that looks like a `Button`. Prefer `Link` for navigation unless | ||
| * button prominence is intentional. |
There was a problem hiding this comment.
Could be a bit more generic and less forward about Link here. 😅
ciampo
left a comment
There was a problem hiding this comment.
Thank you for working on this!
| Choose the component based on **what happens when the user activates it**, not | ||
| only on how it should look. |
There was a problem hiding this comment.
Nit: I don't think we normally break the line in markdown files for formatting reasons?
| Choose the component based on **what happens when the user activates it**, not | |
| only on how it should look. | |
| Choose the component based on **what happens when the user activates it**, not only on how it should look. |
Here and everywhere else applicable in the PR
| <Markdown>{` | ||
| | Goal | Component | Element | Examples | | ||
| | --- | --- | --- | --- | | ||
| | Change something on the current page | [Button](?path=/docs/design-system-components-button--docs) | \`<button>\` | Save, Delete, Open dialog, Toggle | |
There was a problem hiding this comment.
| | Change something on the current page | [Button](?path=/docs/design-system-components-button--docs) | \`<button>\` | Save, Delete, Open dialog, Toggle | | |
| | Performs an action on the current page. | [Button](?path=/docs/design-system-components-button--docs) | \`<button>\` | Save, Delete, Submit, Open dialog, Toggle | |
| @@ -0,0 +1,18 @@ | |||
| @layer wp-ui-utilities, wp-ui-components, wp-ui-compositions, wp-ui-overrides; | |||
|
|
|||
| @layer wp-ui-components { | |||
There was a problem hiding this comment.
Not sure if we should use the compositions layer here, since we technically want to make sure these styles win over Button styles. Also in relationship to #78953
| .link-button, | ||
| .link-button:is(:hover, :focus, :active, :visited) { | ||
| outline: var(--_gcd-link-button-outline, 0 solid transparent); | ||
| color: var(--_gcd-link-button-color, inherit); | ||
| box-shadow: var(--_gcd-link-button-box-shadow, none); | ||
| border-radius: var(--_gcd-link-button-border-radius, 0); | ||
| transition: var(--_gcd-link-button-transition, none); | ||
| text-decoration: var(--_gcd-link-button-text-decoration, none); | ||
| } |
There was a problem hiding this comment.
Some of these new gdc variables are not being set, causing the focus ring is currently invisible, and the Button's border radius is not applied.
I played with these variables locally, and this seems to work well
diff --git i/packages/ui/src/link-button/link-button.tsx w/packages/ui/src/link-button/link-button.tsx
index e68ed654e49..97381ea931e 100644
--- i/packages/ui/src/link-button/link-button.tsx
+++ w/packages/ui/src/link-button/link-button.tsx
@@ -29,10 +29,10 @@ export const LinkButton = forwardRef< HTMLAnchorElement, LinkButtonProps >(
) {
const mergedClassName = clsx(
defenseStyles[ 'link-button' ],
- styles[ 'link-button' ],
resetStyles[ 'box-sizing' ],
focusStyles[ 'outset-ring--focus-except-active' ],
variant !== 'unstyled' && buttonStyles.button,
+ variant !== 'unstyled' && styles[ 'link-button' ],
buttonStyles[ `is-${ tone }` ],
buttonStyles[ `is-${ variant }` ],
buttonStyles[ `is-${ size }` ],
diff --git i/packages/ui/src/link-button/style.module.css w/packages/ui/src/link-button/style.module.css
index 3ed7f450998..af7b72025c1 100644
--- i/packages/ui/src/link-button/style.module.css
+++ w/packages/ui/src/link-button/style.module.css
@@ -6,6 +6,15 @@
global CSS defense. */
--_gcd-link-button-color: var(--wp-ui-button-foreground-color);
--_gcd-link-button-text-decoration: none;
+ --_gcd-link-button-border-radius: var(--wpds-border-radius-sm);
+
+ /* Extend the focus utility's outline transition (set in
+ `focus.module.css`) with a `color` transition, so styled variants fade
+ their foreground like `Button`. The `unstyled` variant omits this class
+ and falls back to the utility's outline-only transition. */
+ @media not ( prefers-reduced-motion ) {
+ --_gcd-link-button-transition: color 0.1s ease-out, outline 0.1s ease-out;
+ }
&:visited {
--_gcd-link-button-color: var(--wp-ui-button-foreground-color);
diff --git i/packages/ui/src/utils/css/focus.module.css w/packages/ui/src/utils/css/focus.module.css
index b8834f98bbd..57d8bd2bd0b 100644
--- i/packages/ui/src/utils/css/focus.module.css
+++ w/packages/ui/src/utils/css/focus.module.css
@@ -10,6 +10,7 @@
.outset-ring--focus-parent-visible {
@media not ( prefers-reduced-motion ) {
--_gcd-a-transition: outline 0.1s ease-out;
+ --_gcd-link-button-transition: outline 0.1s ease-out;
transition: outline 0.1s ease-out;
}
@@ -29,6 +30,7 @@
:focus-visible .outset-ring--focus-parent-visible {
--_gcd-a-outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
--_gcd-div-outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
+ --_gcd-link-button-outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
outline: var(--wpds-border-width-focus) solid var(--wpds-color-stroke-focus-brand);
}
|
|
||
| export type { LinkButtonProps, LinkButtonIconProps } from './types'; | ||
|
|
||
| ButtonIcon.displayName = 'LinkButton.Icon'; |
There was a problem hiding this comment.
This line is actually overriding the displayName set originally on the component (ie. Button.Icon).
We should probably create a separate, thin wrapper component.
| | --- | --- | --- | --- | | ||
| | Change something on the current page | [Button](?path=/docs/design-system-components-button--docs) | \`<button>\` | Save, Delete, Open dialog, Toggle | | ||
| | Navigate to another page or URL | [Link](?path=/docs/design-system-components-link--docs) | \`<a>\` | "Learn more", docs links, external references | | ||
| | Navigate with button styling | [LinkButton](?path=/docs/design-system-components-link-button--docs) | \`<a>\` | Standalone CTAs, card actions | |
There was a problem hiding this comment.
The URL generated by Storybook doesn't have the hyphen
| | Navigate with button styling | [LinkButton](?path=/docs/design-system-components-link-button--docs) | \`<a>\` | Standalone CTAs, card actions | | |
| | Navigate with button styling | [LinkButton](?path=/docs/design-system-components-linkbutton--docs) | \`<a>\` | Standalone CTAs, card actions | |
| > control. Reach for `LinkButton` only when you have considered `Button` and | ||
| > `Link` and still need button prominence. | ||
|
|
||
| See [LinkButton](?path=/docs/design-system-components-link-button--docs) for API |
There was a problem hiding this comment.
The URL generated by Storybook doesn't have the hyphen
| See [LinkButton](?path=/docs/design-system-components-link-button--docs) for API | |
| See [LinkButton](?path=/docs/design-system-components-linkbutton--docs) for API |
mirka
left a comment
There was a problem hiding this comment.
Sorry I didn't have time to run the code or ask my questions to an agent first, but wrote down my initial thoughts 😄 Thanks for working on this!
| componentStatus: { | ||
| status: 'use-with-caution', | ||
| whereUsed: 'global', | ||
| notes: 'Not yet recommended for use alongside components from `@wordpress/components`, pending review of style consistency with `@wordpress/components` and text overflow behavior. See [WordPress/gutenberg#76135](https://github.com/WordPress/gutenberg/issues/76135).', |
| docs: { | ||
| description: { | ||
| component: | ||
| 'See [Usage Guidelines](?path=/docs/design-system-components-button-usage-guidelines--docs) for when to use `Button`, `Link`, or `LinkButton`.', | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Ideally, this kind of description should be consolidated with the main component's JSDoc description so it's reusable everywhere including IntelliSense, not just Storybook.
| className | ||
| ); | ||
|
|
||
| const element = useRender( { |
There was a problem hiding this comment.
Curious, did we consider composing from the Link component, rather than building from scratch? I see there's even an unstyled variant for this kind of purpose. Not sure if that's a good idea, but good to think about the pros/cons nonetheless.
| * A styled anchor element with support for semantic color tones and an | ||
| * unstyled escape hatch. | ||
| * | ||
| * @see {@link https://wordpress.github.io/gutenberg/?path=/docs/design-system-components-button-usage-guidelines--docs When to use Button, Link, or LinkButton} |
There was a problem hiding this comment.
Related to my previous comment about consolidating component descriptions, and not sure if this still holds true in the latest version of Storybook, but @ tags in JSDoc tend to be dropped the docgen, so we've been preferring to write everything without these tags.
| /* Anchor elements styled as buttons (`LinkButton`). Separate from `.a` because | ||
| button foreground colors differ from link colors. */ | ||
| .link-button, | ||
| .link-button:is(:hover, :focus, :active, :visited) { |
There was a problem hiding this comment.
The component has its own defence styles, instead of re-using
adefence. Otherwise, we'd see incorrect text colors;
Not sure I understand why though? We can't just set --_gcd-a-color to the correct color in the component stylesheet?
Ideally this defense module stylesheet knows nothing about our specific components, only the raw elements/selectors that we're defending against.
What?
Resolves #77098
LinkButtoncomponent to UI package, following most of the visual features ofButton.Button,Link, orLinkButton.LinkButton.Iconcomponent, which is just re-export ofButtonIconDoes not support:
openInNewTablikeLink(good for a follow-up)nativeButton,loadingAnnouncement,loading,focusableWhenDisabled,disabledandaria-pressed).Link button behaves visually like
Button, but acceptshrefand semantically behaves likeLink.The component has its own defence styles, instead of re-using

adefence. Otherwise, we'd see incorrect text colors;Why?
In principle, we'd prefer people use
Linkfor actual links, andButtonfor non-links. In practise, these expectations are blurred lines for both designers and users, and sometimes even React apps (including Gutenberg) have internal, in-app flows which require usinghrefinstead ofonClick.In the guidance added in this PR we're still encouraging people to choose
ButtonorLinkoverLinkButton, and explain user expectations.How?
Testing Instructions
/?path=/docs/design-system-components-button-usage-guidelines--docs/?path=/docs/design-system-components-linkbutton--docsTesting Instructions for Keyboard
Use of AI Tools