Skip to content

Commit 08a0178

Browse files
committed
feat: add button, container, externalLink, fonts, icons & sparkleCard UI components
1 parent 81fc410 commit 08a0178

6 files changed

Lines changed: 205 additions & 6 deletions

File tree

src/components/ui/button.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
55
import { cn } from "@/utils/cn";
66

77
const buttonVariants = cva(
8-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-900 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 dark:focus-visible:ring-neutral-300",
8+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-300 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 dark:focus-visible:ring-neutral-700 cursor-pointer",
99
{
1010
variants: {
1111
variant: {
@@ -32,7 +32,7 @@ const buttonVariants = cva(
3232
variant: "default",
3333
size: "default",
3434
},
35-
}
35+
},
3636
);
3737

3838
export interface ButtonProps
@@ -51,7 +51,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
5151
{...props}
5252
/>
5353
);
54-
}
54+
},
5555
);
5656
Button.displayName = "Button";
5757

src/components/ui/container.tsx

Whitespace-only changes.

src/components/ui/externalLink.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { ReactNode } from "react";
2+
import { cn } from "@/utils/cn";
3+
4+
interface Props {
5+
href: string;
6+
title: string;
7+
children: ReactNode;
8+
className?: string;
9+
}
10+
11+
const ExternalLink = (props: Props) => {
12+
return (
13+
<a
14+
href={props.href}
15+
title={props.title}
16+
rel="noreferrer"
17+
target="_blank"
18+
className={cn(props.className)}
19+
>
20+
{props.children}
21+
</a>
22+
);
23+
};
24+
25+
export default ExternalLink;

src/components/ui/fonts.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { Geist, Geist_Mono, Onest } from "next/font/google";
22

33
export const fontSans = Geist({
4-
variable: "--font-sans",
4+
variable: "--font-geist",
55
subsets: ["latin"],
66
});
77

88
export const fontHeadings = Onest({
9-
variable: "--font-headings",
9+
variable: "--font-onest",
1010
subsets: ["latin"],
1111
});
1212

1313
export const fontMono = Geist_Mono({
14-
variable: "--font-mono",
14+
variable: "--font-geist-mono",
1515
subsets: ["latin"],
1616
});

