diff --git a/.changeset/link-underline-prop.md b/.changeset/link-underline-prop.md
new file mode 100644
index 000000000..ff2072e65
--- /dev/null
+++ b/.changeset/link-underline-prop.md
@@ -0,0 +1,5 @@
+---
+"@launchpad-ui/components": minor
+---
+
+Add `underline` prop to Link component with `'always' | 'hover' | 'none'` values
diff --git a/packages/components/__tests__/Link.spec.tsx b/packages/components/__tests__/Link.spec.tsx
index a5f47e8ee..f3f18a3f3 100644
--- a/packages/components/__tests__/Link.spec.tsx
+++ b/packages/components/__tests__/Link.spec.tsx
@@ -30,4 +30,18 @@ describe('Link', () => {
expect(screen.getByRole('link')).toBeVisible();
expect(screen.getByRole('link')).toHaveAttribute('href', 'https://www.test.com');
});
+
+ it('renders with underline="always"', () => {
+ const navigate = vi.fn();
+ render(
+
+
+
+ Link
+
+
+ ,
+ );
+ expect(screen.getByRole('link')).toBeVisible();
+ });
});
diff --git a/packages/components/src/Link.tsx b/packages/components/src/Link.tsx
index 481b5c37e..7e775c9c2 100644
--- a/packages/components/src/Link.tsx
+++ b/packages/components/src/Link.tsx
@@ -16,14 +16,22 @@ const linkStyles = cva(styles.base, {
default: styles.default,
subtle: styles.subtle,
},
+ underline: {
+ always: styles.underlineAlways,
+ hover: styles.underlineHover,
+ none: styles.underlineNone,
+ },
},
defaultVariants: {
variant: 'default',
+ underline: 'hover',
},
});
interface LinkProps extends AriaLinkProps, VariantProps, DOMProps {
ref?: Ref;
+ /** Controls when the link is underlined. */
+ underline?: 'always' | 'hover' | 'none';
}
const LinkContext = createContext>(null);
@@ -35,14 +43,14 @@ const LinkContext = createContext>(nu
*/
const Link = ({ ref, ...props }: LinkProps) => {
[props, ref] = useLPContextProps(props, ref, LinkContext);
- const { variant = 'default' } = props;
+ const { variant = 'default', underline = 'hover' } = props;
return (
- linkStyles({ ...renderProps, variant, className }),
+ linkStyles({ ...renderProps, variant, underline, className }),
)}
/>
);
diff --git a/packages/components/src/styles/Link.module.css b/packages/components/src/styles/Link.module.css
index 1f7bcc9b3..fe94995df 100644
--- a/packages/components/src/styles/Link.module.css
+++ b/packages/components/src/styles/Link.module.css
@@ -41,3 +41,18 @@
composes: link;
color: var(--lp-color-text-ui-secondary);
}
+
+.underlineAlways {
+ text-decoration: underline;
+}
+
+.underlineHover {
+}
+
+.underlineNone {
+ text-decoration: none;
+
+ &[data-hovered] {
+ text-decoration: none;
+ }
+}
diff --git a/packages/components/stories/Link.stories.tsx b/packages/components/stories/Link.stories.tsx
index 4d827bfb7..166cbdcc4 100644
--- a/packages/components/stories/Link.stories.tsx
+++ b/packages/components/stories/Link.stories.tsx
@@ -35,6 +35,22 @@ export const Subtle: Story = {
},
};
+export const UnderlineAlways: Story = {
+ args: {
+ children: 'Link',
+ href: '/test',
+ underline: 'always',
+ },
+};
+
+export const UnderlineNone: Story = {
+ args: {
+ children: 'Link',
+ href: '/test',
+ underline: 'none',
+ },
+};
+
export const States: Story = {
render: (args) => {
const styles = { width: 'fit-content' };