Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/blocks/gaming/gaming.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ChapterIntro from "@/components/ui/8bit/blocks/chapter-intro";
import DialogueBox from "@/components/ui/8bit/blocks/dialogue-box";
import DifficultySelect from "@/components/ui/8bit/blocks/difficulty-select";
import GameOver from "@/components/ui/8bit/blocks/game-over";
Expand All @@ -13,6 +14,32 @@ import { OpenInV0Button } from "../../docs/components/open-in-v0-button";
export default function GamingBlocks() {
return (
<div className="flex flex-col gap-10">
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px]">
<div className="flex flex-col md:flex-row gap-2 items-center justify-between">
<h2 className="text-sm text-muted-foreground sm:pl-3">
Chapter intro
</h2>

<div className="flex flex-col md:flex-row items-center gap-2">
<CopyCommandButton
command="npx shadcn@latest add 8bit-chapter-intro"
copyCommand={`pnpm dlx shadcn@canary add ${process.env.NEXT_PUBLIC_BASE_URL}/r/8bit-chapter-intro.json`}
/>
<OpenInV0Button name="8bit-chapter-intro" className="w-fit" />
</div>
</div>

<ChapterIntro
className="w-full md:w-[800px] mx-auto text-white"
title="LEVEL 1: FIERY SKELETONS"
subtitle="Defeat the skeletons to unlock the gate."
backgroundSrc="/images/fiery-skeletons.png"
height="md"
align="center"
darken={0.5}
/>
</div>

<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px]">
<div className="flex flex-col md:flex-row gap-2 items-center justify-between">
<h2 className="text-sm text-muted-foreground sm:pl-3">Main menu</h2>
Expand Down
23 changes: 10 additions & 13 deletions components/examples/component-showcase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Textarea } from "@/components/ui/8bit/textarea";

import { CommandExample } from "@/components/examples/command";

