name: component-wrapper-architecture description: 包装shadcn/ui组件的最佳实践。适用于创建现有shadcn/ui组件的8位风格变体。
组件包装架构
8位组件包装shadcn/ui组件而不是替换它们。这种模式在添加复古风格的同时保持兼容性。
基本包装模式
结构:
- 使用别名导入基础组件
- 使用class-variance-authority定义变体
- 导出单独的props接口
- 使用ref prop(对于React 19,不使用forwardRef)
import { type VariantProps, cva } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { Button as ShadcnButton } from "@/components/ui/button";
import "@/components/ui/8bit/styles/retro.css";
export const buttonVariants = cva("", {
variants: {
font: {
normal: "",
retro: "retro",
},
variant: {
default: "bg-foreground",
// ...
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
export interface BitButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
ref?: React.Ref<HTMLButtonElement>;
}
function Button({ children, asChild, ...props }: BitButtonProps) {
const { variant, size, className, font } = props;
return (
<ShadcnButton
{...props}
className={cn(
"rounded-none active:translate-y-1 transition-transform",
className
)}
size={size}
variant={variant}
asChild={asChild}
>
{children}
</ShadcnButton>
);
}
重新导出基础组件
对于具有多个子组件的组件,重新导出未更改的部分:
import {
Dialog as ShadcnDialog,
DialogHeader as ShadcnDialogHeader,
DialogFooter as ShadcnDialogFooter,
DialogDescription as ShadcnDialogDescription,
} from "@/components/ui/dialog";
const Dialog = ShadcnDialog;
const DialogHeader = ShadcnDialogHeader;
const DialogFooter = ShadcnDialogFooter;
const DialogDescription = ShadcnDialogDescription;
export {
Dialog,
DialogHeader,
DialogFooter,
DialogDescription,
// ...自定义实现
};
卡片包装模式
使用外部包装器添加像素化边框,同时保留基础组件:
function Card({ className, font, ...props }: BitCardProps) {
return (
<div
className={cn(
"relative border-y-6 border-foreground dark:border-ring !p-0",
className
)}
>
<ShadcnCard
{...props}
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>
);
}
关键原则
- 别名导入 - 使用
as ShadcnComponent模式导入基础组件 - 空的cva基础 - 变体通常从空开始,依赖CSS进行样式化
- 单独的props接口 - 为TypeScript导出
BitComponentProps - React 19 ref - 使用
ref?: React.Ref<T>而不是forwardRef - rounded-none - 从基础组件中移除所有边框半径
- 传递props - 转发所有props,包括
size、variant、className - 条件复古 - 使用
font !== "normal" && "retro"模式
组件示例
components/ui/8bit/button.tsx- 带有像素边框的基本包装器components/ui/8bit/card.tsx- 带有外部包装器的卡片components/ui/8bit/dialog.tsx- 多子组件包装器