@kaizen/components 1.72.0 → 1.73.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/bin/codemod.sh +2 -0
  2. package/codemods/README.md +46 -0
  3. package/codemods/upgradeV1Buttons/index.ts +19 -0
  4. package/codemods/upgradeV1Buttons/transformV1ButtonAttributes.spec.ts +202 -0
  5. package/codemods/upgradeV1Buttons/transformV1ButtonAttributes.ts +146 -0
  6. package/codemods/upgradeV1Buttons/upgradeV1Buttons.spec.ts +658 -0
  7. package/codemods/upgradeV1Buttons/upgradeV1Buttons.ts +93 -0
  8. package/codemods/utils/createJsxElementWithChildren.spec.ts +119 -0
  9. package/codemods/utils/createJsxElementWithChildren.ts +55 -0
  10. package/codemods/utils/createProp.spec.ts +75 -19
  11. package/codemods/utils/createProp.ts +8 -1
  12. package/codemods/utils/getKaioTagName.ts +13 -5
  13. package/codemods/utils/index.ts +1 -0
  14. package/dist/cjs/Link/Link.cjs +45 -0
  15. package/dist/cjs/Link/Link.module.css.cjs +20 -0
  16. package/dist/cjs/Link/subcomponents/LinkContent.cjs +34 -0
  17. package/dist/cjs/index.cjs +2 -0
  18. package/dist/cjs/utilitiesV3.cjs +2 -0
  19. package/dist/esm/Link/Link.mjs +40 -0
  20. package/dist/esm/Link/Link.module.css.mjs +18 -0
  21. package/dist/esm/Link/subcomponents/LinkContent.mjs +26 -0
  22. package/dist/esm/index.mjs +1 -0
  23. package/dist/esm/utilitiesV3.mjs +1 -0
  24. package/dist/styles.css +120 -0
  25. package/dist/types/Link/Link.d.ts +39 -0
  26. package/dist/types/Link/index.d.ts +1 -0
  27. package/dist/types/Link/subcomponents/LinkContent.d.ts +8 -0
  28. package/dist/types/index.d.ts +1 -0
  29. package/package.json +3 -3
  30. package/src/Link/Link.module.css +119 -0
  31. package/src/Link/Link.tsx +90 -0
  32. package/src/Link/_docs/Link--api-specification.mdx +133 -0
  33. package/src/Link/_docs/Link--api-usage-guidelines.mdx +107 -0
  34. package/src/Link/_docs/Link.doc.stories.tsx +238 -0
  35. package/src/Link/_docs/Link.stickersheet.stories.tsx +191 -0
  36. package/src/Link/index.ts +1 -0
  37. package/src/Link/subcomponents/LinkContent.tsx +31 -0
  38. package/src/LinkButton/_docs/LinkButton--api-specification.mdx +1 -57
  39. package/src/__next__/Button/_docs/Button--migration-guide.mdx +81 -0
  40. package/src/index.ts +1 -0
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { mergeClassNames } from '../../utils/mergeClassNames.mjs';
3
+ import styles from '../Link.module.css.mjs';
4
+ var LinkIcon = function (_a) {
5
+ var icon = _a.icon;
6
+ return /*#__PURE__*/React.createElement("span", {
7
+ className: styles.icon
8
+ }, icon);
9
+ };
10
+ var LinkContent = function (_a) {
11
+ var children = _a.children,
12
+ icon = _a.icon,
13
+ iconPosition = _a.iconPosition,
14
+ isUnderlined = _a.isUnderlined;
15
+ var iconPositionStyling = iconPosition === 'start' ? styles.iconStart : styles.iconEnd;
16
+ return /*#__PURE__*/React.createElement("span", {
17
+ className: mergeClassNames(styles.linkContent, isUnderlined && styles.isUnderlined)
18
+ }, icon && iconPosition === 'start' && /*#__PURE__*/React.createElement(LinkIcon, {
19
+ icon: icon
20
+ }), /*#__PURE__*/React.createElement("span", {
21
+ className: mergeClassNames(icon && iconPositionStyling)
22
+ }, children), icon && iconPosition === 'end' && /*#__PURE__*/React.createElement(LinkIcon, {
23
+ icon: icon
24
+ }));
25
+ };
26
+ export { LinkContent };
@@ -316,6 +316,7 @@ export { InputSearch } from './Input/InputSearch/InputSearch.mjs';
316
316
  export { KaizenProvider } from './KaizenProvider/KaizenProvider.mjs';
