Skip to content

Commit 2052b55

Browse files
1 parent 0ec48ad commit 2052b55

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { Link } from "./Link";
3+
4+
const meta: Meta<typeof Link> = {
5+
title: "components/Link",
6+
component: Link,
7+
args: {
8+
children: "Learn more",
9+
},
10+
};
11+
12+
export default meta;
13+
type Story = StoryObj<typeof Link>;
14+
15+
export const Large: Story = {};
16+
17+
export const Small: Story = {
18+
args: {
19+
size: "sm",
20+
},
21+
};
22+
23+
export const InlineUsage: Story = {
24+
render: () => {
25+
return (
26+
<p className="text-sm">
27+
A <Link>workspace</Link> is your personal, customized development
28+
environment. It's based on a <Link>template</Link> that configures your
29+
workspace using Terraform.
30+
</p>
31+
);
32+
},
33+
};

site/src/components/Link/Link.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Slot } from "@radix-ui/react-slot";
2+
import { type VariantProps, cva } from "class-variance-authority";
3+
import { SquareArrowOutUpRightIcon } from "lucide-react";
4+
import { forwardRef } from "react";
5+
import { cn } from "utils/cn";
6+
7+
export const linkVariants = cva(
8+
`relative inline-flex items-center no-underline font-medium text-content-link hover:cursor-pointer
9+
after:hover:content-[''] after:hover:absolute after:hover:left-0 after:hover:w-full after:hover:h-[1px] after:hover:bg-current after:hover:bottom-px
10+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-content-link
11+
focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary focus-visible:rounded-sm
12+
visited:text-content-link pl-[2px]`, //pl-[2px] adjusts the underline spacing to align with the icon on the right.
13+
{
14+
variants: {
15+
size: {
16+
lg: "text-sm gap-[2px] [&_svg]:size-icon-sm [&_svg]:p-[2px] leading-6",
17+
sm: "text-xs gap-1 [&_svg]:size-icon-xs [&_svg]:p-[1px] leading-[18px]",
18+
},
19+
},
20+
defaultVariants: {
21+
size: "lg",
22+
},
23+
},
24+
);
25+
26+
export interface LinkProps
27+
extends React.AnchorHTMLAttributes<HTMLAnchorElement>,
28+
VariantProps<typeof linkVariants> {
29+
asChild?: boolean;
30+
}
31+
32+
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
33+
({ className, children, size, asChild, ...props }, ref) => {
34+
const Comp = asChild ? Slot : "a";
35+
return (
36+
<Comp
37+
className={cn(linkVariants({ size }), className)}
38+
ref={ref}
39+
{...props}
40+
>
41+
{children}
42+
<SquareArrowOutUpRightIcon aria-hidden="true" />
43+
</Comp>
44+
);
45+
},
46+
);

0 commit comments

Comments
 (0)