Installation

Published on npm as @nqmcreative/ui. Everything below is verified against a fresh SvelteKit project on Windows.

The fast path

Four commands from nothing to a running app:

terminal
bunx sv create myapp --template minimal --types ts --install bun
cd myapp
bun add -d tailwindcss @tailwindcss/vite
bun add @nqmcreative/ui

Then let the CLI do the wiring — it writes app.css, patches app.html, and adds the CSS import to your root layout:

terminal
bunx nqm-ui init

It will not rewrite your Vite config — it prints the two lines to paste. Every write is idempotent, so running it twice changes nothing. Add --dry-run to see what it would touch.

Or by hand

1. Install

terminal
bun add @nqmcreative/ui

2. The Tailwind plugin

vite.config.ts
import tailwindcss from '@tailwindcss/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
	// tailwindcss() must come before sveltekit()
	plugins: [tailwindcss(), sveltekit()]
});

3. The entry CSS

src/app.css
@import 'tailwindcss';
@import '@nqmcreative/ui/theme.css';
@import '@nqmcreative/ui/fonts.css';

/* Tailwind v4 skips node_modules — point it at the package's dist so the
   class names used inside the components are generated. */
@source '../node_modules/@nqmcreative/ui/dist';

The @source line is not optional

Without it every component renders unstyled, with no error anywhere. Tailwind v4 skips node_modules, so it never sees the class names that live inside the package. Adjust the path to wherever node_modules sits relative to that CSS file.

4. Import the CSS once

src/routes/+layout.svelte
<script lang="ts">
	import '../app.css';
	import Toaster from '@nqmcreative/ui/toaster';

	let { children } = $props();
</script>

{@render children()}

<Toaster position="bottom-right" />

5. Fonts and theme, before first paint

Both go in src/app.html, above %sveltekit.head%. Plain HTML rather than <svelte:head>, because Svelte parses a bare crossorigin attribute as boolean true and svelte-check rejects it.

src/app.html
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<script>
	const saved = localStorage.getItem('nqm-theme');
	if (saved === 'dark' || saved === 'light') document.documentElement.classList.add(saved);
</script>

Using a component

Each component has its own subpath — the file name in kebab-case, so AvatarGroup is @nqmcreative/ui/avatar-group.

+page.svelte
<script lang="ts">
	import Button from '@nqmcreative/ui/button';
	import Field from '@nqmcreative/ui/field';
	import Input from '@nqmcreative/ui/input';

	let email = $state('');
</script>

<Field label="Work email" hint="We only email about releases.">
	<Input bind:value={email} placeholder="you@example.com" />
</Field>

<Button tone="accent">Get started</Button>

The barrel works too:

either way
import { Button, Field, Input } from '@nqmcreative/ui';

Both produce the same bundle — measured on one app, barrel and subpaths came out byte-identical, because the barrel tree-shakes. Subpaths are about being explicit, and they cut the module graph the bundler walks.

The CLI

terminal
bunx nqm-ui list forms          # every component in a category
bunx nqm-ui info date-picker    # subpath, what it renders, what it imports
bunx nqm-ui add button badge    # print the import lines
bunx nqm-ui add button --to src/routes/+page.svelte

add is a convenience, not an installer: after bun add the whole library is already there, and a component pulls in whatever it renders internally. It writes the import line and tells you what comes along.

Updating

terminal
bun update @nqmcreative/ui

Only if you installed from GitHub

bun caches git dependencies by URL and will not notice new commits on its own. Pin a commit or a tag with #sha, or clear the cache with bun pm cache rm. Installing from npm has no such problem.

Other install routes

A packed tarball

Portable, and needs no repo access.

terminal
# in the library repo
bun run build && bun pm pack

# in your project
bun add ./nqmcreative-ui-0.1.0.tgz

Straight from GitHub

Tracks main rather than a release. The repo is public, so this needs no authentication — no SSH key, no token. git+https://github.com/… resolves to the same thing; bun rewrites both to github:owner/repo#sha.