317
317
  export { Label } from './Label/Label.mjs';
318
318
  export { LabelledMessage } from './LabelledMessage/LabelledMessage.mjs';
319
+ export { Link } from './Link/Link.mjs';
319
320
  export { LikertScaleLegacy } from './LikertScaleLegacy/LikertScaleLegacy.mjs';
320
321
  export { LinkButton } from './LinkButton/LinkButton.mjs';
321
322
  export { LoadingGraphic } from './Loading/LoadingGraphic/LoadingGraphic.mjs';
@@ -316,6 +316,7 @@ export { InputSearch } from './Input/InputSearch/InputSearch.mjs';
316
316
  export { KaizenProvider } from './KaizenProvider/KaizenProvider.mjs';
317
317
  export { Label } from './Label/Label.mjs';
318
318
  export { LabelledMessage } from './LabelledMessage/LabelledMessage.mjs';
319
+ export { Link } from './Link/Link.mjs';
319
320
  export { LikertScaleLegacy } from './LikertScaleLegacy/LikertScaleLegacy.mjs';
320
321
  export { LinkButton } from './LinkButton/LinkButton.mjs';
321
322
  export { LoadingGraphic } from './Loading/LoadingGraphic/LoadingGraphic.mjs';
package/dist/styles.css CHANGED
@@ -5849,6 +5849,126 @@ input[type=range].InputRange-module_ratingScaleRange__gI-rs::-ms-thumb:not(:disa
5849
5849
  left: var(--spacing-md, 1.5rem);
5850
5850
  }
5851
5851
  }
