aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShreyans Jain <[email protected]>2025-09-04 16:50:55 -0700
committerShreyans Jain <[email protected]>2025-09-04 16:50:55 -0700
commit593bb2556abe16132e2db70474d859de24d525eb (patch)
tree881eddbb4ce88fbd1e197ca7be6e955f6dd5c7db
parentfeat: referrals (diff)
downloadsupermemory-shreyans/09-03-feat_referrals.tar.xz
supermemory-shreyans/09-03-feat_referrals.zip
-rw-r--r--apps/web/app/ref/[code]/page.tsx117
-rw-r--r--apps/web/app/ref/page.tsx113
-rw-r--r--apps/web/middleware.ts2
-rw-r--r--apps/web/next.config.ts68
-rw-r--r--bun.lock3
-rw-r--r--package.json1
-rw-r--r--packages/ui/text/heading.tsx63
-rw-r--r--packages/ui/text/label.tsx68
-rw-r--r--packages/ui/text/title.tsx65
9 files changed, 407 insertions, 93 deletions
diff --git a/apps/web/app/ref/[code]/page.tsx b/apps/web/app/ref/[code]/page.tsx
index 8d7bc6fe..9d3b9b87 100644
--- a/apps/web/app/ref/[code]/page.tsx
+++ b/apps/web/app/ref/[code]/page.tsx
@@ -11,13 +11,20 @@ import {
CardTitle,
} from "@ui/components/card"
import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar"
-import { ShareIcon, LoaderIcon } from "lucide-react"
+import { ShareIcon, LoaderIcon, ArrowRightIcon } from "lucide-react"
import Link from "next/link"
-import { useParams } from "next/navigation"
+import { useParams, useRouter } from "next/navigation"
+import { labelVariants } from "@ui/text/label"
+import { cn } from "@lib/utils"
+import { headingVariants } from "@ui/text/heading"
+import { titleVariants } from "@ui/text/title"
+import { useSession } from "@lib/auth"
export default function ReferralInvitePage() {
const params = useParams()
const code = params.code as string
+ const router = useRouter()
+ const session = useSession()
const {
data: referrerData,
@@ -48,6 +55,10 @@ export default function ReferralInvitePage() {
retry: 1,
})
+ if (session.data) {
+ router.push("/")
+ }
+
const referrer = referrerData?.referrer
if (isLoading) {
@@ -55,7 +66,13 @@ export default function ReferralInvitePage() {
<div className="min-h-screen flex items-center justify-center p-4 bg-[#0f1419]">
<div className="flex flex-col items-center gap-4">
<LoaderIcon className="w-8 h-8 text-orange-500 animate-spin" />
- <p className="text-white/60">Loading invitation...</p>
+ <p
+ className={cn(
+ labelVariants({ level: 1, weight: "regular", color: "muted" }),
+ )}
+ >
+ Loading invitation...
+ </p>
</div>
</div>
)
@@ -64,15 +81,22 @@ export default function ReferralInvitePage() {
if (error || !code) {
return (
<div className="min-h-screen flex items-center justify-center p-4 bg-[#0f1419]">
- <Card className="max-w-md w-full bg-[#1a1f2a] border-white/10">
+ <Card className="max-w-md w-full bg-sm-shark">
<CardHeader className="text-center">
<div className="mx-auto mb-4 w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center">
<ShareIcon className="w-8 h-8 text-red-500" />
</div>
- <CardTitle className="text-2xl font-bold text-white">
+ <CardTitle
+ className={cn(titleVariants({ level: 2, weight: "bold" }))}
+ >
Invalid Referral Link
</CardTitle>
- <CardDescription className="text-white/60 mt-2">
+ <CardDescription
+ className={cn(
+ labelVariants({ level: 1, weight: "regular", color: "muted" }),
+ "mt-2",
+ )}
+ >
{error instanceof Error ? error.message : "Invalid referral code"}
</CardDescription>
</CardHeader>
@@ -80,10 +104,17 @@ export default function ReferralInvitePage() {
<div className="space-y-4">
<div className="text-center">
<Link
- className="text-orange-500 hover:text-orange-400 text-sm underline"
+ className={cn(
+ labelVariants({
+ level: 1,
+ weight: "regular",
+ color: "default",
+ }),
+ "underline underline-offset-2 hover:opacity-50 transition-all",
+ )}
href="https://supermemory.ai"
>
- Learn more about supermemory
+ Learn more
</Link>
</div>
</div>
@@ -95,7 +126,7 @@ export default function ReferralInvitePage() {
return (
<div className="min-h-screen flex items-center justify-center p-4 bg-[#0f1419]">
- <Card className="max-w-md w-full bg-[#1a1f2a] border-white/10">
+ <Card className="max-w-md w-full bg-sm-shark">
<CardHeader className="text-center">
<div className="mx-auto mb-4">
<Avatar className="w-20 h-20">
@@ -105,10 +136,17 @@ export default function ReferralInvitePage() {
</AvatarFallback>
</Avatar>
</div>
- <CardTitle className="text-2xl font-bold text-white">
+ <CardTitle
+ className={cn(titleVariants({ level: 2, weight: "bold" }))}
+ >
You've been invited!
</CardTitle>
- <CardDescription className="text-white/60 mt-2">
+ <CardDescription
+ className={cn(
+ labelVariants({ level: 1, weight: "regular", color: "muted" }),
+ "mt-2",
+ )}
+ >
<span className="text-orange-400 font-semibold">
{referrer?.name}
</span>{" "}
@@ -117,29 +155,66 @@ export default function ReferralInvitePage() {
</CardHeader>
<CardContent>
<div className="space-y-4">
- <div className="bg-[#0f1419] rounded-lg p-4 border border-white/10">
- <h3 className="text-white font-semibold mb-2">
+ <div
+ className={
+ (cn(
+ headingVariants({
+ level: "h2",
+ weight: "medium",
+ }),
+ ),
+ "text-white text-center")
+ }
+ >
+ You and {referrer?.name} both get a free month of{" "}
+ <b>supermemory pro</b>
+ </div>
+ <div className="bg-sm-shark rounded-lg p-4 border border-white/10">
+ <h3
+ className={cn(headingVariants({ level: "h3", weight: "bold" }))}
+ >
What is supermemory?
</h3>
- <p className="text-white/70 text-sm leading-relaxed">
+ <p
+ className={cn(
+ labelVariants({
+ level: 1,
+ weight: "regular",
+ color: "muted",
+ }),
+ )}
+ >
supermemory is an AI-powered personal knowledge base that helps
you store, organize, and interact with all your digital
memories.
</p>
</div>
- <Link href={`/login?ref=${code}`} className="block">
- <Button className="w-full bg-orange-500 hover:bg-orange-600 text-white">
- Get Started
- </Button>
- </Link>
+ <Button
+ className={cn(
+ labelVariants({ level: 1, weight: "medium" }),
+ "w-full",
+ )}
+ asChild
+ >
+ <Link href={`/login?ref=${code}`}>
+ Get Started <ArrowRightIcon className="w-4 h-4" />
+ </Link>
+ </Button>
<div className="text-center">
<Link
- className="text-orange-500 hover:text-orange-400 text-sm underline"
+ className={cn(
+ labelVariants({
+ level: 1,
+ weight: "regular",
+ color: "default",
+ }),
+ "underline underline-offset-2 hover:opacity-50 transition-all",
+ )}
href="https://supermemory.ai"
>
- Learn more about supermemory
+ Learn more
</Link>
</div>
</div>
diff --git a/apps/web/app/ref/page.tsx b/apps/web/app/ref/page.tsx
index 78373693..ed10d0aa 100644
--- a/apps/web/app/ref/page.tsx
+++ b/apps/web/app/ref/page.tsx
@@ -1,56 +1,95 @@
-"use client";
+"use client"
-import { Button } from "@ui/components/button";
+import { Button } from "@ui/components/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
-} from "@ui/components/card";
-import { ShareIcon } from "lucide-react";
-import Link from "next/link";
+} from "@ui/components/card"
+import { ArrowRightIcon, HeartIcon, ShareIcon } from "lucide-react"
+import Link from "next/link"
+import { labelVariants } from "@ui/text/label"
+import { cn } from "@lib/utils"
+import { headingVariants } from "@ui/text/heading"
+import { titleVariants } from "@ui/text/title"
export default function ReferralHomePage() {
return (
<div className="min-h-screen flex items-center justify-center p-4 bg-[#0f1419]">
- <Card className="max-w-md w-full bg-[#1a1f2a] border-white/10">
- <CardHeader className="text-center">
- <div className="mx-auto mb-4 w-16 h-16 rounded-full bg-orange-500/10 flex items-center justify-center">
- <ShareIcon className="w-8 h-8 text-orange-500" />
+ <Card className="max-w-md w-full bg-sm-shark">
+ <CardHeader className="flex flex-col items-center gap-4">
+ <div className="mx-auto size-16 rounded-full bg-black/40 flex items-center justify-center">
+ <HeartIcon className="h-full" />
+ </div>
+ <div className="flex flex-col items-center gap-1">
+ <CardTitle
+ className={cn(titleVariants({ level: 3, weight: "bold" }))}
+ >
+ Missing Referral Code?
+ </CardTitle>
+ <CardDescription
+ className={cn(
+ labelVariants({
+ level: 1,
+ weight: "regular",
+ color: "muted",
+ }),
+ "text-center",
+ )}
+ >
+ It looks like you're missing a referral code. Get one from a
+ friend or join directly!
+ </CardDescription>
</div>
- <CardTitle className="text-2xl font-bold text-white">
- Missing Referral Code
- </CardTitle>
- <CardDescription className="text-white/60 mt-2">
- It looks like you're missing a referral code. Get one from a friend
- or join directly!
- </CardDescription>
</CardHeader>
- <CardContent>
- <div className="space-y-4">
- <div className="bg-[#0f1419] rounded-lg p-4 border border-white/10">
- <h3 className="text-white font-semibold mb-2">
- What is supermemory?
- </h3>
- <p className="text-white/70 text-sm leading-relaxed">
- supermemory is an AI-powered personal knowledge base that helps
- you store, organize, and interact with all your digital
- memories.
- </p>
- </div>
+ <CardContent className="flex flex-col gap-4">
+ <div className="bg-sm-shark rounded-lg p-4 border border-white/10">
+ <h3
+ className={cn(headingVariants({ level: "h3", weight: "bold" }))}
+ >
+ What is supermemory?
+ </h3>
+ <p
+ className={cn(
+ labelVariants({ level: 1, weight: "regular", color: "muted" }),
+ )}
+ >
+ supermemory is an AI-powered personal knowledge base that helps
+ you store, organize, and interact with all your digital memories.
+ </p>
+ </div>
+
+ <Button
+ className={cn(
+ labelVariants({ level: 1, weight: "medium" }),
+ "w-full",
+ )}
+ asChild
+ >
+ <Link href={"/login"}>
+ Get Started <ArrowRightIcon className="w-4 h-4" />
+ </Link>
+ </Button>
- <div className="text-center">
- <Link
- className="text-orange-500 hover:text-orange-400 text-sm underline"
- href="https://supermemory.ai"
- >
- Learn more about supermemory
- </Link>
- </div>
+ <div className="text-center">
+ <Link
+ className={cn(
+ labelVariants({
+ level: 1,
+ weight: "regular",
+ color: "default",
+ }),
+ "underline underline-offset-2 hover:opacity-50 transition-all",
+ )}
+ href="https://supermemory.ai"
+ >
+ Learn more
+ </Link>
</div>
</CardContent>
</Card>
</div>
- );
+ )
}
diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts
index 9862462d..59a3e856 100644
--- a/apps/web/middleware.ts
+++ b/apps/web/middleware.ts
@@ -47,6 +47,6 @@ export default async function middleware(request: Request) {
export const config = {
matcher: [
- "/((?!_next/static|_next/image|images|icon.png|monitoring|opengraph-image.png|ingest|api|login|api/emails).*)",
+ "/((?!_next/static|_next/image|images|icon.png|monitoring|opengraph-image.png|ingest|api|login|api/emails|ref).*)",
],
}
diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts
index a6ee01f3..5b9db494 100644
--- a/apps/web/next.config.ts
+++ b/apps/web/next.config.ts
@@ -1,63 +1,63 @@
-import { withSentryConfig } from "@sentry/nextjs";
-import type { NextConfig } from "next";
+import { withSentryConfig } from "@sentry/nextjs"
+import type { NextConfig } from "next"
const nextConfig: NextConfig = {
- experimental: {
- reactCompiler: true,
- viewTransition: true,
- },
- eslint: {
- ignoreDuringBuilds: true,
- },
- poweredByHeader: false,
- async rewrites() {
- return [
- {
- source: "/ingest/static/:path*",
- destination: "https://us-assets.i.posthog.com/static/:path*",
- },
- {
- source: "/ingest/:path*",
- destination: "https://us.i.posthog.com/:path*",
- },
- ];
- },
- skipTrailingSlashRedirect: true,
-};
+ experimental: {
+ reactCompiler: true,
+ viewTransition: true,
+ },
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
+ poweredByHeader: false,
+ async rewrites() {
+ return [
+ {
+ source: "/ingest/static/:path*",
+ destination: "https://us-assets.i.posthog.com/static/:path*",
+ },
+ {
+ source: "/ingest/:path*",
+ destination: "https://us.i.posthog.com/:path*",
+ },
+ ]
+ },
+ skipTrailingSlashRedirect: true,
+}
export default withSentryConfig(nextConfig, {
- // For all available options, see:
+ // For all available options, see:
// https://www.npmjs.com/package/@sentry/webpack-plugin#options
org: "supermemory",
- project: "consumer-app",
+ project: "consumer-app",
- // Only print logs for uploading source maps in CI
+ // Only print logs for uploading source maps in CI
silent: !process.env.CI,
- // For all available options, see:
+ // For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
- // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
+ // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: "/monitoring",
- // Automatically tree-shake Sentry logger statements to reduce bundle size
+ // Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
- // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
+ // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true,
-});
+})
-import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
+import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"
-initOpenNextCloudflareForDev(); \ No newline at end of file
+initOpenNextCloudflareForDev()
diff --git a/bun.lock b/bun.lock
index 00818471..06d015e1 100644
--- a/bun.lock
+++ b/bun.lock
@@ -20,6 +20,7 @@
"boxen": "^8.0.1",
"cloudflare": "^4.5.0",
"compromise": "^14.14.4",
+ "cva": "^1.0.0-beta.4",
"dedent": "^1.6.0",
"destr": "^2.0.5",
"drizzle-orm": "^0.44.3",
@@ -2002,6 +2003,8 @@
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+ "cva": ["[email protected]", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "typescript": ">= 4.5.5" }, "optionalPeers": ["typescript"] }, "sha512-F/JS9hScapq4DBVQXcK85l9U91M6ePeXoBMSp7vypzShoefUBxjQTo3g3935PUHgQd+IW77DjbPRIxugy4/GCQ=="],
+
"cytoscape": ["[email protected]", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="],
"cytoscape-cose-bilkent": ["[email protected]", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="],
diff --git a/package.json b/package.json
index a7cc83e7..e8e5a70b 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
"boxen": "^8.0.1",
"cloudflare": "^4.5.0",
"compromise": "^14.14.4",
+ "cva": "^1.0.0-beta.4",
"dedent": "^1.6.0",
"destr": "^2.0.5",
"drizzle-orm": "^0.44.3",
diff --git a/packages/ui/text/heading.tsx b/packages/ui/text/heading.tsx
new file mode 100644
index 00000000..04324421
--- /dev/null
+++ b/packages/ui/text/heading.tsx
@@ -0,0 +1,63 @@
+import { Root } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "cva"
+import { cn } from "../../lib/utils"
+
+export const headingVariants = cva({
+ base: "tracking-[-0.4px]",
+ variants: {
+ level: {
+ h1: "text-sm sm:text-base md:text-lg lg:text-xl leading-[32px]",
+ h2: "text-xs sm:text-sm md:text-base lg:text-lg leading-[30px]",
+ h3: "text-[0.625rem] sm:text-xs md:text-sm lg:text-base leading-[28px]",
+ h4: "text-[0.5rem] sm:text-[0.625rem] md:text-xs lg:text-sm leading-[24px]",
+ },
+ weight: {
+ bold: "font-bold",
+ medium: "font-medium",
+ },
+ },
+ defaultVariants: {
+ level: "h1",
+ weight: "medium",
+ },
+})
+
+export interface HeadingProps
+ extends React.HTMLAttributes<HTMLHeadingElement>,
+ VariantProps<typeof headingVariants> {
+ asChild?: boolean
+}
+
+export function Heading({
+ className,
+ level = "h1",
+ weight = "medium",
+ asChild,
+ ...props
+}: HeadingProps) {
+ const Comp = asChild ? Root : level
+ return (
+ <Comp
+ className={cn(headingVariants({ level, weight }), className)}
+ {...props}
+ />
+ )
+}
+
+// Export individual variant classes for compatibility
+export const headingH1Bold = () =>
+ headingVariants({ level: "h1", weight: "bold" })
+export const headingH1Medium = () =>
+ headingVariants({ level: "h1", weight: "medium" })
+export const headingH2Bold = () =>
+ headingVariants({ level: "h2", weight: "bold" })
+export const headingH2Medium = () =>
+ headingVariants({ level: "h2", weight: "medium" })
+export const headingH3Bold = () =>
+ headingVariants({ level: "h3", weight: "bold" })
+export const headingH3Medium = () =>
+ headingVariants({ level: "h3", weight: "medium" })
+export const headingH4Bold = () =>
+ headingVariants({ level: "h4", weight: "bold" })
+export const headingH4Medium = () =>
+ headingVariants({ level: "h4", weight: "medium" })
diff --git a/packages/ui/text/label.tsx b/packages/ui/text/label.tsx
new file mode 100644
index 00000000..5c20b12d
--- /dev/null
+++ b/packages/ui/text/label.tsx
@@ -0,0 +1,68 @@
+import { Root } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "cva"
+import { cn } from "../../lib/utils"
+
+export const labelVariants = cva({
+ base: "tracking-[-0.4px]",
+ variants: {
+ level: {
+ 1: "text-[0.875rem] md:text-[1rem] leading-[1.5rem]",
+ 2: "text-[0.25rem] sm:text-[0.375rem] md:text-[0.5rem] lg:text-[0.625rem] leading-[18px]",
+ 3: "text-[0.125rem] sm:text-[0.25rem] md:text-[0.375rem] lg:text-[0.5rem] leading-[16px] tracking-[-0.2px]",
+ },
+ weight: {
+ medium: "font-medium",
+ regular: "font-normal",
+ },
+ color: {
+ default: "",
+ muted: "text-sm-silver-chalice",
+ },
+ },
+ compoundVariants: [
+ {
+ level: [2, 3],
+ color: "default",
+ class: "text-sm-silver-chalice",
+ },
+ ],
+ defaultVariants: {
+ level: 1,
+ weight: "regular",
+ color: "default",
+ },
+})
+
+export interface LabelProps
+ extends Omit<React.HTMLAttributes<HTMLParagraphElement>, "color">,
+ VariantProps<typeof labelVariants> {
+ asChild?: boolean
+}
+
+export function Label({
+ className,
+ level = 1,
+ weight = "regular",
+ color = "default",
+ asChild,
+ ...props
+}: LabelProps) {
+ const Comp = asChild ? Root : "p"
+ return (
+ <Comp
+ className={cn(labelVariants({ level, weight, color }), className)}
+ {...props}
+ />
+ )
+}
+
+// Export individual variant classes for compatibility
+export const label1Medium = () => labelVariants({ level: 1, weight: "medium" })
+export const label1Regular = () =>
+ labelVariants({ level: 1, weight: "regular" })
+export const label2Medium = () => labelVariants({ level: 2, weight: "medium" })
+export const label2Regular = () =>
+ labelVariants({ level: 2, weight: "regular" })
+export const label3Medium = () => labelVariants({ level: 3, weight: "medium" })
+export const label3Regular = () =>
+ labelVariants({ level: 3, weight: "regular" })
diff --git a/packages/ui/text/title.tsx b/packages/ui/text/title.tsx
new file mode 100644
index 00000000..5a6c5f5a
--- /dev/null
+++ b/packages/ui/text/title.tsx
@@ -0,0 +1,65 @@
+import { Root } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "cva"
+import { cn } from "../../lib/utils"
+
+export const titleVariants = cva({
+ base: "tracking-[-0.4px]",
+ variants: {
+ level: {
+ 1: "text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-[70px] tracking-[-0.8px]",
+ 2: "text-lg sm:text-xl md:text-2xl lg:text-3xl leading-[48px]",
+ 3: "text-base sm:text-lg md:text-xl lg:text-2xl leading-[40px]",
+ },
+ weight: {
+ bold: "font-bold",
+ medium: "font-medium",
+ },
+ },
+ compoundVariants: [
+ {
+ level: 2,
+ weight: "medium",
+ class: "leading-[32px] md:leading-[48px]",
+ },
+ ],
+ defaultVariants: {
+ level: 1,
+ weight: "medium",
+ },
+})
+
+export interface TitleProps
+ extends React.HTMLAttributes<HTMLHeadingElement>,
+ VariantProps<typeof titleVariants> {
+ asChild?: boolean
+}
+
+export function Title({
+ className,
+ level = 1,
+ weight = "medium",
+ asChild,
+ ...props
+}: TitleProps) {
+ const levelMap = {
+ 1: "h1",
+ 2: "h2",
+ 3: "h3",
+ } as const
+
+ const Comp = asChild ? Root : levelMap[level]
+ return (
+ <Comp
+ className={cn(titleVariants({ level, weight }), className)}
+ {...props}
+ />
+ )
+}
+
+// Export individual variant classes for compatibility
+export const title1Bold = () => titleVariants({ level: 1, weight: "bold" })
+export const title1Medium = () => titleVariants({ level: 1, weight: "medium" })
+export const title2Bold = () => titleVariants({ level: 2, weight: "bold" })
+export const title2Medium = () => titleVariants({ level: 2, weight: "medium" })
+export const title3Bold = () => titleVariants({ level: 3, weight: "bold" })
+export const title3Medium = () => titleVariants({ level: 3, weight: "medium" })