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:

.npmrc
ini
@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:

~/.npmrc
ini
//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

bash
npm install @jgastager/bundy-ui @unocss/nuxt

3. Register the module

nuxt.config.ts
ts
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() .

uno.config.ts
ts
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.

app.vue
html
<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.

html
<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:

Rule
Why it's a requirement, not a preference
box-sizing: border-box
Components pair w-full with their own padding, so under the CSS default every full-width control overspills its container by exactly that padding.
margin: 0; padding: 0
Layout is flex and gap throughout, assuming a heading or paragraph adds no margin of its own.
body { background-color }
The text color has to live on body so teleported overlays inherit it — so the background does too, or near-white text lands on the browser's white.
body { font-family }
Declaring the token only creates the font-sans utility; nothing applies it. Without this the page inherits the browser's serif.
:root { color-scheme: dark }
Scrollbars, form controls and the canvas behind the page follow the app instead of the OS.
a, button resets
Both are styled entirely through utility classes, so the UA's link underline and button chrome would show through.

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:

Key
What defining it yourself costs
colors.primary
The full 50–950 scale collapses to the single value you set.
colors.secondary
Same — and the components reach for its shades, not just its DEFAULT.
borderRadius.DEFAULT
Every rounded corner changes, and the rest of the radius scale goes with it.
fontFamily.sans
The typeface the preflight applies to body. The one you may actually want to replace.
uno.config.ts
ts
export 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.

Symptom
Cause
401 on install
Step 1 — no token, or a fine-grained one. The registry needs a classic token with read:packages and repo.
Everything renders unstyled
bundyPreset() isn't in uno.config.ts, or the file isn't being picked up. This is step 4, and it fails silently.
Near-white text on a white page, or serif type
The preflight isn't reaching the page — same cause as above, or baseStyles: false without a replacement reset.
Colors flat, or radii wrong everywhere
A theme key of your own is replacing Bundy's. Go through bundyPreset({ colors }) instead.
Full-width controls overflow by a few dozen pixels
border-box isn't applied — the preflight is missing or overridden by a later reset.
A chart renders nothing at all
The charts module isn't registered. It's a second entry in modules.
toast.add() does nothing
No <UiApp> at the root, so there is no toast stack to render into. Step 5.
An icon renders nothing
The name doesn't match a bundled icon. Icons resolve dynamically, so a typo is silent — check the Icons page.

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.

nuxt.config.ts
ts
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 .

Option
Set in
Default
prefix
nuxt.config.ts
"Ui"
Prefix on auto-imported components, e.g. Ui -> <UiButton>. Set it on both modules if you register the charts one too.
colors
uno.config.ts
—
Deep-merged over the default palette. Override one shade or a whole semantic token.
baseStyles
uno.config.ts
true
The preflight above. Turn it off to own the reset yourself.