5852
+ .Link-module_link__8oxip {
5853
+ color: var(--link-text-color, var(--color-blue-500));
5854
+ font-family: var(--typography-paragraph-body-font-family);
5855
+ font-size: var(--link-font-size, inherit);
5856
+ line-height: var(--link-line-height, inherit);
5857
+ font-weight: var(--typography-paragraph-body-font-weight);
5858
+ text-decoration: none;
5859
+ position: relative;
5860
+ white-space: nowrap;
5861
+ outline: 0;
5862
+ }
5863
+
5864
+ .Link-module_link__8oxip[data-focus-visible]::after {
5865
+ content: '';
5866
+ position: absolute;
5867
+ background: transparent;
5868
+ border-color: var(--color-blue-500);
5869
+ border-radius: 0;
5870
+ border-width: var(--border-focus-ring-border-width);
5871
+ border-style: var(--border-focus-ring-border-style);
5872
+ inset: calc(-1 * (var(--border-focus-ring-border-width) * 2) - 1px);
5873
+ }
5874
+
5875
+ .Link-module_isUnderlined__qvbtS {
5876
+ border-bottom: var(--spacing-1) solid var(--link-text-color, var(--color-blue-500));
5877
+ }
5878
+
5879
+ .Link-module_icon__CXh2J > * {
5880
+ vertical-align: text-bottom;
5881
+ font-size: var(--icon-font-size, 1em);
5882
+ }
5883
+
5884
+ .Link-module_iconStart__ZysGR {
5885
+ margin-inline-start: var(--text-icon-gap, 0.5em);
5886
+ }
5887
+
5888
+ .Link-module_iconEnd__-DTiD {
5889
+ margin-inline-end: var(--text-icon-gap, 0.5em);
5890
+ }
5891
+
5892
+ .Link-module_primary__bmhX0[data-hovered] {
5893
+ --link-text-color: var(--color-blue-600);
5894
+ }
5895
+
5896
+ .Link-module_primary__bmhX0[data-pressed] {
5897
+ --link-text-color: var(--color-blue-700);
5898
+ }
5899
+
5900
+ .Link-module_secondary__pyYIV {
5901
+ --link-text-color: var(--color-purple-800);
5902
+ }
5903
+
5904
+ .Link-module_secondary__pyYIV[data-hovered] {
5905
+ --link-text-color: var(--color-gray-600);
5906
+ }
5907
+
5908
+ .Link-module_secondary__pyYIV[data-pressed] {
5909
+ --link-text-color: var(--color-black);
5910
+ }
5911
+
5912
+ [class*='extra-small'] .Link-module_link__8oxip.Link-module_isInline__sznAm,
5913
+ .Link-module_extra-small__nScM0 {
5914
+ --link-font-size: var(--typography-paragraph-extra-small-font-size);
5915
+ --link-line-height: var(--typography-paragraph-extra-small-line-height);
5916
+ --icon-font-size: 0.875rem;
5917
+ --text-icon-gap: var(--spacing-2);
5918
+ }
5919
+
5920
+ [class*='body'] .Link-module_link__8oxip.Link-module_isInline__sznAm,
5921
+ .Link-module_body__H4Jgj {
5922
+ --link-font-size: var(--typography-paragraph-body-font-size);
5923
+ --link-line-height: var(--typography-paragraph-body-line-height);
5924
+ --icon-font-size: 1.25rem;
5925
+ --text-icon-gap: var(--spacing-6);
5926
+ }
5927
+
5928
+ [class*='intro-lede'] .Link-module_link__8oxip.Link-module_isInline__sznAm,
5929
+ .Link-module_intro-lede__38Y6J {
5930
+ --link-font-size: var(--typography-paragraph-intro-lede-font-size);
5931
+ --link-line-height: var(--typography-paragraph-intro-lede-line-height);
5932
+ --icon-font-size: 1.5rem;
5933
+ --text-icon-gap: var(--spacing-8);
5934
+ }
5935
+
5936
+ [class*='small']:not([class*='extra-small']) .Link-module_link__8oxip.Link-module_isInline__sznAm,
5937
+ .Link-module_small__4kJcR {
5938
+ --link-font-size: var(--typography-paragraph-small-font-size);
5939
+ --link-line-height: var(--typography-paragraph-small-line-height);
5940
+ --icon-font-size: 1rem;
5941
+ --text-icon-gap: var(--spacing-4);
5942
+ }
5943
+
5944
+ .Link-module_white__qQr6q {
5945
+ --link-text-color: var(--color-white);
5946
+ }
5947
+
5948
+ .Link-module_white__qQr6q[data-focus-visible]::after {
5949
+ border-color: var(--color-blue-300);
5950
+ }
5951
+
5952
+ .Link-module_white__qQr6q.Link-module_isDisabled__gvLNv {
5953
+ --link-text-color: rgba(var(--color-white-rgb), 0.2);
5954
+ }
5955
+
5956
+ .Link-module_reversed__IqX8B {
5957
+ --link-text-color: var(--color-white);
5958
+ }
5959
+
5960
+ .Link-module_reversed__IqX8B[data-focus-visible]::after {
5961
+ border-color: var(--color-blue-300);
5962
+ }
5963
+
5964
+ .Link-module_link__8oxip.Link-module_isDisabled__gvLNv {
5965
+ --link-text-color: var(--color-gray-400);
5966
+ }
5967
+
5968
+ .Link-module_reversed__IqX8B.Link-module_isDisabled__gvLNv {
5969
+ --link-text-color: rgba(var(--color-white-rgb), 0.2);
5970
+ }
5971
+
5852
5972
  /** THIS IS AN AUTOGENERATED FILE **/
5853
5973
  /** THIS IS AN AUTOGENERATED FILE **/
