本帖最后由 thepoy 于 2026-4-23 12:58 编辑
shadcn是用React框架开发,但它使用的底层组件库不是由其开发者完全控制,因此虽然很好用,但也存在着许多大大小小的bug,只能通过上游来修复。
所以我用solidjs重写了这个组件库,重写时主要参考shadcn的样式而不是其实现方式。
除了图标库外没有使用任何第三方组件库,一些shadcn依赖的上游组件库(比如sonner)也直接移植。
目前开发的主要目标是样式移植,功能缺陷和bug没有完全修复,准备在全部组件完全移植后再修复其中的bug(比如sonner本身存在着严重的bug),不支持服务端渲染。
与shadcn类似,也可以直接复制组件源码到你自己的前端项目中使用,但同时也支持通过npm安装组件库在你的项目中导入本组件库的组件。
已经将移植完成的组件制成成了简单的示例,已静态部署:
Button组件的示例页:

组件库名称为@neut/ui,已经上传到npm:https://www.npmjs.com/package/@neut/ui
组件库源码:https://github.com/thep0y/neut
下面是组件库结构,出于可维护性其于单一职责原则设计:
src
└── components
└── accordion
└── Accordion
└── Accordion.context.tsx
└── Accordion.styles.ts
└── Accordion.tsx
└── Accordion.types.ts
└── index.ts
└── useAccordion.ts
└── AccordionContent
└── AccordionContent.styles.ts
└── AccordionContent.tsx
└── AccordionContent.types.ts
└── index.ts
└── AccordionItem
└── AccordionItem.context.tsx
└── AccordionItem.styles.ts
└── AccordionItem.tsx
└── AccordionItem.types.ts
└── index.ts
└── AccordionTrigger
└── AccordionTrigger.styles.ts
└── AccordionTrigger.tsx
└── AccordionTrigger.types.ts
└── index.ts
└── index.ts
└── aspect-ratio
└── AspectRatio.styles.ts
└── AspectRatio.tsx
└── AspectRatio.types.ts
└── index.ts
└── avatar
└── Avatar
└── Avatar.context.ts
└── Avatar.tsx
└── Avatar.types.ts
└── index.ts
└── AvatarBadge
└── AvatarBadge.tsx
└── AvatarBadge.types.ts
└── index.ts
└── AvatarFallback
└── AvatarFallback.tsx
└── AvatarFallback.types.ts
└── index.ts
└── AvatarGroup
└── AvatarGroup.tsx
└── AvatarGroup.types.ts
└── index.ts
└── AvatarGroupCount
└── AvatarGroupCount.tsx
└── AvatarGroupCount.types.ts
└── index.ts
└── AvatarImage
└── AvatarImage.tsx
└── AvatarImage.types.ts
└── index.ts
└── index.ts
└── badge
└── Badge.styles.ts
└── Badge.tsx
└── Badge.types.ts
└── index.ts
└── button
└── Button.styles.ts
└── Button.tsx
└── Button.types.ts
└── index.ts
└── button-group
└── ButtonGroup
└── ButtonGroup.styles.ts
└── ButtonGroup.tsx
└── ButtonGroup.types.ts
└── index.ts
└── ButtonGroupSeparator
└── ButtonGroupSeparator.styles.ts
└── ButtonGroupSeparator.tsx
└── ButtonGroupSeparator.types.ts
└── index.ts
└── ButtonGroupText
└── ButtonGroupText.styles.ts
└── ButtonGroupText.tsx
└── ButtonGroupText.types.ts
└── index.ts
└── index.ts
└── card
└── Card
└── Card.styles.ts
└── Card.tsx
└── Card.types.ts
└── index.ts
└── CardAction
└── CardAction.styles.ts
└── CardAction.tsx
└── CardAction.types.ts
└── index.ts
└── CardContent
└── CardContent.styles.ts
└── CardContent.tsx
└── CardContent.types.ts
└── index.ts
└── CardDescription
└── CardDescription.styles.ts
└── CardDescription.tsx
└── CardDescription.types.ts
└── index.ts
└── CardFooter
└── CardFooter.styles.ts
└── CardFooter.tsx
└── CardFooter.types.ts
└── index.ts
└── CardHeader
└── CardHeader.styles.ts
└── CardHeader.tsx
└── CardHeader.types.ts
└── index.ts
└── CardTitle
└── CardTitle.styles.ts
└── CardTitle.tsx
└── CardTitle.types.ts
└── index.ts
└── index.ts
└── carousel
└── Carousel
└── Carousel.context.tsx
└── Carousel.styles.ts
└── Carousel.tsx
└── Carousel.types.ts
└── Carousel.utils.ts
└── index.ts
└── useAutoPlay.ts
└── useCarousel.ts
└── useKeyboardNavigation.ts
└── CarouselContent
└── CarouselContent.styles.ts
└── CarouselContent.tsx
└── CarouselContent.types.ts
└── index.ts
└── useItemSizeMeasure.ts
└── CarouselDots
└── CarouselDots.tsx
└── CarouselDots.types.ts
└── index.ts
└── CarouselItem
└── CarouselItem.tsx
└── CarouselItem.types.ts
└── index.ts
└── CarouselNext
└── CarouselNext.tsx
└── CarouselNext.types.ts
└── index.ts
└── CarouselPrevious
└── CarouselPrevious.tsx
└── CarouselPrevious.types.ts
└── index.ts
└── index.ts
└── field
└── Field
└── Field.styles.ts
└── Field.tsx
└── Field.types.ts
└── index.ts
└── FieldContent
└── FieldContent.tsx
└── FieldContent.types.ts
└── index.ts
└── FieldDescription
└── FieldDescription.tsx
└── FieldDescription.types.ts
└── index.ts
└── FieldError
└── FieldError.tsx
└── FieldError.types.ts
└── index.ts
└── useFieldError.ts
└── FieldGroup
└── FieldGroup.tsx
└── FieldGroup.types.ts
└── index.ts
└── FieldLabel
└── FieldLabel.tsx
└── FieldLabel.types.ts
└── index.ts
└── FieldLegend
└── FieldLegend.tsx
└── FieldLegend.types.ts
└── index.ts
└── FieldSeparator
└── FieldSeparator.tsx
└── FieldSeparator.types.ts
└── index.ts
└── FieldSet
└── FieldSet.tsx
└── FieldSet.types.ts
└── index.ts
└── FieldTitle
└── FieldTitle.tsx
└── FieldTitle.types.ts
└── index.ts
└── index.ts
└── image
└── Image.config.ts
└── Image.styles.ts
└── Image.tsx
└── Image.types.ts
└── Image.utils.ts
└── ImageElement.tsx
└── ImagePreload.tsx
└── index.ts
└── useImage.ts
└── input
└── index.ts
└── Input.tsx
└── Input.types.ts
└── input-group
└── index.ts
└── InputGroup
└── index.ts
└── InputGroup.styles.ts
└── InputGroup.tsx
└── InputGroup.types.ts
└── InputGroupAddon
└── index.ts
└── InputGroupAddon.styles.ts
└── InputGroupAddon.tsx
└── InputGroupAddon.types.ts
└── InputGroupButton
└── index.ts
└── InputGroupButton.styles.ts
└── InputGroupButton.tsx
└── InputGroupButton.types.ts
└── InputGroupInput
└── index.ts
└── InputGroupInput.styles.ts
└── InputGroupInput.tsx
└── InputGroupInput.types.ts
└── InputGroupText
└── index.ts
└── InputGroupText.styles.ts
└── InputGroupText.tsx
└── InputGroupText.types.ts
└── InputGroupTextarea
└── index.ts
└── InputGroupTextarea.tsx
└── InputGroupTextarea.types.ts
└── item
└── index.ts
└── Item
└── index.ts
└── Item.styles.ts
└── Item.tsx
└── Item.types.ts
└── ItemActions
└── index.ts
└── ItemActions.tsx
└── ItemActions.types.ts
└── ItemContent
└── index.ts
└── ItemContent.tsx
└── ItemContent.types.ts
└── ItemDescription
└── index.ts
└── ItemDescription.tsx
└── ItemDescription.types.ts
└── ItemFooter
└── index.ts
└── ItemFooter.tsx
└── ItemFooter.types.ts
└── ItemGroup
└── index.ts
└── ItemGroup.tsx
└── ItemGroup.types.ts
└── ItemHeader
└── index.ts
└── ItemHeader.tsx
└── ItemHeader.types.ts
└── ItemMedia
└── index.ts
└── ItemMedia.styles.ts
└── ItemMedia.tsx
└── ItemMedia.types.ts
└── ItemSeparator
└── index.ts
└── ItemSeparator.tsx
└── ItemSeparator.types.ts
└── ItemTitle
└── index.ts
└── ItemTitle.tsx
└── ItemTitle.types.ts
└── kbd
└── index.ts
└── Kbd.tsx
└── Kbd.types.ts
└── label
└── index.ts
└── Label.tsx
└── Label.types.ts
└── pagination
└── index.ts
└── Pagination
└── index.ts
└── Pagination.tsx
└── Pagination.types.ts
└── PaginationContent
└── index.ts
└── PaginationContent.tsx
└── PaginationContent.types.ts
└── PaginationEllipsis
└── index.ts
└── PaginationEllipsis.tsx
└── PaginationEllipsis.types.ts
└── PaginationItem
└── index.ts
└── PaginationItem.tsx
└── PaginationItem.types.ts
└── PaginationLink
└── index.ts
└── PaginationLink.tsx
└── PaginationLink.types.ts
└── PaginationNext
└── index.ts
└── PaginationNext.tsx
└── PaginationNext.types.ts
└── PaginationPrevious
└── index.ts
└── PaginationPrevious.tsx
└── PaginationPrevious.types.ts
└── progress
└── index.ts
└── Progress
└── index.ts
└── Progress.context.ts
└── Progress.styles.ts
└── Progress.tsx
└── Progress.types.ts
└── ProgressIndicator
└── index.ts
└── ProgressIndicator.styles.ts
└── ProgressIndicator.tsx
└── ProgressIndicator.types.ts
└── ProgressLabel
└── index.ts
└── ProgressLabel.styles.ts
└── ProgressLabel.tsx
└── ProgressLabel.types.ts
└── ProgressTrack
└── index.ts
└── ProgressTrack.styles.ts
└── ProgressTrack.tsx
└── ProgressTrack.types.ts
└── ProgressValue
└── index.ts
└── ProgressValue.styles.ts
└── ProgressValue.tsx
└── ProgressValue.types.ts
└── scroll-area
└── index.ts
└── ScrollArea
└── index.ts
└── ScrollArea.context.ts
└── ScrollArea.tsx
└── ScrollArea.types.ts
└── ScrollArea.utils.ts
└── ScrollBar
└── index.ts
└── ScrollBar.tsx
└── ScrollBar.types.ts
└── separator
└── index.ts
└── Separator.styles.ts
└── Separator.tsx
└── Separator.types.ts
└── sidebar
└── index.ts
└── Sidebar
└── index.ts
└── Sidebar.tsx
└── Sidebar.types.ts
└── SidebarContent
└── index.ts
└── SidebarContent.tsx
└── SidebarContent.types.ts
└── SidebarFooter
└── index.ts
└── SidebarFooter.tsx
└── SidebarFooter.types.ts
└── SidebarGroup
└── index.ts
└── SidebarGroup.tsx
└── SidebarGroup.types.ts
└── SidebarGroupAction
└── index.ts
└── SidebarGroupAction.tsx
└── SidebarGroupAction.types.ts
└── SidebarGroupContent
└── index.ts
└── SidebarGroupContent.tsx
└── SidebarGroupContent.types.ts
└── SidebarGroupLabel
└── index.ts
└── SidebarGroupLabel.tsx
└── SidebarGroupLabel.types.ts
└── SidebarHeader
└── index.ts
└── SidebarHeader.tsx
└── SidebarHeader.types.ts
└── SidebarInput
└── index.ts
└── SidebarInput.tsx
└── SidebarInput.types.ts
└── SidebarInset
└── index.ts
└── SidebarInset.tsx
└── SidebarInset.types.ts
└── SidebarMenu
└── index.ts
└── SidebarMenu.tsx
└── SidebarMenu.types.ts
└── SidebarMenuBadge
└── index.ts
└── SidebarMenuBadge.tsx
└── SidebarMenuBadge.types.ts
└── SidebarMenuButton
└── index.ts
└── SidebarMenuButton.styles.ts
└── SidebarMenuButton.tsx
└── SidebarMenuButton.types.ts
└── SidebarMenuItem
└── index.ts
└── SidebarMenuItem.tsx
└── SidebarMenuItem.types.ts
└── SidebarMenuSkeleton
└── index.ts
└── SidebarMenuSkeleton.tsx
└── SidebarMenuSkeleton.types.ts
└── SidebarMenuSub
└── index.ts
└── SidebarMenuSub.tsx
└── SidebarMenuSub.types.ts
└── SidebarMenuSubButton
└── index.ts
└── SidebarMenuSubButton.tsx
└── SidebarMenuSubButton.types.ts
└── SidebarMenuSubItem
└── index.ts
└── SidebarMenuSubItem.tsx
└── SidebarMenuSubItem.types.ts
└── SidebarProvider
└── index.ts
└── SidebarProvider.consts.ts
└── SidebarProvider.context.ts
└── SidebarProvider.tsx
└── SidebarProvider.types.ts
└── SidebarRail
└── index.ts
└── SidebarRail.tsx
└── SidebarRail.types.ts
└── SidebarSeparator
└── index.ts
└── SidebarSeparator.tsx
└── SidebarSeparator.types.ts
└── SidebarTrigger
└── index.ts
└── SidebarTrigger.tsx
└── SidebarTrigger.types.ts
└── skeleton
└── index.ts
└── Skeleton.tsx
└── Skeleton.types.ts
└── slider
└── index.ts
└── Slider
└── index.ts
└── Slider.context.ts
└── Slider.styles.ts
└── Slider.tsx
└── Slider.types.ts
└── useSlider.ts
└── SliderControl
└── index.ts
└── SliderControl.styles.ts
└── SliderControl.tsx
└── SliderControl.types.ts
└── useSliderControl.ts
└── SliderIndicator
└── index.ts
└── SliderIndicator.styles.ts
└── SliderIndicator.tsx
└── SliderIndicator.types.ts
└── useSliderIndicator.ts
└── SliderThumb
└── index.ts
└── SliderThumb.styles.ts
└── SliderThumb.tsx
└── SliderThumb.types.ts
└── useSliderThumb.tsx
└── SliderTrack
└── index.ts
└── SliderTrack.styles.ts
└── SliderTrack.tsx
└── SliderTrack.types.ts
└── spinner
└── index.ts
└── Spinner.styles.ts
└── Spinner.tsx
└── Spinner.types.ts
└── switch
└── index.ts
└── Switch.styles.ts
└── Switch.tsx
└── Switch.types.ts
└── textarea
└── index.ts
└── Textarea.tsx
└── Textarea.types.ts
└── toast
└── components
└── Assets.tsx
└── Toast.tsx
└── ToastActions.tsx
└── ToastContent.tsx
└── Toaster.tsx
└── ToastIcon.tsx
└── constants.ts
└── hooks
└── useSwipe.ts
└── useTheme.ts
└── useToasterKeyboard.ts
└── useToastHeight.ts
└── useToastSubscription.ts
└── useToastTimer.ts
└── hooks.ts
└── index.ts
└── state.ts
└── types.ts
└── utils.ts
└── tooltip
└── index.ts
└── Tooltip
└── index.ts
└── Tooltip.context.ts
└── Tooltip.tsx
└── Tooltip.types.ts
└── TooltipArrow
└── index.ts
└── TooltipArrow.tsx
└── TooltipContent
└── index.ts
└── TooltipContent.context.ts
└── TooltipContent.tsx
└── TooltipContent.types.ts
└── TooltipContent.utils.ts
└── useTooltipContent.ts
└── TooltipTrigger
└── index.ts
└── TooltipTrigger.tsx
└── TooltipTrigger.types.ts
└── useTooltipTrigger.ts
└── hooks
└── index.ts
└── useFontLoader.ts
└── index.ts
└── styles
└── animate.css
└── i18n-font.css
└── index.css
└── toast.css
└── types
└── index.ts
└── props.types.ts
└── utils
└── clsx.ts
└── get-style.ts
└── index.ts
└── warn-once.ts
|