@brumaombra/ui-vintage
v0.8.2
Published
Source-published Nuxt UI runtime with shared theme tokens and built-in Nuxt integrations.
Maintainers
Readme
🎨 UI Vintage
A source-published Nuxt UI runtime for focused, reusable interfaces
@brumaombra/ui-vintage is a Nuxt 4 component library and runtime module for building consistent dashboards, forms, landing pages, overlays, and content experiences. It combines reusable Vue components, shared theme tokens, localization, and Nuxt integrations in one package that is compiled by the consuming application.
📘 Overview
UI Vintage is designed for Nuxt applications that need a coherent interface without rebuilding the same primitives for every project. The package provides low-level controls such as buttons, inputs, selects, dialogs, tabs, switches, and sidebars alongside higher-level building blocks such as dashboard shells, landing layouts, cards, data lists, and message flows.
The package is intentionally published as source. The consuming Nuxt application compiles the library together with its own runtime, which keeps Nuxt-specific integrations available and avoids maintaining a separate framework-agnostic build artifact.
This is a Nuxt library, not a generic Vue component bundle. Components rely on Nuxt runtime features including #components, NuxtImg, and module lifecycle hooks.
🖼️ Images
Screenshots
✨ Features
- Nuxt 4 module with automatic runtime integration.
- Shared stylesheet and design tokens injected from
src/styles.css. - Explicit subpath imports that keep application dependencies clear.
- Vue 3 components built with Composition API and TypeScript source.
- Primitive UI controls based on Reka UI where accessible behavior is required.
- Reusable dashboard, landing, blog, card, field, and data-display components.
- Built-in busy, confirm-dialog, message-dialog, and stacked, swipeable message-toast flows.
- A spring-based motion system (pure CSS
linear()easings) that respectsprefers-reduced-motion. - Theme selector with light, dark, and automatic modes and a circular View Transitions reveal.
- Library locale messages merged into an existing Vue I18n instance when available.
- Automatic
@nuxt/imageinstallation for components that useNuxtImg. - Source publishing with no required library build step before installation.
🏗️ Architecture
UI Vintage is split into three cooperating layers:
- 🧩 Components in
src/components/contain the public Vue UI and composite layouts. - 🎨 Styles and helpers in
src/styles.cssandsrc/lib/provide theme tokens, class utilities, and shared behavior. - 🔌 Nuxt runtime in
module.mjsandsrc/runtime/integrates the package with the consuming app.
🔄 Module Behavior
When the module is registered, it:
- Injects
src/styles.cssinto the Nuxt application once. - Adds the published
src/directory to Nuxt transpilation. - Installs
@nuxt/imagewhen the consuming app has not already registered it. - Registers the library i18n plugin.
- Merges the library’s locale messages into the app’s Vue I18n composer when Vue I18n is present.
The module does not auto-register every component. Import components and helpers explicitly from their public subpaths.
📁 Repository Layout
src/
components/ # Public components and UI primitives
i18n/ # Library locale messages
lib/ # Shared helpers and token utilities
runtime/ # Nuxt runtime plugins
styles.css # Shared design tokens and component styles
module.mjs # Nuxt module entrypoint
demo-app/ # Private Nuxt showcase and manual verification app🚀 Quick Start
📦 Install the package
Install the UI Vintage library and the peer integrations used by the package in an existing Nuxt 4 application:
npm install @brumaombra/ui-vintage @nuxt/image vue-i18n🔌 Register the module
Add the module to nuxt.config.js:
export default defineNuxtConfig({
modules: [
'@brumaombra/ui-vintage'
]
});@nuxt/image is installed by the module when it is not already present in the app’s module list. Install it explicitly when your application also uses image components directly. Vue I18n is optional at runtime, but it is required for the library’s locale messages and translated components.
🧪 Usage
🧱 Use a component
Components are imported from explicit package subpaths:
<script setup>
import { Button } from '@brumaombra/ui-vintage/button';
import { Card, CardContent } from '@brumaombra/ui-vintage/card';
</script>
<template>
<Card>
<CardContent class="flex items-center justify-between gap-4">
<span>Workspace status</span>
<!-- For example, the Button component accepts variants such as primary, secondary, gray, and ghost -->
<Button variant="primary">Save changes</Button>
</CardContent>
</Card>
</template>🔔 Use a message flow
The dialog and busy helpers can be imported directly from their subpaths:
import { showConfirmDialog } from '@brumaombra/ui-vintage/confirm-dialog';
import { setBusy } from '@brumaombra/ui-vintage/busy-indicator';
// Ask for confirmation before starting a destructive action
const confirmed = await showConfirmDialog({
title: 'Delete project?',
message: 'This action cannot be undone.'
});
if (confirmed) {
// Keep the shared loading overlay visible while the request is running
setBusy(true, { label: 'Deleting project...' });
await deleteProject();
setBusy(false);
}🧭 Use a layout component
Higher-level components accept slots so application navigation and content remain app-owned. The shell provides the layout; the application provides its navigation data:
<script setup>
import { DashboardShell } from '@brumaombra/ui-vintage/dashboard-shell';
// List of sections
const sidebarSections = [{
id: 'workspace',
label: 'Workspace',
items: [
{ id: 'overview', label: 'Overview', href: '/', active: true },
{ id: 'settings', label: 'Settings', href: '/settings' }
]
}];
</script>
<template>
<DashboardShell :sidebar-sections="sidebarSections">
<slot />
</DashboardShell>
</template>🧩 Components
The package exposes components through explicit subpaths. The complete public export map is maintained in package.json; common groups include:
🎛️ UI primitives
alert, alert-dialog, accordion, animated-number, avatar, badge, breadcrumb, button, calendar, card, checkbox, collapsible, combobox, command, data-table, date-picker, date-time-picker, dialog, dropdown-menu, field, file-dropzone, hover-card, input, kbd, label, native-select, number-field, pagination, pin-input, popover, progress, radio-group, scroll-area, select, separator, sheet, sidebar, skeleton, slider, spinner, stepper, switch, table, tabs, tags-input, textarea, time-picker, toggle-group, and tooltip.
🧱 Composite components
background-grid, card-grid, chip, dashboard-shell, data-list, empty-state-card, error-page, info-card, landing, landing-content, landing-footer, landing-navbar, landing-shell, load-more-button, loading-state-card, page-header, progress-component, single-value-card, text-link, and theme-selector.
💬 Runtime flows and integrations
busy, busy-indicator, confirm-dialog, message-dialog, message-toast, language-flag, language-selector, blog, content, and utils.
📚 Component catalog
COMPONENTS.md lists every entry point with its import line, components, props and defaults, events, slots, helpers, and usage examples. It is generated from the source and ships inside the package, so apps can always read the version they installed at node_modules/@brumaombra/ui-vintage/COMPONENTS.md.
Regenerate it after changing a public API, adding an entry point, or editing scripts/components-meta.mjs:
# Rewrite COMPONENTS.md from the source
npm run docs:components
# Fail when COMPONENTS.md is out of date (runs in the publish workflow)
npm run docs:components:checkNew entry points need a description and category in scripts/components-meta.mjs; the generator fails until they have one.
🤖 AI assistant skill
The package also ships a skill that teaches coding assistants to reuse UI Vintage and to read the catalog before writing UI. To enable it in a consuming app with Claude Code, copy it into the app's skills directory:
# Copy the skill shipped with the installed version
mkdir -p .claude/skills
cp -r node_modules/@brumaombra/ui-vintage/skills/ui-vintage .claude/skills/The skill only points to COMPONENTS.md, so it stays valid when you update the package. Copy it again only when the skill itself changes.
🎬 Motion system
src/styles.css ships a small motion vocabulary that every component uses and that apps can reuse:
- Easing utilities:
ease-spring,ease-bounce,ease-out-expo, andease-snappy. - Surface utilities:
uv-floating-motion(popovers, menus, tooltips),uv-modal-motion,uv-overlay-motion,uv-collapsible-motion, anduv-field(focus glow and invalid states for inputs). - Animations:
animate-uv-pop,animate-uv-fade-up,animate-uv-shake,animate-uv-shimmer,animate-uv-float, andanimate-uv-ping-soft. - Elevation:
shadow-elevated-smthroughshadow-elevated-xl, plusshadow-glow.
All durations collapse automatically when the user prefers reduced motion. Tailwind v4 animates scale, rotate, and translate as individual properties, so list those names (not transform) in custom transitions.
🖥️ Demo App
The private demo-app/ directory is a Nuxt 4 documentation-style showcase for manual verification. It is organized by category (Foundations, Actions, Forms, Data display, Navigation, Overlays, Feedback, Layouts), shows every component with a live preview and a copyable code tab, includes a live mini-app on the overview page, and has a global command palette (Ctrl/⌘ + K) to jump to any section. The navigation model lives in demo-app/app/utils/demo-navigation.ts.
The demo app is not part of the published package and is not intended to be installed by consumers. It imports the package through file:.., so it exercises the same source that is published to npm.
▶️ Run the showcase locally
From the repository root:
# Install dependencies for the demo app
npm --prefix demo-app install
# Start the Nuxt development server
npm --prefix demo-app run devOpen the local URL printed by Nuxt. To create a production build of the showcase:
# Build the private verification app
npm --prefix demo-app run build⚙️ Configuration
🎨 Styles
The module injects the shared stylesheet automatically. The stylesheet is also available as an explicit export when an app needs to control loading order:
// Import the shared tokens and component styles explicitly when needed
import '@brumaombra/ui-vintage/style.css';Avoid creating a second theme-token system in the consuming app. Extend the existing CSS custom properties in your application stylesheet when a project needs additional brand values.
🌍 Localization
The package includes English, Italian, French, Spanish, German, Portuguese, Chinese, Japanese, and Russian library messages. When @nuxtjs/i18n or Vue I18n is configured, the runtime plugin merges these messages into the existing composer without replacing application messages.
Application-specific translations remain owned by the consuming app. The library only contributes messages under its own uiVintage namespace.
🧭 Imports
Use explicit subpaths for public imports:
// Import a component from its stable public entrypoint
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@brumaombra/ui-vintage/tabs';
// Import shared helper behavior from its dedicated entrypoint
import { showMessageToast } from '@brumaombra/ui-vintage/message-toast';Do not import internal files from src/components/ in application code. Internal paths are implementation details and may change independently of public entrypoints.
📦 Publishing Model
UI Vintage publishes the Nuxt module entrypoint and source files directly:
module.mjsis the package entrypoint registered by Nuxt.src/contains the published components, styles, helpers, locale messages, and runtime plugin.package.jsondefines the public component subpath exports.COMPONENTS.mdandskills/ship the generated component catalog and the AI assistant skill.dist/is generated repository output and is not the source of truth for consumers.
There is no required library build step before publishing. The normal verification command is:
# Check the published TypeScript and Vue source without generating dist output
npm run typecheckFor the release process, see the project skill in .claude/skills/publish-npm/SKILL.md. The package is published under the public npm scope @brumaombra/ui-vintage.
🧰 Requirements
- Node.js compatible with the Nuxt 4 version used by the consuming app.
- Nuxt 4.
- Vue 3.5 or newer.
@nuxt/imagefor image-enabled components. The module installs it when needed.vue-i18nwhen using the library’s localization integration.
🛠️ Troubleshooting
- Components cannot resolve Nuxt imports: confirm the app is Nuxt 4 and that
@brumaombra/ui-vintageis registered innuxt.config.js. - Styles are missing: check that the module is registered once and restart the Nuxt dev server after changing
nuxt.config.js. NuxtImgis unavailable: install@nuxt/imageor allow the UI Vintage module to install it during Nuxt setup.- Translations do not appear: configure Vue I18n or
@nuxtjs/i18n; the runtime plugin merges messages only when an i18n composer is available. - A component import fails: use the public subpath listed in
package.json, such as@brumaombra/ui-vintage/button, rather than an internalsrc/path. - The demo app does not start: run
npm --prefix demo-app installfrom the repository root, then retrynpm --prefix demo-app run dev.
📄 License
This project is released under the MIT License. See LICENSE for the full license text.
