-
-
Notifications
You must be signed in to change notification settings - Fork 70
feat(registry) add new chapter intro component block #327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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> | ||
| ); | ||
| } |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" | ||
| } | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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:
Length of output: 112
🏁 Script executed:
Length of output: 1480
Export or Remove
CardActionto Prevent Broken ImportsThe file
components/ui/card.tsxdoes not exportCardAction, so importingCardAction as ShadcnCardActioninpublic/r/8bit-chapter-intro.json(targetingcomponents/ui/8bit/card.tsx) will break at runtime.• components/ui/card.tsx – add
CardActionto 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
🛠️ 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:
📝 Committable suggestion
🤖 Prompt for AI Agents