Skip to content

Commit 5cc22af

Browse files
committed
Create shared UI library
1 parent a3b2f8f commit 5cc22af

File tree

12 files changed

+200
-148
lines changed

12 files changed

+200
-148
lines changed

apps/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@tanstack/react-query": "5.81.5",
2626
"@tanstack/react-table": "^8.21.3",
2727
"@thirdweb-dev/service-utils": "workspace:*",
28+
"@workspace/ui": "workspace:*",
2829
"@thirdweb-dev/vault-sdk": "workspace:*",
2930
"@vercel/functions": "2.2.2",
3031
"@vercel/og": "^0.6.8",
Lines changed: 2 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,2 @@
1-
import { Slot } from "@radix-ui/react-slot";
2-
import { cva, type VariantProps } from "class-variance-authority";
3-
import * as React from "react";
4-
5-
import { cn } from "@/lib/utils";
6-
7-
const buttonVariants = cva(
8-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9-
{
10-
defaultVariants: {
11-
size: "default",
12-
variant: "default",
13-
},
14-
variants: {
15-
size: {
16-
default: "h-10 px-4 py-2",
17-
icon: "h-10 w-10",
18-
lg: "h-11 rounded-md px-8",
19-
sm: "h-9 rounded-md px-3",
20-
},
21-
variant: {
22-
default: "bg-foreground text-background hover:bg-foreground/90",
23-
destructive:
24-
"bg-destructive hover:bg-destructive/90 text-destructive-foreground ",
25-
ghost: "hover:bg-accent hover:text-accent-foreground",
26-
link: "underline-offset-4 hover:underline",
27-
outline:
28-
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
29-
primary: "bg-primary hover:bg-primary/90 text-primary-foreground ",
30-
secondary:
31-
"bg-secondary hover:bg-secondary/80 text-secondary-foreground ",
32-
upsell:
33-
"bg-green-600 text-white hover:bg-green-700 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
34-
},
35-
},
36-
},
37-
);
38-
39-
export interface ButtonProps
40-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
41-
VariantProps<typeof buttonVariants> {
42-
asChild?: boolean;
43-
}
44-
45-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
46-
({ className, variant, size, asChild = false, disabled, ...props }, ref) => {
47-
const Comp = asChild ? Slot : "button";
48-
49-
// "button" elements automatically handle the `disabled` attribute.
50-
// For non-button elements rendered via `asChild` (e.g. <a>), we still want
51-
// to visually convey the disabled state and prevent user interaction.
52-
// We do that by conditionally adding the same utility classes that the
53-
// `disabled:` pseudo-variant would normally apply and by setting
54-
// `aria-disabled` for accessibility.
55-
const disabledClass = disabled ? "pointer-events-none opacity-50" : "";
56-
57-
const btnOnlyProps =
58-
Comp === "button"
59-
? {
60-
type:
61-
(props as React.ButtonHTMLAttributes<HTMLButtonElement>).type ||
62-
("button" as const),
63-
}
64-
: undefined;
65-
66-
return (
67-
<Comp
68-
aria-disabled={disabled ? true : undefined}
69-
className={cn(
70-
buttonVariants({ className, size, variant }),
71-
disabledClass,
72-
)}
73-
disabled={disabled}
74-
ref={ref}
75-
{...props}
76-
{...btnOnlyProps}
77-
/>
78-
);
79-
},
80-
);
81-
Button.displayName = "Button";
82-
83-
export { Button, buttonVariants };
1+
export type { ButtonProps } from "@workspace/ui";
2+
export { Button, buttonVariants } from "@workspace/ui";

apps/dashboard/tailwind.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { fontFamily } from "tailwindcss/defaultTheme";
22

33
/** @type {import('tailwindcss').Config} */
44
module.exports = {
5-
content: ["./src/**/*.{ts,tsx}"],
5+
content: ["./src/**/*.{ts,tsx}", "../../packages/ui/src/**/*.{ts,tsx}"],
66
darkMode: ["class"],
77
plugins: [require("tailwindcss-animate")],
88
prefix: "",

apps/playground-web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"sonner": "2.0.6",
3939
"tailwind-merge": "^2.6.0",
4040
"thirdweb": "workspace:*",
41+
"@workspace/ui": "workspace:*",
4142
"use-debounce": "^10.0.5",
4243
"zod": "3.25.75"
4344
},
Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,2 @@
1-
import { Slot } from "@radix-ui/react-slot";
2-
import { cva, type VariantProps } from "class-variance-authority";
3-
import * as React from "react";
4-
5-
import { cn } from "@/lib/utils";
6-
7-
const buttonVariants = cva(
8-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9-
{
10-
defaultVariants: {
11-
size: "default",
12-
variant: "default",
13-
},
14-
variants: {
15-
size: {
16-
default: "h-10 px-4 py-2",
17-
icon: "h-10 w-10",
18-
lg: "h-11 rounded-md px-8",
19-
sm: "h-9 rounded-md px-3",
20-
},
21-
variant: {
22-
default: "bg-foreground text-background hover:bg-foreground/90",
23-
destructive:
24-
"bg-destructive hover:bg-destructive/90 text-semibold text-destructive-foreground ",
25-
ghost: "hover:bg-accent text-semibold hover:text-accent-foreground",
26-
link: "text-primary underline-offset-4 hover:underline text-semibold",
27-
outline:
28-
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground text-semibold",
29-
primary:
30-
"bg-primary hover:bg-primary/90 text-semibold text-primary-foreground ",
31-
secondary:
32-
"bg-secondary hover:bg-secondary/80 text-semibold text-secondary-foreground ",
33-
upsell:
34-
"bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
35-
},
36-
},
37-
},
38-
);
39-
40-
export interface ButtonProps
41-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
42-
VariantProps<typeof buttonVariants> {
43-
asChild?: boolean;
44-
}
45-
46-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
47-
({ className, variant, size, asChild = false, ...props }, ref) => {
48-
const Comp = asChild ? Slot : "button";
49-
const btnOnlyProps =
50-
Comp === "button" ? { type: "button" as const } : undefined;
51-
return (
52-
<Comp
53-
className={cn(buttonVariants({ className, size, variant }))}
54-
ref={ref}
55-
{...btnOnlyProps}
56-
{...props}
57-
/>
58-
);
59-
},
60-
);
61-
Button.displayName = "Button";
62-
63-
export { Button, buttonVariants };
1+
export type { ButtonProps } from "@workspace/ui";
2+
export { Button, buttonVariants } from "@workspace/ui";

