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

@twentyfourg/grimoire

v3.0.5

Published

A modern, headless Vue 3 component library built with TypeScript and Composition API. Create your own design system with unstyled, accessible components.

Downloads

718

Readme

Grimoire v3 Version

A modern, headless Vue 3 component library built with TypeScript and Composition API

Grimoire is a collection of unstyled components that provide structure, accessibility, and logic while letting you control the styling. Perfect for building your own design system or theming components to match any brand.

✨ Key Features

  • 🎨 Headless Components - Unstyled by default, bring your own styles
  • Vue 3 Composition API - Modern, performant, tree-shakeable
  • 🔷 TypeScript First - Full type safety and IntelliSense support
  • 🎭 Theme System - Create styled wrappers with useStyledComponent composable
  • Accessible - Built with accessibility in mind
  • 🎯 Direct Ref Access - No more @refMounted events needed!
  • 🧪 Fully Tested - Comprehensive unit and E2E test coverage
  • 🎨 Iconify Integration - Built-in support for 200,000+ icons

🌐 Browser Support

Supports all modern browsers (latest versions):

  • Chrome/Edge (Chromium)
  • Firefox
  • Safari
  • Opera

Note: IE11 is not supported.


📦 Installation & Setup

Install the Package

npm install @twentyfourg/grimoire
# or
pnpm add @twentyfourg/grimoire
# or
yarn add @twentyfourg/grimoire

Basic Setup

<script setup>
import { GButton, GInput, GModal } from '@twentyfourg/grimoire';
import { ref } from 'vue';

const value = ref('');
</script>

<template>
  <GButton variant="primary filled">Click Me</GButton>
  <GInput v-model="value" placeholder="Enter text" />
</template>

Setup with Icons (Optional)

Grimoire uses Iconify for icons. Register icon collections with the plugin:

// main.ts
import { createApp } from 'vue';
import Grimoire from '@twentyfourg/grimoire/plugin';
import { icons } from './icons-bundle'; // Your custom icons (see below)
import App from './App.vue';

const app = createApp(App);

app.use(Grimoire, {
  icons, // Your icon collections
  importBaseIcons: true, // Include Grimoire's base icons (default: true)
});

app.mount('#app');

Build your own icon bundles from SVG files:

// vite.config.ts
import { viteIconBundlePlugin } from '@twentyfourg/grimoire/build-tools';

export default defineConfig({
  plugins: [
    viteIconBundlePlugin({
      sources: [
        {
          type: 'folder',
          path: 'icons', // Your SVG folder
          prefix: 'app', // Icons will be: app:icon-name
          variants: 'both', // Creates colored & monotone versions
        },
      ],
    }),
  ],
});

Then use icons in components:

<template>
  <GIcon icon="app:logo" />
  <GButton icon="app:plus">Add Item</GButton>
  <GInput icon-left="app:search" placeholder="Search..." />
</template>

📚 See: Build Tools Documentation for advanced icon options


📦 Available Components

| Component | Description | | ------------------- | ------------------------------------------------------------- | | GAvatar | User avatar with loading/error states | | GBreadcrumbs | Breadcrumb navigation | | GButton | Button with icon support and variants | | GCheckbox | Checkbox with optional label and indeterminate state | | GCollapsible | Collapsible/accordion sections | | GDatepicker | Date and datetime picker (powered by @vuepic/vue-datepicker) | | GDivider | Visual divider | | GDropdown | Select/multiselect dropdown (powered by @vueform/multiselect) | | GForm | Form with validation support | | GFormField | Form field wrapper with label and error states | | GFormFieldInput | Complete form field with integrated input | | GIcon | Icon component (Iconify integration) | | GInput | Text input, textarea, number input with masking support | | GModal | Modal dialog with backdrop | | GPagination | Pagination controls | | GProgressBar | Linear progress bar | | GProgressCircle | Circular progress indicator | | GRadio | Radio button with optional label | | GSkeleton | Loading skeleton placeholders | | GSwitch | Toggle switch | | GTable | Data table with sorting and pagination | | GTabs | Tab navigation with router support | | GThumbnail | File thumbnail previews |

Component Documentation

📚 Documentation site is currently under construction. Complete API documentation for each component will include:

  • Prop references with types
  • Event documentation
  • Slot descriptions
  • HTML structure
  • CSS variables
  • Usage examples

In the meantime, you can view component source code in packages/grimoire/src/components for detailed prop definitions and TypeScript types.


🎨 Creating Styled Components

Grimoire components are intentionally unstyled. Create themed wrappers for your project using the useStyledComponent composable:

<!-- StyledButton.vue -->
<template>
  <GButton ref="buttonRef" v-bind="mergedProps" class="styled-button">
    <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
      <slot :name="name" v-bind="slotData" />
    </template>
  </GButton>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { GButton, type GButtonExposed } from '@twentyfourg/grimoire';
import { useStyledComponent } from '@twentyfourg/grimoire/composables';

const props = defineProps({
  ...GButton.props,
  // Override base component defaults
  colorVariants: {
    type: Array,
    default: () => ['primary', 'danger'],
  },
  rounded: {
    type: Boolean,
    default: true, // Make rounded default for your project
  },
});

// ✅ Re-export the exposed type for TypeScript consumers
export type StyledButtonExposed = GButtonExposed;