src/components/ui/icons.tsx

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import type { SVGProps } from "react";
2+
import { cn } from "@/utils/cn";
3+
4+
export type Position =
5+
| "top-left"
6+
| "top-right"
7+
| "bottom-left"
8+
| "bottom-right";
9+
10+
export const SparkleSvg = ({
11+
position,
12+
className,
13+
}: {
14+
position: Position;
15+
className?: string;
16+
}) => {
17+
return (
18+
<svg
19+
xmlns="http://www.w3.org/2000/svg"
20+
width="24"
21+
height="24"
22+
viewBox="0 0 24 24"
23+
fill="none"
24+
stroke="currentColor"
25+
strokeWidth="2"
26+
strokeLinecap="round"
27+
strokeLinejoin="round"
28+
className={cn(
29+
"absolute z-10 size-4 fill-black",
30+
position === "top-right" && "-top-2 -right-2",
31+
position === "bottom-left" && "-bottom-2 -left-2",
32+
position === "bottom-right" && "-right-2 -bottom-2",
33+
position === "top-left" && "-top-2 -left-2",
34+
className,
35+
)}
36+
>
37+
<path d="m12 3-1.9 5.8a2 2 0 0 1-1.287 1.288L3 12l5.8 1.9a2 2 0 0 1 1.288 1.287L12 21l1.9-5.8a2 2 0 0 1 1.287-1.288L21 12l-5.8-1.9a2 2 0 0 1-1.288-1.287Z"></path>
38+
</svg>
39+
);
40+
};
41+
42+
export const Shiki = (props: SVGProps<SVGSVGElement>) => (
43+
<svg
44+
fill="none"
45+
xmlns="http://www.w3.org/2000/svg"
46+
{...props}
47+
viewBox="0 0 266 266"
48+
>
49+
<circle cx={219.5} cy={46.5} r={46.5} fill="#CB7676" />
50+
<path fill="#4B9978" d="M0 48h266v65H0z" />
51+
<path
52+
d="M109.463 144.426c-.451-5.634-2.564-10.029-6.339-13.184-3.719-3.156-9.381-4.733-16.988-4.733-4.846 0-8.818.591-11.917 1.775-3.042 1.127-5.296 2.676-6.761 4.648-1.465 1.972-2.226 4.226-2.282 6.761-.113 2.085.254 3.973 1.099 5.663.901 1.634 2.31 3.127 4.225 4.479 1.916 1.296 4.367 2.48 7.353 3.55 2.987 1.071 6.536 2.029 10.65 2.874l14.198 3.042c9.579 2.029 17.777 4.705 24.595 8.029 6.817 3.325 12.396 7.241 16.734 11.748 4.339 4.451 7.522 9.466 9.55 15.044 2.085 5.578 3.156 11.663 3.212 18.256-.056 11.381-2.902 21.016-8.536 28.905-5.635 7.888-13.692 13.888-24.172 18.002-10.424 4.113-22.96 6.169-37.61 6.169-15.044 0-28.172-2.225-39.385-6.676-11.156-4.452-19.833-11.298-26.03-20.538-6.142-9.297-9.241-21.186-9.298-35.666h44.625c.282 5.296 1.606 9.747 3.972 13.354 2.367 3.606 5.691 6.338 9.974 8.198 4.338 1.859 9.494 2.789 15.466 2.789 5.015 0 9.212-.62 12.593-1.86 3.381-1.239 5.944-2.958 7.691-5.155 1.747-2.198 2.648-4.705 2.705-7.522-.057-2.648-.93-4.959-2.62-6.931-1.634-2.028-4.339-3.831-8.114-5.409-3.775-1.634-8.874-3.155-15.298-4.564l-17.241-3.718c-15.326-3.325-27.412-8.875-36.258-16.65-8.79-7.832-13.156-18.509-13.1-32.032-.056-10.987 2.874-20.594 8.79-28.82 5.973-8.283 14.227-14.734 24.763-19.355 10.593-4.62 22.735-6.93 36.427-6.93 13.974 0 26.059 2.338 36.258 7.015 10.198 4.677 18.058 11.269 23.58 19.777 5.578 8.452 8.395 18.34 8.452 29.665h-44.963Z"
53+
fill="#83D0DA"
54+
/>
55+
<path fill="#E6CC78" d="M217 0v266h-65V0z" />
56+
</svg>
57+
);
58+
59+
export const Logo = (props: SVGProps<SVGSVGElement>) => (
60+
<svg
61+
xmlns="http://www.w3.org/2000/svg"
62+
fill="none"
63+
viewBox="0 0 512 512"
64+
{...props}
65+
>
66+
<rect
67+
id="r9"
68+
width="512"
69+
height="512"
70+
x="0"
71+
y="0"
72+
fill="url(#ra)"
73+
stroke="#FFF"
74+
strokeOpacity="100%"
75+
strokeWidth="0"
76+
paintOrder="stroke"
77+
rx="128"
78+
></rect>
79+
<defs>
80+
<radialGradient
81+
id="ra"
82+
cx="50%"
83+
cy="50%"
84+
r="100%"
85+
fx="50%"
86+
fy="0%"
87+
gradientUnits="objectBoundingBox"
88+
>
89+
<stop stopColor="#1D1D1D"></stop>
90+
<stop offset="1"></stop>
91+
</radialGradient>
92+
</defs>
93+
<svg
94+
xmlns="http://www.w3.org/2000/svg"
95+
width="365"
96+
height="365"
97+
x="73.5"
98+
y="73.5"
99+
alignmentBaseline="middle"
100+
color="#E6E6E6"
101+
viewBox="0 0 24 24"
102+
>
103+
<path
104+
fill="currentColor"
105+
d="M8 5h2v2H8zM6 7h2v2H6zM4 9h2v2H4zm-2 2h2v2H2zm2 2h2v2H4zm2 2h2v2H6zm2 2h2v2H8zm8-12h-2v2h2zm2 2h-2v2h2zm2 2h-2v2h2zm2 2h-2v2h2zm-2 2h-2v2h2zm-2 2h-2v2h2zm-2 2h-2v2h2z"
106+
></path>
107+
</svg>
108+
</svg>
109+
);
110+
111+
export const Github = (props: SVGProps<SVGSVGElement>) => (
112+
<svg
113+
viewBox="0 0 256 250"
114+
fill="currentColor"
115+
xmlns="http://www.w3.org/2000/svg"
116+
preserveAspectRatio="xMidYMid"
117+
{...props}
118+
>
119+
<path d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z" />
120+
</svg>
121+
);
122+
123+
export const XTwitter = (props: SVGProps<SVGSVGElement>) => (
124+
<svg
125+
xmlns="http://www.w3.org/2000/svg"
126+
fill="none"
127+
viewBox="0 0 1200 1227"
128+
{...props}
129+
>
130+
<path
131+
fill="currentColor"
132+
d="M714.163 519.284 1160.89 0h-105.86L667.137 450.887 357.328 0H0l468.492 681.821L0 1226.37h105.866l409.625-476.152 327.181 476.152H1200L714.137 519.284h.026ZM569.165 687.828l-47.468-67.894-377.686-540.24h162.604l304.797 435.991 47.468 67.894 396.2 566.721H892.476L569.165 687.854v-.026Z"
133+
/>
134+
</svg>
135+
);

src/components/ui/sparkleCard.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { ReactNode } from "react";
2+
import { SparkleSvg, type Position } from "@/components/ui/icons";
3+
import { cn } from "@/utils/cn";
4+
5+
export interface SectionProps extends React.HTMLAttributes<HTMLElement> {
6+
children: ReactNode;
7+
sparklePositions?: Position[];
8+
className?: string;
9+
}
10+
11+
export const SparkleCard = ({
12+
children,
13+
sparklePositions = ["top-left"],
14+
className,
15+
...props
16+
}: SectionProps) => {
17+
return (
18+
<section
19+
className={cn(
20+
"rounded-none border border-neutral-200 bg-neutral-50 p-11 dark:border-neutral-800 dark:bg-neutral-900",
21+
sparklePositions.length > 0 && "relative",
22+
className,
23+
)}
24+
{...props}
25+
>
26+
{children}
27+
{sparklePositions.length > 0 &&
28+
sparklePositions.map((sparklePosition, index) => {
29+
return (
30+
<SparkleSvg
31+
key={`sparkle_${index}`}
32+
position={sparklePosition}
33+
className="hover:animate-rotate"
34+
/>
35+
);
36+
})}
37+
</section>
38+
);
39+
};

0 commit comments

Comments
 (0)