5854
5974
  /** THIS IS AN AUTOGENERATED FILE **/
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { type LinkProps as RACLinkProps } from 'react-aria-components';
3
+ import { type TextProps } from "../Text";
4
+ export type LinkProps = BaseLinkProps & ((UnderlinedLink | NonUnderlinedLink) & (InlineLink | NonInlineLink));
5
+ type BaseLinkProps = {
6
+ /** Controls the visual style of a link. @default 'primary' */
7
+ variant?: 'primary' | 'secondary' | 'white';
8
+ /** Controls the position of a link. @default 'end' */
9
+ iconPosition?: 'start' | 'end';
10
+ } & Omit<RACLinkProps, 'children'> & {
11
+ /** Used as the label for the Link. */
12
+ children: RACLinkProps['children'];
13
+ };
14
+ type UnderlinedLink = {
15
+ /** Toggles the underline of the icon and children @default true */
16
+ isUnderlined?: true;
17
+ /** The icon to be displayed, optional when link is underlined */
18
+ icon?: JSX.Element;
19
+ };
20
+ type NonUnderlinedLink = {
21
+ /** Toggles the underline of the icon and children */
22
+ isUnderlined?: false;
23
+ /** The icon to be displayed, required when link is not underlined */
24
+ icon: JSX.Element;
25
+ };
26
+ type InlineLink = {
27
+ /** isInline assumes the Link is wrapped in a [Text](https://cultureamp.design/?path=/docs/components-text--docs) component */
28
+ isInline: true;
29
+ /** The size of the link, not passed when isInline */
30
+ size?: never;
31
+ };
32
+ type NonInlineLink = {
33
+ /** isInline assumes the Link is wrapped in a [Text](https://cultureamp.design/?path=/docs/components-text--docs) component @default false */
34
+ isInline?: false;
35
+ /** The size of the link. Sizes correlate to body text sizes. @default 'body' */
36
+ size?: TextProps['variant'];
37
+ };
38
+ export declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
39
+ export {};
@@ -0,0 +1 @@
1
+ export * from './Link';
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ export type LinkContentProps = {
3
+ children: ReactNode;
4
+ icon?: JSX.Element;
5
+ iconPosition?: 'start' | 'end';
6
+ isUnderlined: boolean;
7
+ };
8
+ export declare const LinkContent: ({ children, icon, iconPosition, isUnderlined, }: LinkContentProps) => JSX.Element;
@@ -30,6 +30,7 @@ export * from './Input';
30
30
  export * from './KaizenProvider';
31
31
  export * from './Label';
32
32
  export * from './LabelledMessage';
33
+ export * from './Link';
33
34
  export * from './LikertScaleLegacy';
34
35
  export * from './LinkButton';
35
36
  export * from './Loading';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.72.0",
3
+ "version": "1.73.1",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -185,8 +185,8 @@
185
185
  "svgo": "^3.3.2",
186
186
  "tslib": "^2.8.1",
187
187
  "tsx": "^4.19.2",
188
- "@kaizen/package-bundler": "2.0.4",
189
- "@kaizen/design-tokens": "10.8.7"
188
+ "@kaizen/design-tokens": "10.8.7",
189
+ "@kaizen/package-bundler": "2.0.4"
190
190
  },