apps/playground-web/tailwind.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { fontFamily } from "tailwindcss/defaultTheme";
22

33
/** @type {import('tailwindcss').Config} */
44
module.exports = {
5-
content: ["./src/**/*.{ts,tsx}"],
5+
content: ["./src/**/*.{ts,tsx}", "../../packages/ui/src/**/*.{ts,tsx}"],
66
darkMode: ["class"],
77
plugins: [require("tailwindcss-animate")],
88
prefix: "",

packages/ui/package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "@workspace/ui",
3+
"version": "0.0.1",
4+
"private": true,
5+
"main": "./src/index.ts",
6+
"types": "./src/index.ts",
7+
"exports": {
8+
".": {
9+
"types": "./src/index.ts",
10+
"import": "./src/index.ts"
11+
}
12+
},
13+
"dependencies": {
14+
"@radix-ui/react-label": "^2.1.7",
15+
"@radix-ui/react-slot": "^1.2.3",
16+
"class-variance-authority": "^0.7.1",
17+
"clsx": "^2.1.1",
18+
"tailwind-merge": "^2.6.0"
19+
},
20+
"peerDependencies": {
21+
"react": "^19.0.0",
22+
"react-dom": "^19.0.0"
23+
},
24+
"devDependencies": {
25+
"@types/react": "^19.0.0",
26+
"@types/react-dom": "^19.0.0",
27+
"typescript": "^5.0.0"
28+
}
29+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Slot } from "@radix-ui/react-slot";
2+
import { cva, type VariantProps } from "class-variance-authority";
3+
import * as React from "react";
4+
import { cn } from "@/lib/utils";
5+
6+
const buttonVariants = cva(
7+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
8+
{
9+
defaultVariants: {
10+
size: "default",
11+
variant: "default",
12+
},
13+
variants: {
14+
size: {
15+
default: "h-10 px-4 py-2",
16+
icon: "h-10 w-10",
17+
lg: "h-11 rounded-md px-8",
18+
sm: "h-9 rounded-md px-3",
19+
},
20+
variant: {
21+
default: "bg-foreground text-background hover:bg-foreground/90",
22+
destructive:
23+
"bg-destructive hover:bg-destructive/90 text-destructive-foreground ",
24+
ghost: "hover:bg-accent hover:text-accent-foreground",
25+
link: "underline-offset-4 hover:underline",
26+
outline:
27+
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
28+
primary: "bg-primary hover:bg-primary/90 text-primary-foreground ",
29+
secondary:
30+
"bg-secondary hover:bg-secondary/80 text-secondary-foreground ",
31+
upsell:
32+
"bg-green-600 text-white hover:bg-green-700 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
33+
},
34+
},
35+
},
36+
);
37+
38+
export interface ButtonProps
39+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
40+
VariantProps<typeof buttonVariants> {
41+
asChild?: boolean;
42+
}
43+
44+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
45+
({ className, variant, size, asChild = false, disabled, ...props }, ref) => {
46+
const Comp = asChild ? Slot : "button";
47+
48+
// "button" elements automatically handle the `disabled` attribute.
49+
// For non-button elements rendered via `asChild` (e.g. <a>), we still want
50+
// to visually convey the disabled state and prevent user interaction.
51+
// We do that by conditionally adding the same utility classes that the
52+
// `disabled:` pseudo-variant would normally apply and by setting
53+
// `aria-disabled` for accessibility.
54+
const disabledClass = disabled ? "pointer-events-none opacity-50" : "";
55+
56+
const btnOnlyProps =
57+
Comp === "button"
58+
? {
59+
type:
60+
(props as React.ButtonHTMLAttributes<HTMLButtonElement>).type ||
61+
("button" as const),
62+
}
63+
: undefined;
64+
65+
return (
66+
<Comp
67+
aria-disabled={disabled ? true : undefined}
68+
className={cn(
69+
buttonVariants({ className, size, variant }),
70+
disabledClass,
71+
)}
72+
disabled={disabled}
73+
ref={ref}
74+
{...props}
75+
{...btnOnlyProps}
76+
/>
77+
);
78+
},
79+
);
80+
Button.displayName = "Button";
81+
82+
export { Button, buttonVariants };

packages/ui/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// button
2+
export type { ButtonProps } from "./components/button";
3+
export { Button, buttonVariants } from "./components/button";
4+
5+
// utils
6+
export { cn } from "./lib/utils";

packages/ui/src/lib/utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { type ClassValue, clsx } from "clsx";
2+
import { twMerge } from "tailwind-merge";
3+
4+
export function cn(...inputs: ClassValue[]) {
5+
return twMerge(clsx(inputs));
6+
}

0 commit comments

Comments
 (0)