diff --git a/app/(docs)/layout.tsx b/app/(docs)/layout.tsx
index 1f915365..21294195 100644
--- a/app/(docs)/layout.tsx
+++ b/app/(docs)/layout.tsx
@@ -2,7 +2,6 @@ import { DocsLayout, type DocsLayoutProps } from "fumadocs-ui/layout";
import type { ReactNode } from "react";
import { ArrowUpRight } from "lucide-react";
import { utils } from "@/utils/source";
-import { create } from "@/components/ui/icon";
import { DocsLogo } from "@/components/ui/icon";
import { Body, NavChildren, SidebarBanner } from "./layout.client";
diff --git a/app/global.css b/app/global.css
index 54176950..542236c7 100644
--- a/app/global.css
+++ b/app/global.css
@@ -31,6 +31,7 @@
--code: 0 0% 100%;
--highlight: 40 13% 91%;
--content: 32.31 11.93% 78.63%;
+ --dark: 20 14.3% 4.1%;
}
.dark {
@@ -60,6 +61,7 @@
--code: 0 7.7% 5.1%;
--highlight: 12 6.2% 15.9%;
--content: 40 5.33% 33.14%;
+ --dark: 20 14.3% 4.1%;
}
.bitcoin {
diff --git a/app/layout.tsx b/app/layout.tsx
index 0cf9faf9..3295e09c 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -5,6 +5,7 @@ import type { Viewport } from "next";
import { baseUrl, createMetadata } from "@/utils/metadata";
import { Provider } from "./provider";
import { GoogleTagManager } from "@next/third-parties/google";
+import { Banner } from "@/components/ui/banner";
import { Discord, Github, HiroSVG, Youtube, X } from "@/components/ui/icon";
const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID as string;
@@ -39,6 +40,15 @@ export default function RootLayout({
+
+ Are you a Master of Clarity?
+
{children}
diff --git a/components/ui/banner.tsx b/components/ui/banner.tsx
new file mode 100644
index 00000000..40274496
--- /dev/null
+++ b/components/ui/banner.tsx
@@ -0,0 +1,122 @@
+"use client";
+
+import { type HTMLAttributes, useCallback, useEffect, useState } from "react";
+import { Button } from "./button";
+import Link from "next/link";
+import { X } from "lucide-react";
+import { cn } from "@/utils/cn";
+import { cva } from "class-variance-authority";
+import { isWithinInterval, parseISO } from "date-fns";
+
+export const buttonVariants = cva(
+ "bg-[#CEEFD0] inline-flex items-center justify-center rounded-md p-2 text-sm text-primary font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50",
+ {
+ variants: {
+ color: {
+ outline: "border hover:bg-accent hover:text-accent-foreground",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "border bg-secondary text-secondary-foreground hover:bg-accent hover:text-accent-foreground",
+ },
+ size: {
+ sm: "gap-1 p-0.5 text-xs",
+ icon: "p-1.5 [&_svg]:size-5",
+ },
+ },
+ }
+);
+
+export function Banner({
+ id,
+ cta = "Call to Action",
+ url = "/",
+ startDate,
+ endDate,
+ ...props
+}: HTMLAttributes & {
+ cta?: string;
+ url?: string;
+ startDate?: string;
+ endDate?: string;
+}): React.ReactElement | null {
+ const [open, setOpen] = useState(true);
+ const [isWithinDateRange, setIsWithinDateRange] = useState(false);
+
+ useEffect(() => {
+ const now = new Date();
+ if (startDate && endDate) {
+ const start = parseISO(startDate);
+ const end = parseISO(endDate);
+ setIsWithinDateRange(isWithinInterval(now, { start, end }));
+ } else {
+ setIsWithinDateRange(true);
+ }
+
+ if (id) setOpen(localStorage.getItem(`nd-banner-${id}`) !== "true");
+ }, [id, startDate, endDate]);
+
+ useEffect(() => {
+ if (id) setOpen(localStorage.getItem(`nd-banner-${id}`) !== "true");
+ }, [id]);
+
+ const onClick = useCallback(() => {
+ setOpen(false);
+ if (id) localStorage.setItem(`nd-banner-${id}`, "true");
+ }, [id]);
+
+ if (!isWithinDateRange) return null;
+
+ return (
+
+ {id ? (
+
+ ) : null}
+
+
{props.children}
+ {cta && (
+
+ )}
+
+ {id ? (
+
+ ) : null}
+
+ );
+}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
index c020e5da..9f1819ba 100644
--- a/components/ui/button.tsx
+++ b/components/ui/button.tsx
@@ -21,7 +21,7 @@ const buttonVariants = cva(
},
size: {
default: "h-10 px-4 py-2",
- sm: "h-9 rounded-md px-3",
+ sm: "h-8 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},