191
191
  "devDependenciesComments": {
192
192
  "sass": "Prevent deprecation warnings introduced in 1.80 as we plan to move away from sass",
@@ -0,0 +1,119 @@
1
+ .link {
2
+ color: var(--link-text-color, var(--color-blue-500));
3
+ font-family: var(--typography-paragraph-body-font-family);
4
+ font-size: var(--link-font-size, inherit);
5
+ line-height: var(--link-line-height, inherit);
6
+ font-weight: var(--typography-paragraph-body-font-weight);
7
+ text-decoration: none;
8
+ position: relative;
9
+ white-space: nowrap;
10
+ outline: 0;
11
+ }
12
+
13
+ .link[data-focus-visible]::after {
14
+ content: '';
15
+ position: absolute;
16
+ background: transparent;
17
+ border-color: var(--color-blue-500);
18
+ border-radius: 0;
19
+ border-width: var(--border-focus-ring-border-width);
20
+ border-style: var(--border-focus-ring-border-style);
21
+ inset: calc(-1 * (var(--border-focus-ring-border-width) * 2) - 1px);
22
+ }
23
+
24
+ .isUnderlined {
25
+ border-bottom: var(--spacing-1) solid var(--link-text-color, var(--color-blue-500));
26
+ }
27
+
28
+ .icon > * {
29
+ vertical-align: text-bottom;
30
+ font-size: var(--icon-font-size, 1em);
31
+ }
32
+
33
+ .iconStart {
34
+ margin-inline-start: var(--text-icon-gap, 0.5em);
35
+ }
36
+
37
+ .iconEnd {
38
+ margin-inline-end: var(--text-icon-gap, 0.5em);
39
+ }
40
+
41
+ .primary[data-hovered] {
42
+ --link-text-color: var(--color-blue-600);
43
+ }
44
+
45
+ .primary[data-pressed] {
46
+ --link-text-color: var(--color-blue-700);
47
+ }
48
+
49
+ .secondary {
50
+ --link-text-color: var(--color-purple-800);
51
+ }
52
+
53
+ .secondary[data-hovered] {
54
+ --link-text-color: var(--color-gray-600);
55
+ }
56
+
57
+ .secondary[data-pressed] {
58
+ --link-text-color: var(--color-black);
59
+ }
60
+
61
+ [class*='extra-small'] .link.isInline,
62
+ .extra-small {
63
+ --link-font-size: var(--typography-paragraph-extra-small-font-size);
64
+ --link-line-height: var(--typography-paragraph-extra-small-line-height);
65
+ --icon-font-size: 0.875rem;
66
+ --text-icon-gap: var(--spacing-2);
67
+ }
68
+
69
+ [class*='body'] .link.isInline,
70
+ .body {
71
+ --link-font-size: var(--typography-paragraph-body-font-size);
72
+ --link-line-height: var(--typography-paragraph-body-line-height);
73
+ --icon-font-size: 1.25rem;
74
+ --text-icon-gap: var(--spacing-6);
75
+ }
76
+
77
+ [class*='intro-lede'] .link.isInline,
78
+ .intro-lede {
79
+ --link-font-size: var(--typography-paragraph-intro-lede-font-size);
80
+ --link-line-height: var(--typography-paragraph-intro-lede-line-height);
81
+ --icon-font-size: 1.5rem;
82
+ --text-icon-gap: var(--spacing-8);
83
+ }
84
+
85
+ [class*='small']:not([class*='extra-small']) .link.isInline,
86
+ .small {
87
+ --link-font-size: var(--typography-paragraph-small-font-size);
88
+ --link-line-height: var(--typography-paragraph-small-line-height);
89
+ --icon-font-size: 1rem;
90
+ --text-icon-gap: var(--spacing-4);
91
+ }
92
+
93
+ .white {
94
+ --link-text-color: var(--color-white);
95
+ }
96
+
97
+ .white[data-focus-visible]::after {
98
+ border-color: var(--color-blue-300);
99
+ }
100
+
101
+ .white.isDisabled {
102
+ --link-text-color: rgba(var(--color-white-rgb), 0.2);
103
+ }
104
+
105
+ .reversed {
106
+ --link-text-color: var(--color-white);
107
+ }
108
+
109
+ .reversed[data-focus-visible]::after {
110
+ border-color: var(--color-blue-300);
111
+ }
112
+
113
+ .link.isDisabled {
114
+ --link-text-color: var(--color-gray-400);
115
+ }
116
+
117
+ .reversed.isDisabled {
118
+ --link-text-color: rgba(var(--color-white-rgb), 0.2);
119
+ }
@@ -0,0 +1,90 @@
1
+ import React, { forwardRef } from 'react'
2
+ import { Link as RACLink, type LinkProps as RACLinkProps } from 'react-aria-components'
3
+ import { type TextProps } from '~components/Text'
4
+ import { mergeClassNames } from '~components/utils/mergeClassNames'
5
+ import { LinkContent } from './subcomponents/LinkContent'
6
+ import styles from './Link.module.css'
7
+
8
+ export type LinkProps = BaseLinkProps &
9
+ ((UnderlinedLink | NonUnderlinedLink) & (InlineLink | NonInlineLink))
10
+
11
+ type BaseLinkProps = {
12
+ /** Controls the visual style of a link. @default 'primary' */
13
+ variant?: 'primary' | 'secondary' | 'white'
14
+ /** Controls the position of a link. @default 'end' */
15
+ iconPosition?: 'start' | 'end'
16
+ } & Omit<RACLinkProps, 'children'> & {
17
+ /** Used as the label for the Link. */
18
+ children: RACLinkProps['children']
19
+ }
20
+
21
+ type UnderlinedLink = {
22
+ /** Toggles the underline of the icon and children @default true */
23
+ isUnderlined?: true
24
+ /** The icon to be displayed, optional when link is underlined */
25
+ icon?: JSX.Element
26
+ }
27
+
28
+ type NonUnderlinedLink = {
29
+ /** Toggles the underline of the icon and children */
30
+ isUnderlined?: false
31
+ /** The icon to be displayed, required when link is not underlined */
32
+ icon: JSX.Element
33
+ }
34
+
35
+ type InlineLink = {
36
+ /** isInline assumes the Link is wrapped in a [Text](https://cultureamp.design/?path=/docs/components-text--docs) component */
37
+ isInline: true
38
+ /** The size of the link, not passed when isInline */
39
+ size?: never
40
+ }
41
+
42
+ type NonInlineLink = {
43
+ /** isInline assumes the Link is wrapped in a [Text](https://cultureamp.design/?path=/docs/components-text--docs) component @default false */
44
+ isInline?: false
45
+ /** The size of the link. Sizes correlate to body text sizes. @default 'body' */
46
+ size?: TextProps['variant']
47
+ }
48
+
49
+ export const Link = forwardRef(
50
+ (
51
+ {
52
+ children,
53
+ variant = 'primary',
54
+ size = 'body',
55
+ icon,
56
+ iconPosition = 'end',
57
+ isInline = false,
58
+ isDisabled,
59
+ className,
60
+ isUnderlined = true,
61
+ ...otherProps
62
+ }: LinkProps,
63
+ ref: React.ForwardedRef<HTMLAnchorElement>,
64
+ ) => {
65
+ const childIsFunction = typeof children === 'function'
66
+
67
+ return (
68
+ <RACLink
69
+ ref={ref}
70
+ className={mergeClassNames(
71
+ styles.link,
72
+ isDisabled && styles.isDisabled,
73
+ isInline ? styles.isInline : styles[size],
74
+ styles[variant],
75
+ className,
76
+ )}
77
+ isDisabled={isDisabled}
78
+ {...otherProps}
79
+ >
80
+ {(racStateProps) => (
81
+ <LinkContent icon={icon} iconPosition={iconPosition} isUnderlined={isUnderlined}>
82
+ {childIsFunction ? children(racStateProps) : children}
83
+ </LinkContent>
84
+ )}
85
+ </RACLink>
86
+ )
87
+ },
88
+ )
89
+
90
+ Link.displayName = 'Link'
@@ -0,0 +1,133 @@
1
+ import { Canvas, Meta, Controls, ArgTypes, DocsStory } from '@storybook/blocks'
2
+ import { ResourceLinks, KAIOInstallation, LinkTo } from '~storybook/components'
3
+ import * as exampleStories from './Link.doc.stories'
4
+
5
+ <Meta title="Components/Link/API Specification" />
6
+
7
+ # Link API Specification
8
+
9
+ Update Jan 29, 2025
10
+
11
+ <ResourceLinks
12
+ sourceCode="blah blah update when available on main"
13
+ figma="https://www.figma.com/design/FWIOtGjpv9z0by95j1SgP0/Link?node-id=273-4107"
14
+ designGuidelines="/?path=/docs/components-link-usage-guidelines--docs"
15
+ />
16
+
17
+ <KAIOInstallation exportNames={'Link'} />
18
+
19
+ ## Overview
20
+
21
+ `Link` allows users to navigate to another page or resource. It is a wrapper around the native `a` element, with additional functionality and styling.
22
+
23
+ The following example and table showcases the essential props that enable the core functionality of `Link`. For the remaining suite of API options refer to [this section](#additional-api-options).
24
+
25
+ <Canvas of={exampleStories.Playground} />
26
+
27
+ <Controls
28
+ of={exampleStories.Playground}
29
+ include={[
30
+ 'className',
31
+ 'children',
32
+ 'href',
33
+ 'variant',
34
+ 'size',
35
+ 'icon',
36
+ 'iconPosition',
37
+ 'underlined',
38
+ 'isInline',
39
+ 'isUnderlined',
40
+ 'isReversed',
41
+ 'isDisabled',
42
+ ]}
43
+ />
44
+
45
+ ## API
46
+
47
+ This is built on top of [React Aria's Link component](https://react-spectrum.adobe.com/react-aria/Link.html)
48
+
49
+ ## Navigation and native anchor attributes
50
+
51
+ Out of the box, `Link` supports the majority of native `anchor` attributes and shares the same basic behaviour, i.e. `href` will trigger new page loads.
52
+
53
+ While client side routing is possible, the `Link` is agnostic to the routing technology chosen. Refer to our general set up guide to get started with [client side routing](#client-side-routing).
54
+
55
+ #### Opening new tabs and accessibility considerations
56
+
57
+ The general recommendation is to limit the number of links that open a new tab or window on a single page. While there are valid scenarios that can help avoid loss of data and or progress, as with links in forms, opening new tabs can be disorienting for users - especially for those who have difficulty perceiving visual content.
58
+
59
+ In order to provide advance warning to all users, it is recommended that links using `target="_blank"` be accompanied by a visual indicator and audible warning. As shown in the following example, additional context can be provided via a visually hidden element within the `children` of the component.
60
+
61
+ You may also consider using the `rel="noopener noreferrer"` attribute to prevent the new tab from accessing the `window.opener` object, which can be a security risk. Read more about this consideration on the [Lighthouse docs](https://developer.chrome.com/docs/lighthouse/best-practices/external-anchors-use-rel-noopener).
62
+
63
+ <Canvas of={exampleStories.LinkOpensInNewTab} />
64
+
65
+ For more context on this recommendation, we recommend taking a look at the [W3C page on the G200 success criteria](https://www.w3.org/TR/WCAG20-TECHS/G200.html).
66
+
67
+ ### Variants
68
+
69
+ `Link` supports `primary`, `secondary` and `white` variants. If the `variant` prop is not specified, the default variant is `primary`.
70
+
71
+ <Canvas of={exampleStories.LinkVariants} />
72
+
73
+ <DocsStory of={exampleStories.LinkVariantWhite} expanded={false} />
74
+
75
+ ### Sizes
76
+
77
+ Link supports the following sizes: `extra-small`, `small`, `body` and `intro-lede`. If the `size` prop is not specified, the default size is `body`.
78
+
79
+ <Canvas of={exampleStories.LinkSizes} />
80
+
81
+ ### Links within Text
82
+
83
+ The `isInline` prop can be toggled to have the `Link` inherit the sizing from the parent text element. This is useful when the `Link` is nested within a paragraph or other text element.
84
+
85
+ ### Link content and children
86
+
87
+ Labels and any `Link` content can be passed to the component via `children`. Where possible, ensure `Link` does not break over multiple lines. [See the section](/docs/components-link-usage-guidelines--docs) for standalone links for guidance. For icons as content, refer to the [next section](#icons-and-positioning).
88
+
89
+ ```tsx
90
+ <Link href="#link">Label</Link>
91
+ ```
92
+
93
+ While in most cases, `children` will be a `ReactNode`, `Link` also accepts a render function with React Aria's `LinkRenderProps`. This allows for more advanced styling and rendering options by hooking into React Aria's internal Link state. You can read more about this [here](https://react-spectrum.adobe.com/react-aria/Link.html#styling).
94
+
95
+ ### Icons and positioning
96
+
97
+ [This is the intended icon](docs/components-icon-icon-future-api-specification--docs) component to be used with `Link`. The `icon` property abstracts the need to handle positioning and sizing of icons within the `Link` component. The `iconPosition` prop allows for the icon to be positioned to the left or right of the `children`.
98
+
99
+ ### Icons and Underlines
100
+
101
+ The underline on the `Link` can be toggled using the `isUnderlined` prop and is `true` by default. If the `Link` is not underlined, the `icon` prop must be passed. The `icon` prop is optional if the link is underlined.
102
+
103
+ <Canvas of={exampleStories.LinkWithIconStart} />
104
+
105
+ Setting the `iconPosition` props will ensure content is flipped in `RTL` layouts. Note that icons will need the [shouldMirrorInRTL](/docs/illustrations-icon-icon-future-api-specification--docs#mirror-in-rtl) prop set to `true` when mirroring is required.
106
+
107
+ <Canvas of={exampleStories.LinkWithIconEnd} />
108
+
109
+ ## Client side routing
110
+
111
+ Please refer to the [client side routing](/docs/guides-client-side-routing--docs) for more information on how to set up client side routing with the `Link` component.
112
+
113
+ ## Additional API options
114
+
115
+ The following table is a collection of additional React Aria and native HTML props that are exposed from the [React Aria Link API](https://react-spectrum.adobe.com/react-aria/Link.html). These are not required for the implementation of `LinkButton` but can be used to extend its functionality. Refer back to the [overview section](#overview) for the core props that enable most use cases.
116
+
117
+ <ArgTypes
118
+ of={exampleStories.Playground}
119
+ exclude={[
120
+ 'className',
121
+ 'children',
122
+ 'href',
123
+ 'variant',
124
+ 'size',
125
+ 'icon',
126
+ 'iconPosition',
127
+ 'underlined',
128
+ 'isInline',
129
+ 'isUnderlined',
130
+ 'isReversed',
131
+ 'isDisabled',
132
+ ]}
133
+ />