Getting Started
Installation
Bundy UI is a Nuxt module. Every component is styled with UnoCSS utility classes, so @unocss/nuxt is a required peer rather than an optional one — the five steps below are all of it, and none of them are skippable.
1. Authenticate against GitHub Packages
Bundy UI is a private package. GitHub's npm registry rejects anonymous requests, so this comes first — without it the install fails with a 401 .
Point the scope at the registry in your project. This file holds no secret and can be committed:
@jgastager:registry=https://npm.pkg.github.com Then put a classic personal access token with the read:packages and repo scopes in your user-level npmrc — never the project one, which gets committed:
//npm.pkg.github.com/:_authToken=ghp_yourtokenhere It has to be a classic token; fine-grained tokens don't authenticate against npm.pkg.github.com . The repo scope is needed because the source repository is private. In GitHub Actions use secrets.GITHUB_TOKEN with permissions: { packages: read } instead of a token of your own.
2. Install
npm install @jgastager/bundy-ui @unocss/nuxt3. Register the module
export default defineNuxtConfig({
modules: ["@unocss/nuxt", "@jgastager/bundy-ui"],
});4. Register the preset
Required, not optional: without it none of the components' utility classes resolve to CSS and everything renders unstyled. Both presets are needed — bundyPreset() supplies only the theme, while the utilities the components are built from ( flex , gap-md , text-lg ) come from presetWind3() .
import { defineConfig, presetWind3 } from "unocss";
import { bundyPreset } from "@jgastager/bundy-ui/preset";
export default defineConfig({
presets: [presetWind3(), bundyPreset()],
});Already have this file? Put bundyPreset() last in presets so its theme wins, and keep your existing safelist — yours and the preset's concatenate rather than replace. If you're on presetUno() , that's the same preset under an older name in UnoCSS 66; swap it for presetWind3() and nothing else changes.
5. Wrap your app in App
One component at the root, and the three things there can only be one of are handled: useToast() gets somewhere to render, every component below reads its language from the same place, and <html> gets its lang and dir . Skip it and toast.add() still succeeds while nothing appears.
<template>
<UiApp>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</UiApp>
</template> The locale prop is optional — leave it off for English. See i18n for the six bundled languages and how to write your own, and App for its toaster options (position, stacking, or null to place your own).
Check it worked
Every component is auto-imported under a Ui prefix — no imports to write.
<UiButton color="primary">Click me</UiButton>A correct install renders a rounded button with teal text on a faint teal fill, against a near-black page, in Outfit. A white background, serif type, or a bare browser button each mean something different — see If something looks wrong below.
What the preset changed in your app
Alongside the tokens, bundyPreset() emits a preflight — the page-level defaults every component is built against, rather than styling choices. It's listed here so nothing about your app changes without you knowing what did it:
box-sizing: border-boxmargin: 0; padding: 0body { background-color }body { font-family }:root { color-scheme: dark }a, button resets It also safelists the cursor-* classes the components apply from bindings — a resize edge, a disabled control, a drag handle. Those never appear literally in your source, so without the safelist UnoCSS tree-shakes them and the cursor silently falls back to an arrow.
Already have a reset? The preflight wins any rule both of them set, and that isn't something you can reorder your way out of: @unocss/nuxt injects its stylesheet from a Nuxt plugin rather than from nuxt.config 's css array, so it always lands after everything listed there, and its injectPosition option is disabled upstream. To take a rule back, override the token it comes from (background, text color and font all derive from the theme) or out-specify it — body.my-app beats a bare body whatever the order. To own the whole reset, bundyPreset({ baseStyles: false }) — but the components assume all of it, so that one is the heaviest.
Your own theme keys replace Bundy's
UnoCSS merges theme one key at a time, so a key you define wins outright rather than blending. Set colors.primary to a single hex and the whole 50–950 scale is gone, along with every bg-primary-100 the components use. Nothing errors; the colors are just wrong. Four keys account for almost all of it, because they're the ones a project is most likely to set for itself:
colors.primarycolors.secondaryborderRadius.DEFAULTfontFamily.sansexport default defineConfig({
// Don't — this replaces Bundy's scale:
// theme: { colors: { primary: "#7C5CFF" } },
// Do — this merges into it:
presets: [presetWind3(), bundyPreset({ colors: { primary: { DEFAULT: "#7C5CFF" } } })],
});fontFamily.sans is the exception worth knowing: it's a single value rather than a scale, so replacing it loses nothing and is the supported way to change the typeface. See Theming for the full palette and what colors accepts.
If something looks wrong
Most setup mistakes here fail quietly rather than loudly. These are the symptoms and what each one actually means.
Charts are a separate module
Everything above installs the base components. The 16 chart components — LineChart , BarChart , DonutChart and the rest — need one more module entry, because they safelist a few hundred paint classes a project drawing no charts shouldn't carry. There's nothing extra to install.
export default defineNuxtConfig({
modules: [
"@unocss/nuxt",
"@jgastager/bundy-ui",
"@jgastager/bundy-ui/charts",
],
});Miss that entry and chart components resolve to nothing and render silently. See Charts for which chart to reach for and how their data is shaped.
Options
Two small surfaces. Per-component default props live in app.config.ts instead — see Theming .
prefix"Ui"colors—baseStylestrue