Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 677
Accessibility engineering review for Link component.#3317
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
66e640345899609f40db6d37e430a206550c1c1242c8bb5369d40e3fc518bb5df75238a90c33ee19891d38695a442e3ceb20288a40ad1a4007f7e8b2176bc98c262c3ed6256cec88a54fed6236e9988c5bf6b5e800cad18e21b94e97ba614d873d9e8de413b48e36e21a2b64441fcb98b40594d70476d01103d066e4e560aed48516ad63d53fc73f81e320d79684a5cdbfe69d1e1cc7e4aba402File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@primer/react": minor | ||
| --- | ||
| Link components no longer accept an `as` prop. If you need link-like styling, you should use a different component and style accordingly. | ||
| Button has a `link` variant which can be used for this purpose. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,10 +10,6 @@ import data from '../../src/Link/Link.docs.json' | ||
| The Link component styles anchor tags with default hyperlink color cues and hover text decoration. `Link` is used for destinations, or moving from one page to another. | ||
| In special cases where you'd like a `<button>` styled like a `Link`, use `<Link as='button'>`. Make sure to provide a click handler with `onClick`. | ||
| **Important:** When using the `as` prop, be sure to always render an accessible element type, like `a`, `button`, `input`, or `summary`. | ||
radglob marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ## Examples | ||
| ```jsx live | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -8,7 +8,7 @@ export const StyledButton = styled.button<SxProp>` | ||
| ${sx}; | ||
| ` | ||
| export type VariantType = 'default' | 'primary' | 'invisible' | 'danger' | 'outline' | ||
| export type VariantType = 'default' | 'primary' | 'invisible' | 'danger' | 'outline' | 'link' | ||
mperrotti marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| export type Size = 'small' | 'medium' | 'large' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import React, {forwardRef, useEffect} from 'react' | ||
| import React, {forwardRef} from 'react' | ||
| import styled from 'styled-components' | ||
| import {system} from 'styled-system' | ||
| import {get} from '../constants' | ||
| @@ -23,55 +23,27 @@ const hoverColor = system({ | ||
| const StyledLink = styled.a<StyledLinkProps>` | ||
| color: ${props => (props.muted ? get('colors.fg.muted')(props) : get('colors.accent.fg')(props))}; | ||
| text-decoration: ${props => (props.underline ? 'underline' : 'none')}; | ||
| &:hover { | ||
| &:hover, | ||
| &:focus { | ||
| text-decoration: ${props => (props.muted ? 'none' : 'underline')}; | ||
| ${props => (props.hoverColor ? hoverColor : props.muted ? `color: ${get('colors.accent.fg')(props)}` : '')}; | ||
| } | ||
| &:is(button) { | ||
| display: inline-block; | ||
| padding: 0; | ||
| font-size: inherit; | ||
| white-space: nowrap; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| background-color: transparent; | ||
| border: 0; | ||
| appearance: none; | ||
| } | ||
| ${sx}; | ||
| ` | ||
| const Link = forwardRef(({as: Component = 'a', ...props}, forwardedRef) => { | ||
| const Link = forwardRef(({as, ...props}, forwardedRef) => { | ||
radglob marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| const innerRef = React.useRef<HTMLAnchorElement>(null) | ||
| useRefObjectAsForwardedRef(forwardedRef, innerRef) | ||
| if (__DEV__) { | ||
| /** | ||
| * The Linter yells because it thinks this conditionally calls an effect, | ||
| * but since this is a compile-time flag and not a runtime conditional | ||
| * this is safe, and ensures the entire effect is kept out of prod builds | ||
| * shaving precious bytes from the output, and avoiding mounting a noop effect | ||
| */ | ||
| // eslint-disable-next-line react-hooks/rules-of-hooks | ||
| useEffect(() => { | ||
| if ( | ||
| innerRef.current && | ||
| !(innerRef.current instanceof HTMLButtonElement) && | ||
| !(innerRef.current instanceof HTMLAnchorElement) | ||
| ) { | ||
| // eslint-disable-next-line no-console | ||
| console.error( | ||
| 'Error: Found `Link` component that renders an inaccessible element', | ||
| innerRef.current, | ||
| 'Please ensure `Link` always renders as <a> or <button>', | ||
| ) | ||
| } | ||
| }, [innerRef]) | ||
| if (as !== undefined) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| 'Links no longer accept an as prop. If you need to style another tag as a link, you should use a different component and apply appropriate styling.', | ||
| ) | ||
| } | ||
| return ( | ||
| <StyledLink | ||
| as={Component} | ||
| {...props} | ||
| // @ts-ignore shh | ||
| ref={innerRef} | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.