const buttonRef = ref<GButtonExposed>();
const { mergedProps, expose } = useStyledComponent<GButtonExposed>(buttonRef, props);

// ✅ This ensures all GButton methods are available on your styled component
defineExpose(expose);
defineOptions({
  inheritAttrs: false,
});
</script>

<style scoped>
.styled-button {
  /* Your project's button styles */
  background: var(--color-primary);
  color: white;
  padding: 12px 24px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
  transition: background 0.2s;
}

.styled-button:hover {
  background: var(--color-primary-dark);
}

.styled-button.rounded {
  border-radius: 24px;
}
</style>

Benefits of useStyledComponent

  • Automatic prop merging - spreads base props, merges overrides
  • Method forwarding - all base component methods automatically available
  • Direct ref access - no more @refMounted events
  • TypeScript safe - full type inference
  • Consistent pattern across all your styled components

Advanced: Conditional Props

For conditional logic based on other props, use computeProps:

const { mergedProps, expose } = useStyledComponent(dropdownRef, props, {
  computeProps(merged, attrs) {
    // Apply smart defaults for multi-select modes
    if (merged.mode === 'tags' || merged.mode === 'multiple') {
      merged.hideSelected = merged.hideSelected ?? false;
      merged.closeOnSelect = merged.closeOnSelect ?? false;
    }
    return merged;
  },
});

📚 See: Styled Component Migration Guide for complete details


🎯 Direct Ref Access (v3 Feature!)

One of v3's biggest improvements: access child component methods directly through template refs.

Old Way (v2)

<template>
  <StyledDropdown @refMounted="dropdownRef = $event" />
</template>

<script setup>
const dropdownRef = ref(null);

function openDropdown() {
  dropdownRef.value.open(); // Only works after @refMounted fires
}
</script>

New Way (v3)

<template>
  <StyledDropdown ref="dropdown" />
  <button @click="dropdown.open()">Open</button>
</template>

<script setup>
import { ref } from 'vue';

const dropdown = ref(null);

// All base component methods available directly!
function doStuff() {
  dropdown.value.open();
  dropdown.value.close();
  dropdown.value.toggle();
  dropdown.value.$base.value; // Escape hatch to child ref
}
</script>

No more:

  • @refMounted event listeners
  • ❌ Waiting for mounted lifecycle
  • ❌ Event handler boilerplate

Just:

  • ✅ Direct ref access like native Vue
  • ✅ All base component methods available
  • ✅ TypeScript autocomplete support

Exposed Types for Full TypeScript Support

Every Grimoire component exposes its methods and properties through an *Exposed type. This gives you complete TypeScript autocomplete and type safety:

import { ref } from 'vue';
import { type GDropdownExposed, type GInputExposed } from '@twentyfourg/grimoire';

const dropdown = ref<GDropdownExposed>();
const input = ref<GInputExposed>();

// ✅ TypeScript knows ALL available methods and properties!

// VueMultiselect methods (from wrapped component)
dropdown.value?.open();
dropdown.value?.close();

// Custom GDropdown properties
dropdown.value?.isDropdownOpen;
dropdown.value?.toggle();

// Native HTMLInputElement methods
input.value?.focus();
input.value?.blur();

// Custom GInput methods
input.value?.clear();
input.value?.validate();

When creating styled wrappers, you should re-export the base component's exposed type to maintain this TypeScript support. See the styled component example below for how to do this.


🔷 TypeScript Support

All components are written in TypeScript with full type definitions:

import type { GButtonProps } from '@twentyfourg/grimoire';

interface MyButtonProps extends GButtonProps {
  myCustomProp?: string;
}

const props = defineProps<MyButtonProps>();

For component ref types (to get full TypeScript autocomplete for methods and properties), see the Exposed Types section above.


🧪 Testing

Grimoire has comprehensive test coverage:

  • 40+ unit tests per component (Vitest)
  • E2E visual regression tests (Playwright)
  • 100% prop coverage
  • Accessibility testing

Run tests:

# Unit tests
pnpm test

# Unit tests with UI
pnpm test:ui

# E2E tests
pnpm --filter @grimoire/showcase test:e2e

# Coverage report
pnpm test:coverage

📚 See: Testing Strategy for testing styled components


📚 Documentation


🤝 Contributing

Contributions are welcome!

Development Setup

# Clone the repo
git clone https://github.com/twentyfourg/grimoire.git

# Install dependencies
pnpm install

# Run showcase in dev mode
pnpm run:showcase

# Run tests
pnpm test

Project Structure

grimoire/
├── @app/showcase/         # Component showcase app
├── packages/
│   ├── grimoire/          # Core unstyled components
│   │   └── ai-docs/       # Consumer documentation (shipped with npm)
│   └── themes/            # Styled component examples
├── dev-docs/              # Internal developer documentation
├── ai_docs/               # AI-assisted maintenance documentation
└── scripts/               # Build and utility scripts

Documentation

  • dev-docs/ - Internal documentation for Grimoire maintainers
  • ai_docs/ - AI-assisted docs for maintaining Grimoire (creating showcases, tests, etc.)
  • packages/grimoire/ai-docs/ - Consumer docs shipped with npm (migration guide, component usage, etc.)

📝 Changelog

See CHANGELOG.md for version history.

We follow Semantic Versioning.


📄 License

MIT License - see LICENSE file for details


Built with ❤️ by 24G