@adddog/shadcn-vue-design-system
v0.1.0
Published
A collection of reusable Vue 3 components built with Reka UI and Tailwind CSS, following the shadcn/ui pattern
Maintainers
Readme
@adddog/shadcn-vue-design-system
Vue 3 component library wrapping reka-ui primitives with Tailwind CSS styling, following shadcn-vue patterns.
Usage
Consumers should only import from rad/ components. The rad/ layer is the public API — it wraps ui/ with transparent pass-throughs and providers. Do not import directly from ui/.
<!-- CORRECT -->
import { Button } from "@adddog/shadcn-vue-design-system/components/rad/button"
<!-- WRONG — do not import from ui/ directly -->
import { Button } from "@adddog/shadcn-vue-design-system/components/ui/button"Architecture
Component Layers
src/components/
├── ui/ # Internal: reka-ui wrappers with Tailwind styling (do not import directly)
├── rad/ # Public API: aliases + providers (HoudiniProvider) — use these
├── custom/ # App-specific composites (IconButtonTooltip)
└── houdini/ # CSS Paint API components (ConfettiBackground)Core Patterns
Simple wrapper - forwards props/emits, adds styling:
<script setup lang="ts">
import type { ButtonProps } from "reka-ui"
import { Primitive, useForwardPropsEmits } from "reka-ui"
import { cn } from "~/lib/utils"
import { buttonVariants } from "."
const props = defineProps<ButtonProps & { class?: string; variant?: string; size?: string }>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<Primitive :class="cn(buttonVariants({ variant, size }), props.class)" v-bind="forwarded">
<slot />
</Primitive>
</template>Styled with class extraction - uses reactiveOmit to separate class prop:
<script setup lang="ts">
import { reactiveOmit } from "@vueuse/core"
const props = defineProps<SomeProps & { class?: string }>()
const delegatedProps = reactiveOmit(props, "class")
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<Component :class="cn('base-styles', props.class)" v-bind="forwarded" />
</template>rad/ wrapper - transparent pass-through:
<script setup lang="ts">
import OriginalButton from "../../ui/button/Button.vue"
defineOptions({ inheritAttrs: false })
</script>
<template>
<OriginalButton v-bind="$attrs">
<template v-for="(_, name) in $slots" #[name]="slotProps">
<slot :name="name" v-bind="slotProps ?? {}" />
</template>
</OriginalButton>
</template>Key Utilities
src/lib/utils/cn.ts:
cn(...inputs)- clsx + tailwind-merge for class concatenationvalueUpdater(updaterOrValue, ref)- @tanstack/vue-table state helper
Houdini Paint API
src/composables/houdini/:
useHoudini(autoInit?)- returns{ supported, initialized, error }refsregisterWorklets()- loads 4 worklets: squircle, animated-gradient, smooth-border, confetti-backgroundisHoudiniSupported()- feature detectionapplyHoudiniClass()- addshoudini-supportedto document
src/components/houdini/ConfettiBackground.vue - animated confetti using CSS Paint API with fallback.
src/components/rad/HoudiniProvider.vue - app-wide Houdini initialization.
Styling
src/style/:
styles.css- main entry (imports css/main.css)css/main.css- imports fonts, themes, sizing, tailwind-mappingcss/themes.css- semantic color tokens (primary, secondary, destructive, etc)css/sizing.css- responsive sizing + touch targetscss/tailwind-mapping.css- maps Tailwind utilities to design tokenshoudini/fallbacks.css- fallbacks for unsupported browsers
Exports
// Main components
import { Button, Dialog, ... } from "@adddog/shadcn-vue-design-system"
// Specific paths
import Button from "@adddog/shadcn-vue-design-system/components/ui/button/Button.vue"
import { useHoudini } from "@adddog/shadcn-vue-design-system/composables"
import { cn } from "@adddog/shadcn-vue-design-system/lib/utils"
import "@adddog/shadcn-vue-design-system/styles.css"Component Inventory
ui/ + rad/ (59): accordion, alert, alert-dialog, aspect-ratio, avatar, badge, breadcrumb, button, button-group, calendar, card, carousel, checkbox, collapsible, combobox, command, context-menu, dialog, drawer, dropdown-menu, empty, field, form, hover-card, input, input-group, input-otp, item, kbd, label, menubar, native-select, navigation-menu, number-field, pagination, pin-input, popover, progress, radio-group, range-calendar, resizable, scroll-area, select, separator, sheet, sidebar, skeleton, slider, sonner, spinner, stepper, switch, table, tabs, tags-input, textarea, toggle, toggle-group, tooltip
custom/: IconButtonTooltip
houdini/: ConfettiBackground
Dependencies
reka-ui- headless primitives (formerly radix-vue)class-variance-authority- component variantsclsx+tailwind-merge- class management@vueuse/core- Vue utilitieslucide-vue-next- icons
Optional peer deps (for specific components):
embla-carousel-vue- carouselvue-sonner- sonner (toast notifications)vee-validate+@vee-validate/zod- form validationvue-input-otp- OTP input
Conventions
data-slotattribute on root elements for debugging/styling hooksdefineOptions({ inheritAttrs: false })+v-bind="$attrs"for wrapper transparency- CVA (class-variance-authority) for variant definitions exported from component index.ts
- All components support
classprop extension viacn()
Scripts
| Script | Description |
|--------|-------------|
| lint | eslint . |
| lint:fix | eslint --fix . |
| types | vue-tsc --noEmit |
