@brumaombra/ui-vintage

中文文档
0.9.48发布于 2026/11/08MITES Module构建来源已验证近 30 天下载 7414

Source-published Nuxt UI runtime with shared theme tokens and built-in Nuxt integrations.

原始使用文档
文档目录22 节
  1. Why UI Vintage
  2. Installation
  3. Usage
  4. Use a component
  5. Ask, confirm, and notify
  6. Showcase
  7. Components
  8. Component catalog
  9. Using UI Vintage with AI coding agents
  10. Theming
  11. Tokens and dark mode
  12. Brand color
  13. Motion
  14. Localization
  15. Installable app
  16. How it works
  17. Only the classes the app uses
  18. Demo app
  19. Development
  20. Requirements
  21. Troubleshooting
  22. License
UI Vintage: source-published Nuxt 4 components with a retro soul

Light and dark themes · 9 built-in locales · accessible primitives on Reka UI

npm version npm downloads Nuxt 4 Vue 3.5 Tailwind CSS v4 MIT license

Installation · Usage · Showcase · Components · Theming · Component catalog · Demo app


The UI Vintage demo app: dashboard shell, hero with animated highlight, stat cards, and live preview

Why UI Vintage

UI Vintage is a Nuxt 4 module and component library for dashboards, forms, landing pages, overlays, and blogs that should look and feel like one product. It gives you low-level primitives (buttons, inputs, selects, dialogs, tabs) and complete building blocks (dashboard and landing shells, stat cards, data tables, a full blog) that share one set of theme tokens and one motion vocabulary.

  • Source-published. The package ships its Vue and TypeScript source. Your Nuxt app compiles it together with your own code, so there is no separate build artifact and every Nuxt integration (NuxtImg, #components, module hooks) keeps working.
  • Explicit imports. Every component lives behind its own subpath, such as @brumaombra/ui-vintage/button. Nothing is auto-registered, so your dependencies stay obvious and tree-shakeable.
  • Accessible by default. Interactive primitives are built on Reka UI, with keyboard support, focus management, and ARIA wiring included.
  • Motion that respects people. Springs are pure CSS linear() easings, and every animation collapses when the user prefers reduced motion.
  • More than primitives. Promise-based dialogs and toasts, dashboard and landing shells, data tables, a complete Nuxt Content blog, 9 locales, and an installable app with an offline page.
  • Ready for AI coding agents. A generated component catalog (COMPONENTS.md) and agent instructions (AGENTS.md) ship inside the package.

Installation

Install the package with its peer integrations in an existing Nuxt 4 app. The stylesheet is built with Tailwind CSS v4, the components use Vue I18n for their labels, and icons come from Hugeicons:

npm install @brumaombra/ui-vintage @nuxt/image @nuxtjs/i18n tailwindcss @tailwindcss/vite @hugeicons/core-free-icons @hugeicons/vue

Register the module, Nuxt I18n, and the Tailwind Vite plugin in nuxt.config:

import tailwindcss from '@tailwindcss/vite';

export default defineNuxtConfig({
    modules: [
        '@brumaombra/ui-vintage',
        '@nuxtjs/i18n'
    ],

    vite: {
        plugins: [
            tailwindcss()
        ]
    },

    i18n: {
        defaultLocale: 'en',
        locales: [{ code: 'en', language: 'en-US' }]
    }
});

That's it: the module injects the shared stylesheet, adds the source to Nuxt transpilation, installs @nuxt/image when your app has not registered it, and merges the library messages into your i18n setup. The components call useI18n(), so an i18n setup is required even for a single-language app. Using the blog or content components? Install @nuxt/content too.

Usage

Use a component

Import components from their public subpath:

<script setup>
import { Button } from '@brumaombra/ui-vintage/button';
import { SingleValueCard } from '@brumaombra/ui-vintage/single-value-card';
import Money03Icon from '@hugeicons/core-free-icons/Money03Icon';
</script>

<template>
    <SingleValueCard label="Revenue" :value="48290" :icon="Money03Icon" :trend="12.4" trend-label="vs last month"
        :format-options="{ style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }" />

    <Button variant="primary">Save changes</Button>
</template>

Ask, confirm, and notify

Dialogs, toasts, and the busy overlay mount themselves on first use, so you never place them in a layout:

import { showConfirmDialog } from '@brumaombra/ui-vintage/confirm-dialog';
import { showMessageToast } from '@brumaombra/ui-vintage/message-toast';
import { setBusy } from '@brumaombra/ui-vintage/busy-indicator';

// Resolves true or false with the user's choice
const confirmed = await showConfirmDialog({
    title: 'Delete project?',
    message: 'This action cannot be undone.',
    confirmButtonType: 'red'
});

if (confirmed) {
    // Keep the shared loading overlay visible while the request runs
    setBusy(true, { label: 'Deleting project...' });
    await deleteProject();
    setBusy(false);

    showMessageToast({ type: 'success', title: 'Project deleted', message: 'Everything was removed.' });
}

Showcase

Every screenshot comes from the demo app, and each one follows your GitHub theme (light or dark).

Buttons in primary, secondary, ghost, link, and tone variants, with sizes, icon buttons, and a disabled state

Buttons
Variants, tones, sizes, icon buttons, and states

Text fields with validation, a textarea with a counter, and date and time pickers

Forms
Fields with validation, counters, and date and time pickers

Data table with selection, status badges, sorting, and pagination

Data table
Typed columns, selection, animated sorting, and pagination

Stat cards with trends, a stat strip, and an animated total

Stats and numbers
Stat cards, the compact stat strip, and numbers that count up

A stack of success, info, and warning toasts fanned out in the corner

Toasts
Stacked toasts that fan out on hover and can be swiped away

A destructive confirm dialog asking to delete three invoices

Confirm dialog
Promise-based dialogs with async handlers

The blog home with an announcement, a highlighted title, stats, and the featured carousel

Blog home
Hero header with stats and a featured carousel

A blog post header with breadcrumbs, labelled details, tags, and the cover image

Blog post
Post header with labelled details, tags, and the share sidebar

Components

Every entry point, grouped as in the catalog. Each name is also its import subpath, for example @brumaombra/ui-vintage/data-table.

CategoryEntry points
Actionsbutton, dropdown-menu, kbd, load-more-button, text-link, toggle-group
Formscalendar, checkbox, combobox, date-picker, date-time-picker, field, file-dropzone, input, label, native-select, number-field, pin-input, radio-group, select, slider, slider-form-component, switch, switch-form-component, tags-input, textarea, time-picker
Data displayanimated-number, avatar, badge, card, card-grid, chip, data-list, data-table, info-card, line-chart, progress, progress-component, scroll-area, separator, single-value-card, skeleton, stat-strip, table
Navigationaccordion, breadcrumb, collapsible, pagination, sidebar, stepper, tabs
Overlaysalert-dialog, command, dialog, hover-card, popover, sheet, tooltip
Feedbackalert, busy, busy-indicator, confirm-dialog, cookie-consent, empty-state-card, loading-state-card, message-dialog, message-toast, spinner
Layouts and app shellbackground-grid, dashboard-shell, error-page, landing, landing-content, landing-footer, landing-navbar, landing-shell, language-flag, language-selector, page-header, theme-selector
Content and blogblog, content
Utilities and typescommon-types, utils

Component catalog

COMPONENTS.md documents every entry point: its import line, props and defaults, events, slots, helpers, and examples. It is generated from the source and ships inside the package, so your app can always read the version it installed at node_modules/@brumaombra/ui-vintage/COMPONENTS.md.

Using UI Vintage with AI coding agents

The package ships AGENTS.md, a short set of instructions that teaches coding agents to reuse UI Vintage: read the catalog before writing UI, pick the right component, follow the library conventions, and check the result.

Point your agent to it from your app's own instructions file, so it always reads the version you installed:

  • Codex, Cursor, GitHub Copilot, Gemini, and other tools that read AGENTS.md: add this line to the AGENTS.md at the root of your app:

    Before writing UI, read and follow node_modules/@brumaombra/ui-vintage/AGENTS.md.
    
  • Claude Code: import it in your app's CLAUDE.md:

    @node_modules/@brumaombra/ui-vintage/AGENTS.md
    

Both the instructions and the catalog update with the package, so you never need to copy them again.

Theming

Tokens and dark mode

The stylesheet defines semantic tokens (background, foreground, card, primary, muted, border, and more) for both themes. Dark mode follows the .dark class on <html>; ThemeSelector sets it for you, with light, dark, and automatic modes. Extend the existing CSS custom properties in your app stylesheet instead of creating a second token system.

The whole page uses one font, the --font-mono token (the system monospace by default), so font-mono elements match the text. Set your own once, in any stylesheet of the app:

:root {
    --font-mono: 'JetBrains Mono', monospace;
}

Brand color

The brand color (buttons, focus rings, selected states, the sidebar) comes from six tokens, so an app can replace the default orange in its own stylesheet, for both themes:

:root {
    --uv-primary-light: #c62d3a;
    --uv-primary-hover-light: #ad2230;
    --uv-primary-foreground-light: #ffffff; /* Text on the brand color */
    --uv-primary-dark: #ef5b67;
    --uv-primary-hover-dark: #f47d86;
    --uv-primary-foreground-dark: #260609;
}

The library's defaults have zero specificity, so these win whatever the order of the style sheets. To change the color at runtime (a theme per customer, per mode, per selected item), set the same tokens under an attribute of <html>, such as :root[data-brand='teal'] { … }, and switch the attribute. Keep the foreground readable on the brand color in both themes.

The standalone: variant styles the installed app (opened from the home screen, or from the Play Store as a Trusted Web Activity) through @media (display-mode: standalone), for example standalone:hidden on a footer that only belongs in the browser, or standalone:h-dvh on a screen that must fit without scrolling. It needs no script, so the page never flashes the browser look first.

The module injects the stylesheet automatically. Import it explicitly only when you need to control the loading order (the import gets the same stylesheet the module injects, with the classes of the components the app uses):

import '@brumaombra/ui-vintage/style.css';

LandingNavbar, LandingFooter, and DashboardShell accept app-logo and app-logo-dark, so your logo can follow the theme too.

Motion

Every component uses the same small vocabulary, and your app can reuse it:

  • Easings: ease-spring, ease-bounce, ease-out-expo, and ease-snappy.
  • Surfaces: uv-floating-motion (popovers, menus, tooltips), uv-modal-motion, uv-overlay-motion, uv-collapsible-motion, and uv-field (focus ring and invalid states for inputs).
  • Animations: animate-uv-pop, animate-uv-fade-up, animate-uv-shake, animate-uv-shimmer, animate-uv-float, animate-uv-ping-soft, animate-uv-word-in, animate-uv-grow-x, and animate-uv-draw.
  • Elevation: shadow-elevated-sm through shadow-elevated-xl.

All durations collapse when the user prefers reduced motion. Tailwind v4 animates scale, rotate, and translate as separate properties, so name them (not transform) in custom transitions.

Localization

Library strings live under the uiVintage namespace and are available in English, Italian, French, Spanish, German, Portuguese, Chinese, Japanese, and Russian. With @nuxtjs/i18n or Vue I18n configured, the runtime plugin merges them into your existing composer without replacing your messages.

Installable app

Set uiVintage: { pwa: { name: 'Acme' } } to make the app installable, with a service worker, an offline page, a manifest per language, push notifications, and the Android app (Trusted Web Activity), with no extra dependencies. See docs/pwa.md for every option.

How it works

When the module is registered, it:

  1. Injects src/styles.css into the app once, with Tailwind scanning only the library files the app uses (see below).
  2. Adds the published src/ directory to Nuxt transpilation.
  3. Installs @nuxt/image when the app has not registered it.
  4. Registers the library i18n plugin, which merges the locale messages when Vue I18n is present.
  5. On iPhone and iPad, adds maximum-scale=1 to the viewport, so Safari doesn't zoom the page in when a field is tapped (people can still pinch to zoom). Fields keep their size on every device.
  6. With pwa set, writes the service worker and the offline page as static files of the build, and registers the worker in production.

Only the classes the app uses

On every build, the module follows the app's @brumaombra/ui-vintage/... imports through the library and tells Tailwind to scan only the files it reached, which cuts the CSS by 12% to 59% on the apps that use it. Only literal imports from a public subpath are found; set uiVintage: { scanAllComponents: true } to scan the whole library (for example, while debugging a missing class). Files under src/components/ are implementation details and may change between versions.

Demo app

demo-app/ is a documentation-style Nuxt 4 app that shows every component with a live preview and a copyable code tab, a live mini-app on the overview page, a complete blog built with Nuxt Content, and a command palette (Ctrl/⌘ + K) to jump to any section. It imports the package through file:.., so it runs the same source that is published to npm.

It needs Node.js 24.21 or newer. Nuxt Content uses the built-in node:sqlite module instead of a native SQLite build.

# Install and start the demo
npm --prefix demo-app install
npm --prefix demo-app run dev

# Production build
npm --prefix demo-app run build

Development

There is no build step: the published files are module.mjs, src/, COMPONENTS.md, and AGENTS.md. Before opening a pull request, run the checks the release workflow runs:

# Type-check the published TypeScript and Vue source
npm run typecheck

# Run the tests (unit tests in Node, component tests inside the demo Nuxt app)
npm test

# Regenerate the component catalog after changing a public API
npm run docs:components

# Fail when COMPONENTS.md is out of date
npm run docs:components:check

New entry points need an export in package.json and a description and category in scripts/components-meta.mjs; the catalog generator fails until they have one. The release process is described in .claude/skills/publish-npm/SKILL.md.

Requirements

  • Nuxt 4 and Vue 3.5 or newer.
  • Tailwind CSS v4 with @tailwindcss/vite registered in vite.plugins.
  • @hugeicons/core-free-icons 4.3 or newer and @hugeicons/vue 1 (the icons you pass to :icon props, and the ones the components render). Import each icon from its own module, as in the example above: the package barrel makes the build parse the whole icon set.
  • @nuxtjs/i18n 10 (the components call useI18n(), and every link renders NuxtLinkLocale).
  • @nuxt/image 2 for image components (the module installs it when needed).
  • @nuxt/content 3, only for the blog and content components.

Troubleshooting

  • Components can't resolve Nuxt imports: check that the app runs Nuxt 4 and that @brumaombra/ui-vintage is registered in nuxt.config.
  • Styles are missing: register the module once, add tailwindcss() from @tailwindcss/vite to vite.plugins, and restart the dev server after changing nuxt.config.
  • NuxtImg is unavailable: install @nuxt/image, or let the module install it during setup.
  • useI18n errors, missing translations, or NuxtLinkLocale failing to resolve: register @nuxtjs/i18n; the components need its composer and link component, and the library messages are merged into it.
  • An import fails: use the public subpath from package.json, such as @brumaombra/ui-vintage/button, never an internal src/ path.

License

MIT © Mauro Brambilla

版本与发布记录

@brumaombra/ui-vintage 共发布 80 个版本,首次发布于 2026/04/09;过去 12 个月发布了 80 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.48latest2026/11/08今天
0.9.472026/11/071 天前
0.9.462026/11/062 天前
0.9.452026/11/053 天前
0.9.442026/11/053 天前
0.9.432026/11/053 天前

关于 @brumaombra/ui-vintage 的常见问题

@brumaombra/ui-vintage 是什么?

Source-published Nuxt UI runtime with shared theme tokens and built-in Nuxt integrations.

如何安装 @brumaombra/ui-vintage?

在项目目录运行 npm install @brumaombra/ui-vintage,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:vue(^3.5.0)、nuxt(^4.0.0)、vue-i18n(^9.0.0 || ^10.0.0 || ^11.0.0)、@nuxt/image(^2.0.0)、tailwindcss(^4.0.0)、@hugeicons/vue(^1.0.0)、@hugeicons/core-free-icons(^4.3.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@brumaombra/ui-vintage 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/brumaombra__ui-vintage 是否存在。

@brumaombra/ui-vintage 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@brumaombra/ui-vintage 可以商用吗?

@brumaombra/ui-vintage 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@brumaombra/ui-vintage 的下载量有多少?

根据 npm 官方下载统计,@brumaombra/ui-vintage 在 2026/10/09 – 2026/11/07 这 30 天内被下载 7,414 次,日均约 247 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@brumaombra/ui-vintage 还在维护吗?

最新版本 0.9.48 发布于 2026/11/08(今天);至今共发布 80 个版本,其中过去 12 个月发布了 80 个;首次发布于 2026/04/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。