npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@witchcraft/ui

v0.3.25

Published

Vue component library.

Readme

🚧 WORK IN PROGRESS 🚧

Docs Release NPM Version (with latest tag)

Opinionated, batteries included, vue/nuxt component library + some related utils.

  • Built with accessibility in mind.
  • Themable via metamorphosis which allows providing a custom set of tailwind variables for easier application theming (e.g. *-fg, *-bg, *-accent). No regular tailwind colors are used except neutral.
  • Easily tweak parts of a component via props without having to pass complicate objects. For example, the input component can take a wrapper-class attribute to style it's wrapper.
  • Simple components have an unstyle option to use them minimally styled if needed*

This is unfortunately not true styleless since the tailwind classes are still in the build output.

Storybook

Usage with Nuxt

Modules, composables, and directives should work out of the box without auto imports. The module also installs the reka-ui/nuxt module.

Tailwind (V4)

The module automatically sets up tailwind v4 and generates a custom witchcraft-ui.css file with the proper imports, just add it in your tailwind css file.

It does not install "@nuxtjs/tailwindcss" for now since it's not compatible with v4. See #919.

@import "@tailwindcss" source("../../../app");
@import "../../../.nuxt/witchcraft-ui.css";

// without Nuxt:
@import "@witchcraft/ui/utils.css";
@import "@witchcraft/ui/base.css";
@import "@witchcraft/ui/animations.css";
// source used components
@source "@witchcraft/ui/components";

Icons

The module also installs the unplugin-icons/nuxt module and configures it with a custom config styles icons a bit better. This can be disabled with the includeUnpluginIconsPlugins option.

Other

There are some additional helpers that are not auto imported. These can be imported from #witchcraft-ui-helpers.

Anything else that might be needed can be imported from #witchcraft-ui.

General Usage

Props

To make it easier to style parts of components or override behavior, some components can accept additional prefixed attributes, for example, you can pass wrapper-class to the input component to style it's wrapper.

I think this is nicer in general than having to pass an object with extra attributes. The only weird part, is because of how vue changes the case of props, for attributes like innerHTML you will need to pass {prefix}-inner-h-t-m-l, but all components have proper types to help this. Similarly events look like {prefix}-on-click.

Slots

Usually it is possible to do most modifications through props, but slots to replace parts of components are available.

Most slots where possible are passed all properties needed to replace them except classes so you can do something like this, to for example replace the simple input component inside the input component.

<lib-simple-input-deprecated>
	<template #input="slotProps">
		<lib-simple-input
			:class="'completely custom styles'"
			v-bind="slotProps"
		></lib-simple-input>
	</template>
</lib-simple-input>

Other

Standalone Usage

import { defineConfig } from "vite"

import { WitchcraftUiResolver } from "@witchcraft/ui/build/WitchcraftUiResolver"
import IconsResolver from "unplugin-icons/resolver"
import Icons from "unplugin-icons/vite"
import Components from "unplugin-vue-components/vite"
import { unpluginIconViteOptions } from "@witchcraft/ui/build/unpluginIconViteOptions"

export default defineConfig({
	plugins: [
		vue() as any,
		Components({
			resolvers: [
				// required
				IconsResolver(),
				// to enable auto-imports
				// not-required
				WitchcraftUiResolver({
					// optionally change the prefix
					prefix: "W",
					filter: (name) => name !== "...",
				}),
			],
			dts: "./types/components.d.ts",
		}),
		// style the icons correctly
		Icons(unpluginIconViteOptions)
	],
})

See [@witchcraft/editor/vite.config.dev.ts].

Setting up Tailwind

Extra Classes

@import "@witchcraft/ui/utils.css";
@import "@witchcraft/ui/base.css";
@import "@witchcraft/ui/animations.css";
@source "/path/to/node_modules/@witchcraft/ui/src/runtime/components";

Base just contains animation keyframes, basic styles for vue animations, and some basic global styles. They will get imported regardless of whether they are used since otherwise tailwind does not detect they are being used.

tailwind-merge

The library provides a customized twMerge instance (@witchcraft-ui/utils/twMere.js). You will need to use it or import the options it extends and customize your twMerge instance.

Icons

This library uses unplugin-icons to provide icons. It installs as a dependency to have all icons available @iconify/json since it uses various icons.

Getting Proper Types

While components should be correctly typed, the library internally extends vue's interfaces to allow data-* attributes and also some missing aria attributes (like aria-description).

You might need to customize these by adding the following in a global d.ts.

// to make the component types globally available you can do:
import { GlobalComponentTypes } from "@witchcraft/ui"
declare module "@vue/runtime-core" {
	interface GlobalComponents extends GlobalComponentTypes { }
}

// to add more attributes yourself you can do:
declare module "vue" {
	interface HTMLAttributes {
		// ...
	}
	interface AriaAttributes {
		// ...
	}
	interface ComponentCustomProps {
		// ...
	}
}
export {}

Note that using the strictTemplates compiler option can cause weird issues with fallthrough props.

Development

Props

Due to issues with vue 3 removing $listeners and the fact that we can't inherit from an existing HTML attribute types to specify props for wrapper components (most of them), we have to declare props in a convoluted way to get proper types when we consume the components.

Here is an example of the needed code for props to work correctly:

<script lang="ts">
// this is done in a seperate script so that it actually compiles
// and we can have interface merging, a setup script cannot interface merge

import type { BaseInteractiveProps } from "../shared/props.js"

// real props vue can understand, they will show up under props.*
type RealProps =
// simple types are okay
& BaseInteractiveProps
& {
	// any ignored props that we need to use in the component template 
	// will need to be re-defined so vue can see them
	// in a way vue can see them if it's one of the problem properties
	id?: InputHTMLAttributes["id"]
}

interface Props
	extends
	// we need to ignore the complex InputHTMLAttributes type
	// otherwise vue compilation fails
	// if we ignore it, all it's properties are passed as fallback attrs
	// NOT to props as normal
	// we also need to omit properties or events that the component overrides with a different type
	/** @vue-ignore */
	Partial<Omit<InputHTMLAttributes,"class" | "onSubmit"> & {
		//	overrides for the components
		// usually, for example, class can only be a string (because of tailwind)
		// this will be passed as an attr
		class?:string
	}>,
	/** @vue-ignore */
	RealProps
{ }
</script>

<script lang="ts" setup>
const props = withDefaults(defineProps<Props>(), {
	id: "",
	unstyle: false, disabled:false, readonly:false, border:true,
})
const $attrs = useAttrs()
</script>

For prop types that don't error, I think we can just define them normally, but then it turns into a mess, some components will have their values in props, some in $attrs, this way. This is consistent and also more in line with what we expect.

Related Links:

  • https://github.com/vuejs/rfcs/discussions/397
  • https://github.com/vuejs/core/issues/8522
  • https://github.com/vuejs/rfcs/pull/477
  • https://github.com/vuejs/language-tools/issues/1232#issuecomment-1118176103