terminal
bun add github:mukhsamr/nqmcreative-ui

A live symlink

For working on the library and an app at once. Use npm here — bun link and bun add file:<dir> both fail on Windows with EBUSY, because bun copies the whole source directory into its cache.

terminal
npm install file:../path/to/nqmcreative-ui
esc

layout & navigation

Accordion Wrapper that stacks collapsible items with hairlines between them.
AccordionItem A single collapsible row, built on <details> so it works without JS.
Breadcrumb Trail of links to the current page.
Divider Hairline rule, horizontal or vertical, with an optional caption.
Pagination Page numbers that collapse to an ellipsis around the current one.
Steps Progress through a flow, horizontal or vertical, with a failed state.
Tabs Underline, pill or segmented tabs bound to a value.

feedback

Alert Inline message in any tone, optionally dismissible.
EmptyState Placeholder for an empty list, with a glyph and an action slot.
Progress Determinate or indeterminate bar, in three sizes.
Skeleton Pulsing placeholder in text, block or circle form.
Spinner Rotating ring that inherits the surrounding colour.
Toaster Renders the toast queue. Mount once, near the root.

data display

Avatar Image or initials, circular or squared, in five sizes.
AvatarGroup Overlapping row of avatars with a ring cut out of the background.
Badge Pill label for status and counts. Solid, soft or outline.
Card Bordered, filled or tinted surface with an optional header and footer.
Kbd Keyboard key, styled with a thicker bottom edge.
Stat Figure with a label, delta and trend arrow.
Table Data-driven table with sorting, row selection and a cell snippet.

actions

Button Five variants and four sizes, with a loading state and an href mode.
Link Anchor in any tone, with an external variant.

forms

Calendar Month grid with keyboard navigation, bounds and disabled days.
Checkbox Single checkbox with a label, description and indeterminate state.
CheckboxGroup A set of checkboxes bound to a string array, with an optional cap.
Combobox Single-select with a text filter and keyboard navigation.
DatePicker Text field plus a calendar popover. Value is a YYYY-MM-DD string.
Dropzone Drag-and-drop file input with type, size and count validation.
Field Wraps any control with a label, hint and error message.
Input Text field with prefix and suffix slots, sizes and an invalid state.
InputGroup Joins an input to a button or addon with a single shared border.
Label Form label with an optional required marker.
MultiSelect Multi-select with chips, grouping and a selection cap.
NumberInput Number field with steppers, clamping and decimal handling.
PasswordInput Password field with a visibility toggle and a strength meter.
Radio Single radio bound to a shared group value.
RadioGroup A set of radios, optionally rendered as selectable cards.
SegmentedControl Exclusive choice as one joined row, with arrow-key movement.
Select Native select with a chevron and data-driven options.
Slider Range input with ticks, a value bubble and dual-handle mode.
Switch On/off toggle with a label and description.
Textarea Multi-line field that can grow with its content.

overlay

CommandPalette Cmd+K launcher with grouping, shortcuts and hidden keywords.
ConfirmDialog Small dialog that stays busy until an async confirm settles.
ContextMenu Right-click menu placed at the pointer, flipped near the edges.
Drawer Panel that slides in from any edge, on the native dialog top layer.
Dropdown Menu anchored to a trigger, with focus trap and viewport flipping.
MenuItem Row inside a Dropdown or ContextMenu, with a shortcut hint.
MenuSeparator Rule between menu groups, with an optional caption.
Modal Centred dialog on the native top layer, with a footer slot.
Popover Anchored panel that opens on click or hover.
ThemeToggle Light, dark and system, remembered in localStorage.
Tooltip CSS-only label on hover and focus, in four placements.

app shell

Footer Site footer with link columns and a bottom bar.
Navbar Top bar that collapses into a drawer below the md breakpoint.
Sidebar Nested, collapsible navigation rail for an app shell.

locale

LocaleProvider Scopes a locale to a subtree — the SSR-safe way to translate.