import ChapterIntro from "../ui/8bit/blocks/chapter-intro";
import DifficultySelect from "../ui/8bit/blocks/difficulty-select";
import GameOver from "../ui/8bit/blocks/game-over";
import GameProgress from "../ui/8bit/blocks/game-progress";
Expand Down Expand Up @@ -109,19 +110,15 @@ export default function ComponentShowcase() {
<div className="flex flex-col gap-4 lg:col-span-2">
<GameOver />

{/* <Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-sm font-medium">
Total visitors in the last 6 months
</CardTitle>
<Users className="size-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<ChartAreaStep />
</CardContent>
</Card> */}

<LoginForm />
<ChapterIntro
className="w-full md:w-full mx-auto text-white"
title="LEVEL 1: SKELETONS"
subtitle="Defeat the skeletons to unlock the gate."
backgroundSrc="/images/fiery-skeletons.png"
height="md"
align="center"
darken={0.5}
/>

<Skeleton className="w-full h-full flex flex-col justify-center items-center">
Skeleton
Expand Down
89 changes: 89 additions & 0 deletions components/ui/8bit/blocks/chapter-intro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { cn } from "@/lib/utils";

import { Card, CardContent } from "@/components/ui/8bit/card";

export interface ChapterIntroProps extends React.ComponentProps<"div"> {
title: string;
subtitle?: string;
backgroundSrc?: string;
align?: "left" | "center" | "right";
height?: "sm" | "md" | "lg";
darken?: number;
}

export default function ChapterIntro({
className,
title,
subtitle,
backgroundSrc = "/placeholder.svg",
align = "center",
height = "md",
darken = 0.5,
...props
}: ChapterIntroProps) {
const heightClass =
height === "lg"
? "min-h-[420px] md:min-h-[640px]"
: height === "sm"
? "min-h-[240px] md:min-h-[360px]"
: "min-h-[320px] md:min-h-[480px]";

const alignClass =
align === "left"
? "justify-start text-left"
: align === "right"
? "justify-end text-right"
: "justify-center text-center";

return (
<Card className={cn(className)} {...props}>
<CardContent className={cn("relative p-0")}>
<div className={cn("relative w-full", heightClass)}>
{/* Background image */}
<img
src={backgroundSrc}
alt=""
className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.7]"
style={{ imageRendering: "pixelated" }}
/>

{/* Darken/gradient overlay for readability */}
<div
className="absolute inset-0 bg-background/60 mix-blend-multiply"
style={{ opacity: darken }}
aria-hidden="true"
/>

{/* Cinematic letterbox bars */}
<div
className="absolute top-0 left-0 right-0 h-6 md:h-10 bg-secondary/40"
aria-hidden="true"
/>
<div
className="absolute bottom-0 left-0 right-0 h-6 md:h-10 bg-secondary/80"
aria-hidden="true"
/>

{/* Content overlay */}
<div
className={cn(
"relative z-10 flex h-full items-center p-8 md:p-12",
alignClass
)}
>
<div className="mx-auto max-w-3xl">
<h1 className="text-2xl md:text-4xl lg:text-5xl font-bold leading-tight drop-shadow-[0_3px_0_rgba(0,0,0,0.8)]">
{title}
</h1>
{subtitle && (
<p className="mt-4 text-xs md:text-base text-secondary/90 drop-shadow-[0_2px_0_rgba(0,0,0,0.8)]">
{subtitle}
</p>
)}
</div>
</div>
</div>
</CardContent>
</Card>
);
}
Binary file added public/images/fiery-skeletons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions public/r/8bit-chapter-intro.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "8bit-chapter-intro",
"type": "registry:component",
"title": "8-bit Chapter Intro",
"description": "A cinematic chapter/level intro with pixel art background.",
"registryDependencies": [
"card"
],
"files": [
{
"path": "components/ui/8bit/blocks/chapter-intro.tsx",
"content": "import { cn } from \"@/lib/utils\";\n\nimport { Card, CardContent } from \"@/components/ui/8bit/card\";\n\nexport interface ChapterIntroProps extends React.ComponentProps<\"div\"> {\n title: string;\n subtitle?: string;\n backgroundSrc?: string;\n align?: \"left\" | \"center\" | \"right\";\n height?: \"sm\" | \"md\" | \"lg\";\n darken?: number;\n}\n\nexport default function ChapterIntro({\n className,\n title,\n subtitle,\n backgroundSrc = \"/placeholder.svg\",\n align = \"center\",\n height = \"md\",\n darken = 0.5,\n ...props\n}: ChapterIntroProps) {\n const heightClass =\n height === \"lg\"\n ? \"min-h-[420px] md:min-h-[640px]\"\n : height === \"sm\"\n ? \"min-h-[240px] md:min-h-[360px]\"\n : \"min-h-[320px] md:min-h-[480px]\";\n\n const alignClass =\n align === \"left\"\n ? \"justify-start text-left\"\n : align === \"right\"\n ? \"justify-end text-right\"\n : \"justify-center text-center\";\n\n return (\n <Card className={cn(className)} {...props}>\n <CardContent className={cn(\"relative p-0\")}>\n <div className={cn(\"relative w-full\", heightClass)}>\n {/* Background image */}\n <img\n src={backgroundSrc}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover dark:brightness-[0.7]\"\n style={{ imageRendering: \"pixelated\" }}\n />\n\n {/* Darken/gradient overlay for readability */}\n <div\n className=\"absolute inset-0 bg-background/60 mix-blend-multiply\"\n style={{ opacity: darken }}\n aria-hidden=\"true\"\n />\n\n {/* Cinematic letterbox bars */}\n <div\n className=\"absolute top-0 left-0 right-0 h-6 md:h-10 bg-secondary/40\"\n aria-hidden=\"true\"\n />\n <div\n className=\"absolute bottom-0 left-0 right-0 h-6 md:h-10 bg-secondary/80\"\n aria-hidden=\"true\"\n />\n\n {/* Content overlay */}\n <div\n className={cn(\n \"relative z-10 flex h-full items-center p-8 md:p-12\",\n alignClass\n )}\n >\n <div className=\"mx-auto max-w-3xl\">\n <h1 className=\"text-2xl md:text-4xl lg:text-5xl font-bold leading-tight drop-shadow-[0_3px_0_rgba(0,0,0,0.8)]\">\n {title}\n </h1>\n {subtitle && (\n <p className=\"mt-4 text-xs md:text-base text-secondary/90 drop-shadow-[0_2px_0_rgba(0,0,0,0.8)]\">\n {subtitle}\n </p>\n )}\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n );\n}\n",
"type": "registry:component",
"target": "components/ui/8bit/blocks/chapter-intro.tsx"
},
{
"path": "components/ui/8bit/styles/retro.css",
"content": "@import url(\"https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap\");\n\n.retro {\n font-family:\n \"Press Start 2P\",\n system-ui,\n -apple-system,\n sans-serif;\n line-height: 1.5;\n letter-spacing: 0.5px;\n}\n",
"type": "registry:component",
"target": "components/ui/8bit/styles/retro.css"
},
{
"path": "components/ui/8bit/card.tsx",
"content": "import { type VariantProps, cva } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n Card as ShadcnCard,\n CardAction as ShadcnCardAction,\n CardContent as ShadcnCardContent,\n CardDescription as ShadcnCardDescription,\n CardFooter as ShadcnCardFooter,\n CardHeader as ShadcnCardHeader,\n CardTitle as ShadcnCardTitle,\n} from \"@/components/ui/card\";\n\nimport \"./styles/retro.css\";\n\nexport const cardVariants = cva(\"\", {\n variants: {\n font: {\n normal: \"\",\n retro: \"retro\",\n },\n },\n defaultVariants: {\n font: \"retro\",\n },\n});\n\nexport interface BitCardProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof cardVariants> {\n asChild?: boolean;\n}\n\nfunction Card({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <div\n className={cn(\n \"relative border-y-6 border-foreground dark:border-ring !p-0\",\n className\n )}\n >\n <ShadcnCard\n {...props}\n className={cn(\n \"rounded-none border-0 !w-full\",\n font !== \"normal\" && \"retro\",\n className\n )}\n />\n\n <div\n className=\"absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none\"\n aria-hidden=\"true\"\n />\n </div>\n );\n}\n\nfunction CardHeader({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardHeader\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardTitle({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardTitle\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardDescription({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardDescription\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardAction({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardAction\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardContent({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardContent\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardFooter({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardFooter\n data-slot=\"card-footer\"\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n};\n",
"type": "registry:component",
"target": "components/ui/8bit/card.tsx"
}
Comment on lines +24 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Confirm that base Card actually exports CardAction

Shadcn’s default Card API usually does not include CardAction; if your local components/ui/card adds it, all good—otherwise this will break imports.

Run this script to verify the export:


🏁 Script executed:

#!/bin/bash
# Verify whether components/ui/card exports CardAction
fd -t f -a 'card.*\.(t|j)sx?$' components | xargs -I{} rg -n -C2 -P 'export\s+\{[^}]*CardAction[^}]*\}' {}

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Locate card module(s) under components/ui and check for CardAction exports
echo "=== Listing all card-related files in components/ui ==="
fd -HI 'card.*\.(ts|tsx)' components/ui

echo -e "\n=== Searching each file for CardAction export or definition ==="
fd -HI 'card.*\.(ts|tsx)' components/ui | while read -r file; do
  echo -e "\n---> $file"
  rg -nC2 'export\s+\{[^}]*CardAction[^}]*\}' "$file"
  rg -nC2 'export\s+(function|const)\s+CardAction' "$file"
done

Length of output: 1480


Export or Remove CardAction to Prevent Broken Imports

The file components/ui/card.tsx does not export CardAction, so importing CardAction as ShadcnCardAction in public/r/8bit-chapter-intro.json (targeting components/ui/8bit/card.tsx) will break at runtime.

• components/ui/card.tsx – add CardAction to the named exports (or define and export it)
• components/ui/8bit/card.tsx – if you don’t intend to extend CardAction, remove its import and usages instead

🤖 Prompt for AI Agents
In public/r/8bit-chapter-intro.json around lines 24 to 28: the 8bit variant
imports ShadcnCardAction from components/ui/card.tsx but that file does not
export CardAction, causing runtime breakage; either add and export CardAction
from components/ui/card.tsx (so the 8bit wrapper can extend it) or remove the
import and any CardAction component/usage from components/ui/8bit/card.tsx and
replace with a supported exported component (e.g., use a plain div or
ShadcnCard’s existing exported slots); update imports accordingly and run a
quick build to verify no unresolved imports remain.

🛠️ Refactor suggestion

Do not forward variant props (e.g., font) to DOM; destructure them out before spreading

Currently each wrapper passes {...props} down to shadcn Card primitives; this leaks "font" onto DOM nodes, creating invalid attributes and potential hydration/warning issues. Destructure and pass the rest.

Apply this diff within card.tsx:

-function Card({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function Card({ className, font, ...rest }: BitCardProps) {
@@
-      <ShadcnCard
-        {...props}
+      <ShadcnCard
+        {...rest}
         className={cn(
           "rounded-none border-0 !w-full",
           font !== "normal" && "retro",
           className
         )}
       />
@@
-function CardHeader({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function CardHeader({ className, font, ...rest }: BitCardProps) {
@@
-    <ShadcnCardHeader
-      className={cn(font !== "normal" && "retro", className)}
-      {...props}
-    />
+    <ShadcnCardHeader
+      className={cn(font !== "normal" && "retro", className)}
+      {...rest}
+    />
@@
-function CardTitle({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function CardTitle({ className, font, ...rest }: BitCardProps) {
@@
-    <ShadcnCardTitle
-      className={cn(font !== "normal" && "retro", className)}
-      {...props}
-    />
+    <ShadcnCardTitle
+      className={cn(font !== "normal" && "retro", className)}
+      {...rest}
+    />
@@
-function CardDescription({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function CardDescription({ className, font, ...rest }: BitCardProps) {
@@
-    <ShadcnCardDescription
-      className={cn(font !== "normal" && "retro", className)}
-      {...props}
-    />
+    <ShadcnCardDescription
+      className={cn(font !== "normal" && "retro", className)}
+      {...rest}
+    />
@@
-function CardAction({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function CardAction({ className, font, ...rest }: BitCardProps) {
@@
-    <ShadcnCardAction
-      className={cn(font !== "normal" && "retro", className)}
-      {...props}
-    />
+    <ShadcnCardAction
+      className={cn(font !== "normal" && "retro", className)}
+      {...rest}
+    />
@@
-function CardContent({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function CardContent({ className, font, ...rest }: BitCardProps) {
@@
-    <ShadcnCardContent
-      className={cn(font !== "normal" && "retro", className)}
-      {...props}
-    />
+    <ShadcnCardContent
+      className={cn(font !== "normal" && "retro", className)}
+      {...rest}
+    />
@@
-function CardFooter({ ...props }: BitCardProps) {
-  const { className, font } = props;
+function CardFooter({ className, font, ...rest }: BitCardProps) {
@@
-    <ShadcnCardFooter
+    <ShadcnCardFooter
       data-slot="card-footer"
-      className={cn(font !== "normal" && "retro", className)}
-      {...props}
-    />
+      className={cn(font !== "normal" && "retro", className)}
+      {...rest}
+    />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"path": "components/ui/8bit/card.tsx",
"content": "import { type VariantProps, cva } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n Card as ShadcnCard,\n CardAction as ShadcnCardAction,\n CardContent as ShadcnCardContent,\n CardDescription as ShadcnCardDescription,\n CardFooter as ShadcnCardFooter,\n CardHeader as ShadcnCardHeader,\n CardTitle as ShadcnCardTitle,\n} from \"@/components/ui/card\";\n\nimport \"./styles/retro.css\";\n\nexport const cardVariants = cva(\"\", {\n variants: {\n font: {\n normal: \"\",\n retro: \"retro\",\n },\n },\n defaultVariants: {\n font: \"retro\",\n },\n});\n\nexport interface BitCardProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof cardVariants> {\n asChild?: boolean;\n}\n\nfunction Card({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <div\n className={cn(\n \"relative border-y-6 border-foreground dark:border-ring !p-0\",\n className\n )}\n >\n <ShadcnCard\n {...props}\n className={cn(\n \"rounded-none border-0 !w-full\",\n font !== \"normal\" && \"retro\",\n className\n )}\n />\n\n <div\n className=\"absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none\"\n aria-hidden=\"true\"\n />\n </div>\n );\n}\n\nfunction CardHeader({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardHeader\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardTitle({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardTitle\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardDescription({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardDescription\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardAction({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardAction\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardContent({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardContent\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nfunction CardFooter({ ...props }: BitCardProps) {\n const { className, font } = props;\n\n return (\n <ShadcnCardFooter\n data-slot=\"card-footer\"\n className={cn(font !== \"normal\" && \"retro\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n};\n",
"type": "registry:component",
"target": "components/ui/8bit/card.tsx"
}
import { type VariantProps, cva } from "class-variance-authority";
import { cn } from "@/lib/utils";
import {
Card as ShadcnCard,
CardAction as ShadcnCardAction,
CardContent as ShadcnCardContent,
CardDescription as ShadcnCardDescription,
CardFooter as ShadcnCardFooter,
CardHeader as ShadcnCardHeader,
CardTitle as ShadcnCardTitle,
} from "@/components/ui/card";
import "./styles/retro.css";
export const cardVariants = cva("", {
variants: {
font: {
normal: "",
retro: "retro",
},
},
defaultVariants: {
font: "retro",
},
});
export interface BitCardProps
extends React.ComponentProps<"div">,
VariantProps<typeof cardVariants> {
asChild?: boolean;
}
function Card({ className, font, ...rest }: BitCardProps) {
return (
<div
className={cn(
"relative border-y-6 border-foreground dark:border-ring !p-0",
className
)}
>
<ShadcnCard
{...rest}
className={cn(
"rounded-none border-0 !w-full",
font !== "normal" && "retro",
className
)}
/>
<div
className="absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none"
aria-hidden="true"
/>
</div>
);
}
function CardHeader({ className, font, ...rest }: BitCardProps) {
return (
<ShadcnCardHeader
className={cn(font !== "normal" && "retro", className)}
{...rest}
/>
);
}
function CardTitle({ className, font, ...rest }: BitCardProps) {
return (
<ShadcnCardTitle
className={cn(font !== "normal" && "retro", className)}
{...rest}
/>
);
}
function CardDescription({ className, font, ...rest }: BitCardProps) {
return (
<ShadcnCardDescription
className={cn(font !== "normal" && "retro", className)}
{...rest}
/>
);
}
function CardAction({ className, font, ...rest }: BitCardProps) {
return (
<ShadcnCardAction
className={cn(font !== "normal" && "retro", className)}
{...rest}
/>
);
}
function CardContent({ className, font, ...rest }: BitCardProps) {
return (
<ShadcnCardContent
className={cn(font !== "normal" && "retro", className)}
{...rest}
/>
);
}
function CardFooter({ className, font, ...rest }: BitCardProps) {
return (
<ShadcnCardFooter
data-slot="card-footer"
className={cn(font !== "normal" && "retro", className)}
{...rest}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};
🤖 Prompt for AI Agents
In public/r/8bit-chapter-intro.json (components/ui/8bit/card.tsx) around lines
24-28, wrapper components are forwarding the variant prop "font" into DOM by
spreading {...props}; destructure variant props out (e.g., const { className,
font, ...rest } = props) and spread only the remaining props ({...rest}) into
the Shadcn primitives (and not into DOM elements), update every function (Card,
CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter) to
stop passing font to children.

]
}
2 changes: 1 addition & 1 deletion public/r/8bit-difficulty-select.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"files": [
{
"path": "components/ui/8bit/blocks/difficulty-select.tsx",
"content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { Button } from \"@/components/ui/8bit/button\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/8bit/card\";\n\nexport type Difficulty = \"easy\" | \"normal\" | \"hard\";\n\nexport interface DifficultySelectProps extends React.ComponentProps<\"div\"> {\n value?: Difficulty;\n defaultValue?: Difficulty;\n onChange?: (value: Difficulty) => void;\n title?: string;\n description?: string;\n vertical?: boolean;\n}\n\nexport default function DifficultySelect({\n className,\n value,\n defaultValue = \"normal\",\n onChange,\n title = \"Select Difficulty\",\n description,\n vertical = true,\n ...props\n}: DifficultySelectProps) {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] =\n React.useState<Difficulty>(defaultValue);\n\n const selected = isControlled ? (value as Difficulty) : internalValue;\n\n const setSelected = React.useCallback(\n (next: Difficulty) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange]\n );\n\n return (\n <Card className={cn(className)} {...props}>\n <CardHeader className=\"flex flex-col items-center justify-center gap-2\">\n <CardTitle>{title}</CardTitle>\n {description ? <CardDescription>{description}</CardDescription> : null}\n </CardHeader>\n <CardContent>\n <div\n className={cn(\"gap-4\", vertical ? \"flex flex-col\" : \"flex flex-row\")}\n >\n <Button\n className=\"flex items-center justify-center\"\n variant={selected === \"easy\" ? \"default\" : \"secondary\"}\n onClick={() => setSelected(\"easy\")}\n >\n EASY\n </Button>\n <Button\n className=\"flex items-center justify-center\"\n variant={selected === \"normal\" ? \"default\" : \"secondary\"}\n onClick={() => setSelected(\"normal\")}\n >\n NORMAL\n </Button>\n <Button\n className=\"flex items-center justify-center\"\n variant={selected === \"hard\" ? \"default\" : \"secondary\"}\n onClick={() => setSelected(\"hard\")}\n >\n HARD\n </Button>\n </div>\n </CardContent>\n </Card>\n );\n}\n",
"content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { Button } from \"@/components/ui/8bit/button\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/8bit/card\";\n\nexport type Difficulty = \"easy\" | \"normal\" | \"hard\";\n\nexport interface DifficultySelectProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n value?: Difficulty;\n defaultValue?: Difficulty;\n onChange?: (value: Difficulty) => void;\n title?: string;\n description?: string;\n vertical?: boolean;\n}\n\nexport default function DifficultySelect({\n className,\n value,\n defaultValue = \"normal\",\n onChange,\n title = \"Select Difficulty\",\n description,\n vertical = true,\n ...props\n}: DifficultySelectProps) {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] =\n React.useState<Difficulty>(defaultValue);\n\n const selected = isControlled ? (value as Difficulty) : internalValue;\n\n const setSelected = React.useCallback(\n (next: Difficulty) => {\n if (!isControlled) setInternalValue(next);\n onChange?.(next);\n },\n [isControlled, onChange]\n );\n\n return (\n <Card className={cn(className)} {...props}>\n <CardHeader className=\"flex flex-col items-center justify-center gap-2\">\n <CardTitle>{title}</CardTitle>\n {description ? <CardDescription>{description}</CardDescription> : null}\n </CardHeader>\n <CardContent>\n <div\n className={cn(\"gap-4\", vertical ? \"flex flex-col\" : \"flex flex-row\")}\n >\n <Button\n className=\"flex items-center justify-center\"\n variant={selected === \"easy\" ? \"default\" : \"secondary\"}\n onClick={() => setSelected(\"easy\")}\n >\n EASY\n </Button>\n <Button\n className=\"flex items-center justify-center\"\n variant={selected === \"normal\" ? \"default\" : \"secondary\"}\n onClick={() => setSelected(\"normal\")}\n >\n NORMAL\n </Button>\n <Button\n className=\"flex items-center justify-center\"\n variant={selected === \"hard\" ? \"default\" : \"secondary\"}\n onClick={() => setSelected(\"hard\")}\n >\n HARD\n </Button>\n </div>\n </CardContent>\n </Card>\n );\n}\n",
"type": "registry:component",
"target": "components/ui/8bit/blocks/difficulty-select.tsx"
},
Expand Down
24 changes: 24 additions & 0 deletions registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@
"name": "8bitcn",
"homepage": "https://8bitcn.com",
"items": [
{
"name": "8bit-chapter-intro",
"type": "registry:component",
"title": "8-bit Chapter Intro",
"description": "A cinematic chapter/level intro with pixel art background.",
"registryDependencies": ["card"],
"files": [
{
"path": "components/ui/8bit/blocks/chapter-intro.tsx",
"type": "registry:component",
"target": "components/ui/8bit/blocks/chapter-intro.tsx"
},
{
"path": "components/ui/8bit/styles/retro.css",
"type": "registry:component",
"target": "components/ui/8bit/styles/retro.css"
},
{
"path": "components/ui/8bit/card.tsx",
"type": "registry:component",
"target": "components/ui/8bit/card.tsx"
}
]
},
{
"name": "8bit-button",
"type": "registry:component",
Expand Down