): JSX.Element => {
+ const { copied, copy } = useClipboard({
+ timeout: 1500,
+ });
+
+ /**
+ * Maintaining a debounced copied state too, so we can change the tooltip content only after the tooltip is actually invisible
+ */
+
+ const [debouncedCopied, debouncilySetState] = useDebouncedState(copied, 200);
+
+ useEffect(() => {
+ debouncilySetState(copied);
+ }, [debouncilySetState, copied]);
+
+ const tooltipInCopiedState = copied || debouncedCopied;
+
+ return (
+
+ copy(text)}
+ className="inline-flex rounded-md"
+ >
+
+ {children ?? text}
+
+
+
+
+
+ {tooltipInCopiedState && }
+ {tooltipInCopiedState ? "Copied" : "Click to copy"}
+
+
+
+ );
+};
diff --git a/src/components/requirements/Requirement.tsx b/src/components/requirements/Requirement.tsx
new file mode 100644
index 0000000000..5066d2d651
--- /dev/null
+++ b/src/components/requirements/Requirement.tsx
@@ -0,0 +1,48 @@
+import { cn } from "@/lib/cssUtils";
+import type { PropsWithChildren } from "react";
+
+export const Requirement = ({
+ className,
+ children,
+}: PropsWithChildren<{ className?: string }>) => (
+
+ {children}
+
+);
+
+export const RequirementImage = ({
+ className,
+ children,
+}: PropsWithChildren<{ className?: string }>) => (
+
+ {children}
+
+);
+
+export const RequirementContent = ({
+ className,
+ children,
+}: PropsWithChildren<{ className?: string }>) => (
+
+ {children}
+
+);
+
+export const RequirementFooter = ({
+ className,
+ children,
+}: PropsWithChildren<{ className?: string }>) => (
+ *]:mt-1",
+ className,
+ )}
+ >
+ {children}
+
+);
diff --git a/src/components/requirements/RequirementLink.tsx b/src/components/requirements/RequirementLink.tsx
new file mode 100644
index 0000000000..57d14f27d5
--- /dev/null
+++ b/src/components/requirements/RequirementLink.tsx
@@ -0,0 +1,18 @@
+import type { PropsWithChildren } from "react";
+import { Anchor } from "../ui/Anchor";
+
+export const RequirementLink = ({
+ href,
+ children,
+}: PropsWithChildren<{ href: string }>) => (
+
+ {children}
+
+);
diff --git a/src/components/ui/Anchor.tsx b/src/components/ui/Anchor.tsx
index a712e42e15..11ead78fda 100644
--- a/src/components/ui/Anchor.tsx
+++ b/src/components/ui/Anchor.tsx
@@ -12,7 +12,7 @@ const anchorVariants = cva(
variant: {
default: "text-foreground hover:underline",
highlighted: "text-blue-500 dark:text-blue-400 hover:underline",
- muted: "text-muted-foreground hover:underline",
+ secondary: "text-foreground-secondary hover:underline",
unstyled: "",
},
},
diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx
index 10a0e1b0fd..f81b6a5e8d 100644
--- a/src/components/ui/Badge.tsx
+++ b/src/components/ui/Badge.tsx
@@ -7,7 +7,7 @@ export const badgeVariants = cva(
{
variants: {
size: {
- sm: "text-xs h-5",
+ sm: "text-xs h-5 gap-1",
md: "text-sm h-6",
lg: "text-base h-8",
},
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
index 85917978cf..b59343d5da 100644
--- a/src/components/ui/Button.tsx
+++ b/src/components/ui/Button.tsx
@@ -32,7 +32,7 @@ const buttonVariants = cva(
},
size: {
xs: "h-6 px-2 text-xs gap-1",
- sm: "h-8 px-3 text-sm",
+ sm: "h-8 px-3 text-sm gap-1",
md: "h-11 px-4 py-2",
lg: "h-12 px-6 py-4 text-lg",
xl: "h-14 px-6 py-4 text-lg gap-2",
diff --git a/src/config/chains.ts b/src/config/chains.ts
new file mode 100644
index 0000000000..dc56e4ac7e
--- /dev/null
+++ b/src/config/chains.ts
@@ -0,0 +1,21 @@
+import { mainnet, sepolia } from "viem/chains";
+import type { Register } from "wagmi";
+
+export const CHAINS = {
+ [mainnet.id]: {
+ name: mainnet.name,
+ icon: "/chainLogos/eth.svg",
+ },
+ [sepolia.id]: {
+ name: sepolia.name,
+ icon: "/chainLogos/eth.svg",
+ },
+} satisfies Record<
+ SupportedChainID,
+ {
+ name: string;
+ icon: string;
+ }
+>;
+
+export type SupportedChainID = Register["config"]["chains"][number]["id"];
diff --git a/src/stories/Anchor.stories.tsx b/src/stories/Anchor.stories.tsx
index 0390308ceb..6032a0120f 100644
--- a/src/stories/Anchor.stories.tsx
+++ b/src/stories/Anchor.stories.tsx
@@ -35,11 +35,11 @@ export const Default: Story = {
},
};
-export const Muted: Story = {
+export const Secondary: Story = {
args: {
...Default.args,
- variant: "muted",
- children: "Muted",
+ variant: "secondary",
+ children: "Secondary",
},
argTypes: {
...Default.argTypes,
diff --git a/src/stories/requirements/ChainIndicator.stories.tsx b/src/stories/requirements/ChainIndicator.stories.tsx
new file mode 100644
index 0000000000..a032a29bdf
--- /dev/null
+++ b/src/stories/requirements/ChainIndicator.stories.tsx
@@ -0,0 +1,17 @@
+import { ChainIndicator } from "@/components/requirements/ChainIndicator";
+import type { Meta, StoryObj } from "@storybook/react";
+
+const meta: Meta = {
+ title: "Requirement building blocks/ChainIndicator",
+ component: ChainIndicator,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ chain: 1,
+ },
+};
diff --git a/src/stories/requirements/DataBlock.stories.tsx b/src/stories/requirements/DataBlock.stories.tsx
new file mode 100644
index 0000000000..e277fe3657
--- /dev/null
+++ b/src/stories/requirements/DataBlock.stories.tsx
@@ -0,0 +1,17 @@
+import { DataBlock } from "@/components/requirements/DataBlock";
+import type { Meta, StoryObj } from "@storybook/react";
+
+const meta: Meta = {
+ title: "Requirement building blocks/DataBlock",
+ component: DataBlock,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ children: "DataBlock",
+ },
+};
diff --git a/src/stories/requirements/DataBlockWithCopy.stories.tsx b/src/stories/requirements/DataBlockWithCopy.stories.tsx
new file mode 100644
index 0000000000..672523e416
--- /dev/null
+++ b/src/stories/requirements/DataBlockWithCopy.stories.tsx
@@ -0,0 +1,24 @@
+import { DataBlockWithCopy } from "@/components/requirements/DataBlockWithCopy";
+import { TooltipProvider } from "@/components/ui/Tooltip";
+import type { Meta, StoryObj } from "@storybook/react";
+
+const meta: Meta = {
+ title: "Requirement building blocks/DataBlockWithCopy",
+ component: DataBlockWithCopy,
+ decorators: (Story) => (
+
+
+
+ ),
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ children: "DataBlockWithCopy",
+ text: "DataBlockWithCopy",
+ },
+};
diff --git a/src/stories/requirements/Requirement.stories.tsx b/src/stories/requirements/Requirement.stories.tsx
new file mode 100644
index 0000000000..ce064e1b7c
--- /dev/null
+++ b/src/stories/requirements/Requirement.stories.tsx
@@ -0,0 +1,53 @@
+import { ChainIndicator } from "@/components/requirements/ChainIndicator";
+import { DataBlockWithCopy } from "@/components/requirements/DataBlockWithCopy";
+import {
+ Requirement,
+ RequirementContent,
+ RequirementFooter,
+ RequirementImage,
+} from "@/components/requirements/Requirement";
+import { RequirementLink } from "@/components/requirements/RequirementLink";
+import { Card } from "@/components/ui/Card";
+import { TooltipProvider } from "@/components/ui/Tooltip";
+import { QuestionMark } from "@phosphor-icons/react/dist/ssr";
+import type { Meta, StoryObj } from "@storybook/react";
+
+const RequirementExample = () => (
+
+
+
+
+
+
+
+ {"Copy "}
+
+ {" to your clipboard"}
+
+
+
+
+
+ View on explorer
+
+
+
+
+
+);
+
+const meta: Meta = {
+ title: "Requirement building blocks/Requirement",
+ component: RequirementExample,
+ decorators: (Story) => (
+
+
+
+ ),
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
diff --git a/src/stories/requirements/RequirementLink.stories.tsx b/src/stories/requirements/RequirementLink.stories.tsx
new file mode 100644
index 0000000000..e701e8a6df
--- /dev/null
+++ b/src/stories/requirements/RequirementLink.stories.tsx
@@ -0,0 +1,18 @@
+import { RequirementLink } from "@/components/requirements/RequirementLink";
+import type { Meta, StoryObj } from "@storybook/react";
+
+const meta: Meta = {
+ title: "Requirement building blocks/RequirementLink",
+ component: RequirementLink,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ href: "https://polygonscan.com/token/0xff04820c36759c9f5203021fe051239ad2dcca8a",
+ children: "Guild Pin contract",
+ },
+};
diff --git a/src/styles/globals.css b/src/styles/globals.css
index a618b6a950..e74452b9e7 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -63,6 +63,17 @@ html {
--blue-800: #1e40af;
--blue-900: #1e3a8a;
+ --orange-50: #fff7ed;
+ --orange-100: #ffedd5;
+ --orange-200: #fed7aa;
+ --orange-300: #fdba74;
+ --orange-400: #fb923c;
+ --orange-500: #f97316;
+ --orange-600: #ea580c;
+ --orange-700: #c2410c;
+ --orange-800: #9a3412;
+ --orange-900: #7c2d12;
+
--red-50: #fef2f2;
--red-100: #fee2e2;
--red-200: #fecaca;
@@ -156,6 +167,7 @@ html {
--icon-success: var(--green-500);
--icon-error: var(--red-500);
+ --icon-warning: var(--orange-500);
}
.dark {
@@ -213,6 +225,7 @@ html {
--icon-success: var(--green-400);
--icon-error: var(--red-400);
+ --icon-warning: var(--orange-300);
}
body {
diff --git a/tailwind.config.ts b/tailwind.config.ts
index d35465e192..85b633f1da 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -12,11 +12,11 @@ const config = {
],
prefix: "",
theme: {
- fontFamily: {
- sans: ["var(--font-inter,sans-serif)"],
- display: ["var(--font-dystopian,sans-serif)"],
- },
extend: {
+ fontFamily: {
+ sans: ["var(--font-inter,sans-serif)"],
+ display: ["var(--font-dystopian,sans-serif)"],
+ },
colors: {
background: "var(--background)",
foreground: {
@@ -71,8 +71,19 @@ const config = {
"scroll-thumb": "var(--scroll-thumb)",
icon: {
success: "var(--icon-success)",
- error: "var(--icon-error)"
- }
+ error: "var(--icon-error)",
+ warning: "var(--icon-warning)",
+ },
+ blackAlpha: {
+ DEFAULT: "var(--blackAlpha)",
+ medium: "var(--blackAlpha-medium)",
+ hard: "var(--blackAlpha-hard)",
+ },
+ whiteAlpha: {
+ DEFAULT: "var(--whiteAlpha)",
+ medium: "var(--whiteAlpha-medium)",
+ hard: "var(--whiteAlpha-hard)",
+ },
},
keyframes: {
"collapse-open": {