@brumaombra/ui-vintage 0.8.2 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/AGENTS.md +41 -0
  2. package/COMPONENTS.md +89 -18
  3. package/README.md +296 -262
  4. package/module.mjs +5 -11
  5. package/package.json +30 -12
  6. package/src/components/blog/BlogContentRenderer.vue +1 -1
  7. package/src/components/blog/BlogFAQSection.vue +1 -1
  8. package/src/components/blog/BlogHeaderSection.vue +93 -19
  9. package/src/components/blog/BlogInfoSection.vue +42 -37
  10. package/src/components/blog/BlogPostHeader.vue +181 -0
  11. package/src/components/blog/BlogProgressBar.vue +3 -5
  12. package/src/components/blog/BlogSectionTitle.vue +8 -6
  13. package/src/components/blog/BlogTagsSection.vue +16 -7
  14. package/src/components/blog/CategoryCard.vue +19 -23
  15. package/src/components/blog/HeaderCarousel.vue +17 -7
  16. package/src/components/blog/PostCard.vue +14 -21
  17. package/src/components/blog/SocialShareSidebar.vue +32 -15
  18. package/src/components/blog/TableOfContents.vue +64 -29
  19. package/src/components/blog/index.ts +1 -0
  20. package/src/components/card-grid/CardGrid.vue +3 -3
  21. package/src/components/chip/Chip.vue +1 -1
  22. package/src/components/confirm-dialog/ConfirmDialog.vue +6 -1
  23. package/src/components/confirm-dialog/confirm-dialog-state.ts +22 -22
  24. package/src/components/content/BlogList.vue +29 -52
  25. package/src/components/content/BlogTable.vue +26 -30
  26. package/src/components/content/CodeBlock.vue +99 -23
  27. package/src/components/content/Flow.vue +29 -35
  28. package/src/components/content/ProseHr.vue +6 -6
  29. package/src/components/content/ProsePre.vue +9 -4
  30. package/src/components/content/Terminal.vue +45 -21
  31. package/src/components/cookie-consent/CookieConsent.vue +1 -1
  32. package/src/components/dashboard-shell/DashboardShell.vue +24 -7
  33. package/src/components/empty-state-card/EmptyStateCard.vue +3 -3
  34. package/src/components/error-page/ErrorPage.vue +4 -4
  35. package/src/components/info-card/InfoCard.vue +4 -4
  36. package/src/components/landing/landing-footer/LandingFooter.vue +17 -12
  37. package/src/components/landing/landing-navbar/LandingNavbar.vue +22 -6
  38. package/src/components/landing/landing-shell/LandingShell.vue +2 -7
  39. package/src/components/language-flag/index.ts +1 -0
  40. package/src/components/language-selector/LanguageSelector.vue +19 -11
  41. package/src/components/load-more-button/LoadMoreButton.vue +9 -13
  42. package/src/components/loading-state-card/LoadingStateCard.vue +3 -3
  43. package/src/components/message-dialog/MessageDialog.vue +14 -12
  44. package/src/components/message-dialog/message-dialog-state.ts +1 -4
  45. package/src/components/message-toast/MessageToast.vue +28 -10
  46. package/src/components/message-toast/message-toast-state.ts +20 -0
  47. package/src/components/page-header/PageHeader.vue +1 -1
  48. package/src/components/progress-component/ProgressComponent.vue +7 -3
  49. package/src/components/single-value-card/SingleValueCard.vue +39 -40
  50. package/src/components/slider-form-component/SliderFormComponent.vue +1 -1
  51. package/src/components/stat-strip/StatStrip.vue +36 -0
  52. package/src/components/stat-strip/index.ts +2 -0
  53. package/src/components/switch-form-component/SwitchFormComponent.vue +5 -5
  54. package/src/components/text-link/TextLink.vue +1 -1
  55. package/src/components/theme-selector/ThemeSelector.vue +6 -3
  56. package/src/components/theme-selector/theme-transition.ts +15 -3
  57. package/src/components/ui/accordion/Accordion.vue +19 -19
  58. package/src/components/ui/alert/AlertTitle.vue +1 -1
  59. package/src/components/ui/alert/index.ts +1 -1
  60. package/src/components/ui/avatar/AvatarFallback.vue +2 -1
  61. package/src/components/ui/avatar/AvatarGroup.vue +13 -3
  62. package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +3 -3
  63. package/src/components/ui/busy/Busy.vue +5 -7
  64. package/src/components/ui/button/Button.vue +4 -2
  65. package/src/components/ui/button/index.ts +1 -4
  66. package/src/components/ui/calendar/Calendar.vue +12 -7
  67. package/src/components/ui/calendar/CalendarCell.vue +1 -1
  68. package/src/components/ui/calendar/CalendarCellTrigger.vue +1 -1
  69. package/src/components/ui/calendar/CalendarHeadCell.vue +1 -1
  70. package/src/components/ui/calendar/CalendarNextButton.vue +2 -2
  71. package/src/components/ui/calendar/CalendarPrevButton.vue +2 -2
  72. package/src/components/ui/card/Card.vue +5 -15
  73. package/src/components/ui/checkbox/Checkbox.vue +5 -1
  74. package/src/components/ui/combobox/ComboboxInput.vue +5 -1
  75. package/src/components/ui/combobox/ComboboxItem.vue +1 -1
  76. package/src/components/ui/combobox/ComboboxSelect.vue +2 -1
  77. package/src/components/ui/combobox/ComboboxTrigger.vue +1 -1
  78. package/src/components/ui/command/CommandGroup.vue +17 -3
  79. package/src/components/ui/command/CommandInput.vue +2 -2
  80. package/src/components/ui/command/CommandList.vue +5 -1
  81. package/src/components/ui/data-table/DataTable.vue +13 -4
  82. package/src/components/ui/date-picker/DatePicker.vue +8 -2
  83. package/src/components/ui/date-time-picker/DateTimePicker.vue +5 -1
  84. package/src/components/ui/dialog/DialogContent.vue +1 -1
  85. package/src/components/ui/dialog/DialogScrollContent.vue +1 -1
  86. package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +1 -1
  87. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
  88. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +2 -2
  89. package/src/components/ui/dropdown-menu/classes.ts +2 -0
  90. package/src/components/ui/field/Field.vue +4 -0
  91. package/src/components/ui/field/FieldLabel.vue +6 -2
  92. package/src/components/ui/field/field-context.ts +62 -0
  93. package/src/components/ui/field/index.ts +1 -0
  94. package/src/components/ui/file-dropzone/FileDropzone.vue +14 -5
  95. package/src/components/ui/input/Input.vue +5 -1
  96. package/src/components/ui/native-select/NativeSelect.vue +7 -3
  97. package/src/components/ui/number-field/NumberField.vue +7 -2
  98. package/src/components/ui/pagination/PaginationEllipsis.vue +1 -1
  99. package/src/components/ui/pagination/PaginationFirst.vue +1 -1
  100. package/src/components/ui/pagination/PaginationItem.vue +1 -1
  101. package/src/components/ui/pagination/PaginationLast.vue +1 -1
  102. package/src/components/ui/pagination/PaginationNext.vue +1 -1
  103. package/src/components/ui/pagination/PaginationPrevious.vue +1 -1
  104. package/src/components/ui/pin-input/PinInputInput.vue +2 -2
  105. package/src/components/ui/progress/Progress.vue +2 -2
  106. package/src/components/ui/radio-group/RadioGroupCard.vue +1 -1
  107. package/src/components/ui/select/SelectItem.vue +2 -2
  108. package/src/components/ui/select/SelectItemContent.vue +1 -1
  109. package/src/components/ui/select/SelectScrollDownButton.vue +1 -1
  110. package/src/components/ui/select/SelectScrollUpButton.vue +1 -1
  111. package/src/components/ui/select/SelectTrigger.vue +6 -2
  112. package/src/components/ui/sidebar/Sidebar.vue +1 -1
  113. package/src/components/ui/sidebar/SidebarGroupAction.vue +1 -1
  114. package/src/components/ui/sidebar/SidebarGroupLabel.vue +1 -1
  115. package/src/components/ui/sidebar/SidebarInset.vue +1 -1
  116. package/src/components/ui/sidebar/SidebarMenuAction.vue +1 -1
  117. package/src/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
  118. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +2 -2
  119. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
  120. package/src/components/ui/sidebar/SidebarProvider.vue +17 -6
  121. package/src/components/ui/sidebar/SidebarTrigger.vue +1 -1
  122. package/src/components/ui/sidebar/index.ts +2 -2
  123. package/src/components/ui/sidebar/utils.ts +1 -1
  124. package/src/components/ui/slider/Slider.vue +5 -4
  125. package/src/components/ui/spinner/Spinner.vue +3 -2
  126. package/src/components/ui/stepper/SimpleStepper.vue +1 -1
  127. package/src/components/ui/stepper/StepperDescription.vue +5 -3
  128. package/src/components/ui/stepper/StepperIndicator.vue +3 -3
  129. package/src/components/ui/stepper/StepperTitle.vue +5 -3
  130. package/src/components/ui/switch/Switch.vue +6 -2
  131. package/src/components/ui/tabs/TabsList.vue +1 -1
  132. package/src/components/ui/tabs/TabsTrigger.vue +1 -1
  133. package/src/components/ui/tags-input/TagsInputItemDelete.vue +1 -1
  134. package/src/components/ui/textarea/Textarea.vue +5 -1
  135. package/src/components/ui/time-picker/TimePicker.vue +2 -2
  136. package/src/components/ui/toggle-group/ToggleGroup.vue +1 -1
  137. package/src/components/ui/tooltip/TooltipContent.vue +1 -1
  138. package/src/i18n/de.json +16 -6
  139. package/src/i18n/en.json +16 -6
  140. package/src/i18n/es.json +16 -6
  141. package/src/i18n/fr.json +16 -6
  142. package/src/i18n/it.json +16 -6
  143. package/src/i18n/ja.json +16 -6
  144. package/src/i18n/pt.json +16 -6
  145. package/src/i18n/ru.json +16 -6
  146. package/src/i18n/zh.json +16 -6
  147. package/src/lib/color-tokens.ts +1 -1
  148. package/src/lib/i18n.ts +32 -4
  149. package/src/lib/utils.ts +21 -1
  150. package/src/runtime/ui-vintage-i18n-plugin.ts +11 -1
  151. package/src/styles.css +12 -16
  152. package/skills/ui-vintage/SKILL.md +0 -42
package/README.md CHANGED
@@ -1,212 +1,152 @@
1
1
  <div align="center">
2
2
 
3
- # 🎨 UI Vintage
3
+ <a href="https://github.com/brumaombra/ui-vintage">
4
+ <picture>
5
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/banner-dark.webp">
6
+ <img alt="UI Vintage: source-published Nuxt 4 components with a retro soul" src="docs/images/banner-light.webp" width="100%">
7
+ </picture>
8
+ </a>
4
9
 
5
- ### A source-published Nuxt UI runtime for focused, reusable interfaces
6
-
7
- `@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.
10
+ <p><b>84 explicit entry points</b> · <b>light and dark themes</b> · <b>9 built-in locales</b> · <b>accessible primitives on Reka UI</b></p>
8
11
 
9
12
  <p>
10
- <a href="https://github.com/brumaombra/ui-vintage"><img alt="GitHub Repo" src="https://img.shields.io/badge/github-brumaombra%2Fui--vintage-111111?logo=github"></a>
11
- <img alt="Nuxt 4" src="https://img.shields.io/badge/nuxt-4-00DC82?logo=nuxt&logoColor=white">
12
- <img alt="Vue 3" src="https://img.shields.io/badge/vue-3-42B883?logo=vue.js&logoColor=white">
13
- <img alt="npm" src="https://img.shields.io/npm/v/@brumaombra/ui-vintage?color=CB3837&logo=npm">
14
- <img alt="License MIT" src="https://img.shields.io/badge/license-MIT-2563EB">
13
+ <a href="https://www.npmjs.com/package/@brumaombra/ui-vintage"><img alt="npm version" src="https://img.shields.io/npm/v/@brumaombra/ui-vintage?style=flat-square&color=F7931E&label=npm"></a>
14
+ <a href="https://www.npmjs.com/package/@brumaombra/ui-vintage"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@brumaombra/ui-vintage?style=flat-square&color=1C2029"></a>
15
+ <img alt="Nuxt 4" src="https://img.shields.io/badge/Nuxt-4-00DC82?style=flat-square&logo=nuxt&logoColor=white">
16
+ <img alt="Vue 3.5" src="https://img.shields.io/badge/Vue-3.5-42B883?style=flat-square&logo=vue.js&logoColor=white">
17
+ <img alt="Tailwind CSS v4" src="https://img.shields.io/badge/Tailwind_CSS-v4-38BDF8?style=flat-square&logo=tailwindcss&logoColor=white">
18
+ <a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-1C2029?style=flat-square"></a>
15
19
  </p>
16
20
 
17
21
  <p>
18
- 🧩 Reusable Vue components • 🎨 Shared theme tokens • 🌍 Built-in locale messages • 🖼️ Nuxt Image integration • ⚡ Source-based publishing
22
+ <a href="#installation"><strong>Installation</strong></a> ·
23
+ <a href="#usage"><strong>Usage</strong></a> ·
24
+ <a href="#showcase"><strong>Showcase</strong></a> ·
25
+ <a href="#components"><strong>Components</strong></a> ·
26
+ <a href="#theming"><strong>Theming</strong></a> ·
27
+ <a href="COMPONENTS.md"><strong>Component catalog</strong></a> ·
28
+ <a href="#demo-app"><strong>Demo app</strong></a>
19
29
  </p>
20
30
 
21
- <p>
22
- <a href="#overview"><strong>Overview</strong></a> •
23
- <a href="#images"><strong>Images</strong></a> •
24
- <a href="#features"><strong>Features</strong></a> •
25
- <a href="#architecture"><strong>Architecture</strong></a> •
26
- <a href="#quick-start"><strong>Quick Start</strong></a> •
27
- <a href="#usage"><strong>Usage</strong></a> •
28
- <a href="#components"><strong>Components</strong></a> •
29
- <a href="#demo-app"><strong>Demo App</strong></a> •
30
- <a href="#configuration"><strong>Configuration</strong></a> •
31
- <a href="#publishing"><strong>Publishing</strong></a> •
32
- <a href="#troubleshooting"><strong>Troubleshooting</strong></a> •
33
- <a href="#license"><strong>License</strong></a>
34
- </p>
35
-
36
- </div>
37
-
38
- <a id="overview"></a>
39
- ## 📘 Overview
40
-
41
- 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.
42
-
43
- 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.
44
-
45
- This is a Nuxt library, not a generic Vue component bundle. Components rely on Nuxt runtime features including `#components`, `NuxtImg`, and module lifecycle hooks.
46
-
47
- <a id="images"></a>
48
- ## 🖼️ Images
49
-
50
- ### Screenshots
51
-
52
- <div align="center">
53
- <table width="100%" style="max-width: 768px;">
54
- <tr>
55
- <td align="center" valign="top">
56
- <img src="https://raw.githubusercontent.com/brumaombra/ui-vintage/main/docs/images/screen-1.png" alt="UI Vintage component demo controls and primary actions" width="100%">
57
- </td>
58
- <td align="center" valign="top">
59
- <img src="https://raw.githubusercontent.com/brumaombra/ui-vintage/main/docs/images/screen-2.png" alt="UI Vintage select, accordion, tabs, and component previews" width="100%">
60
- </td>
61
- </tr>
62
- <tr>
63
- <td align="center" valign="top">
64
- <img src="https://raw.githubusercontent.com/brumaombra/ui-vintage/main/docs/images/screen-3.png" alt="UI Vintage surface variants, badges, dialogs, and feedback previews" width="100%">
65
- </td>
66
- <td align="center" valign="top">
67
- <img src="https://raw.githubusercontent.com/brumaombra/ui-vintage/main/docs/images/screen-4.png" alt="UI Vintage loading, toast, and current state previews" width="100%">
68
- </td>
69
- </tr>
70
- </table>
71
- <p>
72
- <img src="https://raw.githubusercontent.com/brumaombra/ui-vintage/main/docs/images/blog.png" alt="UI Vintage landing and blog showcase" width="100%" style="max-width: 768px;">
73
- </p>
74
- <p>
75
- <sub>Component showcase, interactive states, landing surface, and full-width blog showcase</sub>
76
- </p>
77
31
  </div>
78
32
 
79
- <a id="features"></a>
80
- ## ✨ Features
81
-
82
- - Nuxt 4 module with automatic runtime integration.
83
- - Shared stylesheet and design tokens injected from `src/styles.css`.
84
- - Explicit subpath imports that keep application dependencies clear.
85
- - Vue 3 components built with Composition API and TypeScript source.
86
- - Primitive UI controls based on Reka UI where accessible behavior is required.
87
- - Reusable dashboard, landing, blog, card, field, and data-display components.
88
- - Built-in busy, confirm-dialog, message-dialog, and stacked, swipeable message-toast flows.
89
- - A spring-based motion system (pure CSS `linear()` easings) that respects `prefers-reduced-motion`.
90
- - Theme selector with light, dark, and automatic modes and a circular View Transitions reveal.
91
- - Library locale messages merged into an existing Vue I18n instance when available.
92
- - Automatic `@nuxt/image` installation for components that use `NuxtImg`.
93
- - Source publishing with no required library build step before installation.
94
-
95
- <a id="architecture"></a>
96
- ## 🏗️ Architecture
33
+ <br>
97
34
 
98
- UI Vintage is split into three cooperating layers:
35
+ <picture>
36
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/hero-dark.webp">
37
+ <img alt="The UI Vintage demo app: dashboard shell, hero with animated highlight, stat cards, and live preview" src="docs/images/hero-light.webp" width="100%">
38
+ </picture>
99
39
 
100
- - 🧩 **Components** in `src/components/` contain the public Vue UI and composite layouts.
101
- - 🎨 **Styles and helpers** in `src/styles.css` and `src/lib/` provide theme tokens, class utilities, and shared behavior.
102
- - 🔌 **Nuxt runtime** in `module.mjs` and `src/runtime/` integrates the package with the consuming app.
40
+ ## Why UI Vintage
103
41
 
104
- ### 🔄 Module Behavior
42
+ 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.
105
43
 
106
- When the module is registered, it:
44
+ - **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.
45
+ - **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.
46
+ - **Accessible by default.** Interactive primitives are built on [Reka UI](https://reka-ui.com), with keyboard support, focus management, and ARIA wiring included.
47
+ - **Motion that respects people.** Springs are pure CSS `linear()` easings, and every animation collapses when the user prefers reduced motion.
107
48
 
108
- 1. Injects `src/styles.css` into the Nuxt application once.
109
- 2. Adds the published `src/` directory to Nuxt transpilation.
110
- 3. Installs `@nuxt/image` when the consuming app has not already registered it.
111
- 4. Registers the library i18n plugin.
112
- 5. Merges the library’s locale messages into the app’s Vue I18n composer when Vue I18n is present.
49
+ ## Features
113
50
 
114
- The module does not auto-register every component. Import components and helpers explicitly from their public subpaths.
51
+ - **Nuxt 4 module** that injects the stylesheet, transpiles the source, installs `@nuxt/image` when needed, and merges the library locale messages into Vue I18n.
52
+ - **Shared design tokens** for colors, borders, and elevation, with light, dark, and automatic themes and a circular View Transitions reveal when switching.
53
+ - **Primitives on Reka UI:** select, combobox, dialog, sheet, popover, tooltip, tabs, stepper, slider, calendar, date and time pickers, PIN input, tags input, and more.
54
+ - **Composite building blocks:** dashboard and landing shells, page headers, stat cards and stat strips, data tables with animated sorting, card grids with empty and loading states.
55
+ - **Promise-based flows:** `showConfirmDialog`, `showMessageDialog`, stacked and swipeable `showMessageToast`, and a shared busy overlay with `setBusy`.
56
+ - **A complete blog** for Nuxt Content: hero header, featured carousel, post header, table of contents, share sidebar, FAQ, reading progress, and VS Code-style code blocks.
57
+ - **Localization** in English, Italian, French, Spanish, German, Portuguese, Chinese, Japanese, and Russian.
58
+ - **Ready for AI coding agents:** a generated component catalog ([`COMPONENTS.md`](COMPONENTS.md)) and agent instructions ([`AGENTS.md`](AGENTS.md)) ship inside the package.
115
59
 
116
- ### 📁 Repository Layout
60
+ ## Installation
117
61
 
118
- ```text
119
- src/
120
- components/ # Public components and UI primitives
121
- i18n/ # Library locale messages
122
- lib/ # Shared helpers and token utilities
123
- runtime/ # Nuxt runtime plugins
124
- styles.css # Shared design tokens and component styles
125
- module.mjs # Nuxt module entrypoint
126
- demo-app/ # Private Nuxt showcase and manual verification app
127
- ```
128
-
129
- <a id="quick-start"></a>
130
- ## 🚀 Quick Start
131
-
132
- ### 📦 Install the package
133
-
134
- Install the UI Vintage library and the peer integrations used by the package in an existing Nuxt 4 application:
62
+ 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:
135
63
 
136
64
  ```bash
137
- npm install @brumaombra/ui-vintage @nuxt/image vue-i18n
65
+ npm install @brumaombra/ui-vintage @nuxt/image @nuxtjs/i18n tailwindcss @tailwindcss/vite @hugeicons/core-free-icons @hugeicons/vue
138
66
  ```
139
67
 
140
- ### 🔌 Register the module
141
-
142
- Add the module to `nuxt.config.js`:
68
+ Register the module, Nuxt I18n, and the Tailwind Vite plugin in `nuxt.config`:
143
69
 
144
70
  ```js
71
+ import tailwindcss from '@tailwindcss/vite';
72
+
145
73
  export default defineNuxtConfig({
146
74
  modules: [
147
- '@brumaombra/ui-vintage'
148
- ]
75
+ '@brumaombra/ui-vintage',
76
+ '@nuxtjs/i18n'
77
+ ],
78
+
79
+ vite: {
80
+ plugins: [
81
+ tailwindcss()
82
+ ]
83
+ },
84
+
85
+ i18n: {
86
+ defaultLocale: 'en',
87
+ locales: [{ code: 'en', language: 'en-US' }]
88
+ }
149
89
  });
150
90
  ```
151
91
 
152
- `@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.
92
+ 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.
153
93
 
154
- <a id="usage"></a>
155
- ## 🧪 Usage
94
+ ## Usage
156
95
 
157
- ### 🧱 Use a component
96
+ ### Use a component
158
97
 
159
- Components are imported from explicit package subpaths:
98
+ Import components from their public subpath:
160
99
 
161
100
  ```vue
162
101
  <script setup>
163
102
  import { Button } from '@brumaombra/ui-vintage/button';
164
- import { Card, CardContent } from '@brumaombra/ui-vintage/card';
103
+ import { SingleValueCard } from '@brumaombra/ui-vintage/single-value-card';
104
+ import Money03Icon from '@hugeicons/core-free-icons/Money03Icon';
165
105
  </script>
166
106
 
167
107
  <template>
168
- <Card>
169
- <CardContent class="flex items-center justify-between gap-4">
170
- <span>Workspace status</span>
171
-
172
- <!-- For example, the Button component accepts variants such as primary, secondary, gray, and ghost -->
173
- <Button variant="primary">Save changes</Button>
174
- </CardContent>
175
- </Card>
108
+ <SingleValueCard label="Revenue" :value="48290" :icon="Money03Icon" :trend="12.4" trend-label="vs last month"
109
+ :format-options="{ style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }" />
110
+
111
+ <Button variant="primary">Save changes</Button>
176
112
  </template>
177
113
  ```
178
114
 
179
- ### 🔔 Use a message flow
115
+ ### Ask, confirm, and notify
180
116
 
181
- The dialog and busy helpers can be imported directly from their subpaths:
117
+ Dialogs, toasts, and the busy overlay mount themselves on first use, so you never place them in a layout:
182
118
 
183
119
  ```js
184
120
  import { showConfirmDialog } from '@brumaombra/ui-vintage/confirm-dialog';
121
+ import { showMessageToast } from '@brumaombra/ui-vintage/message-toast';
185
122
  import { setBusy } from '@brumaombra/ui-vintage/busy-indicator';
186
123
 
187
- // Ask for confirmation before starting a destructive action
124
+ // Resolves true or false with the user's choice
188
125
  const confirmed = await showConfirmDialog({
189
126
  title: 'Delete project?',
190
- message: 'This action cannot be undone.'
127
+ message: 'This action cannot be undone.',
128
+ confirmButtonType: 'red'
191
129
  });
192
130
 
193
131
  if (confirmed) {
194
- // Keep the shared loading overlay visible while the request is running
132
+ // Keep the shared loading overlay visible while the request runs
195
133
  setBusy(true, { label: 'Deleting project...' });
196
134
  await deleteProject();
197
135
  setBusy(false);
136
+
137
+ showMessageToast({ type: 'success', title: 'Project deleted', message: 'Everything was removed.' });
198
138
  }
199
139
  ```
200
140
 
201
- ### 🧭 Use a layout component
141
+ ### Build a layout
202
142
 
203
- Higher-level components accept slots so application navigation and content remain app-owned. The shell provides the layout; the application provides its navigation data:
143
+ Shells own the layout and responsive behavior; your app owns the navigation data and the content:
204
144
 
205
145
  ```vue
206
146
  <script setup>
207
147
  import { DashboardShell } from '@brumaombra/ui-vintage/dashboard-shell';
208
148
 
209
- // List of sections
149
+ // Sidebar sections and their links
210
150
  const sidebarSections = [{
211
151
  id: 'workspace',
212
152
  label: 'Workspace',
@@ -218,168 +158,262 @@ const sidebarSections = [{
218
158
  </script>
219
159
 
220
160
  <template>
221
- <DashboardShell :sidebar-sections="sidebarSections">
161
+ <DashboardShell app-name="Acme" app-logo="/logo.svg" app-logo-dark="/logo-dark.svg" :sidebar-sections="sidebarSections">
222
162
  <slot />
223
163
  </DashboardShell>
224
164
  </template>
225
165
  ```
226
166
 
227
- <a id="components"></a>
228
- ## 🧩 Components
229
-
230
- The package exposes components through explicit subpaths. The complete public export map is maintained in `package.json`; common groups include:
167
+ <a id="showcase"></a>
168
+ ## Showcase
169
+
170
+ Every screenshot comes from the [demo app](#demo-app), and each one follows your GitHub theme (light or dark).
171
+
172
+ <table>
173
+ <tr>
174
+ <td width="50%" valign="top">
175
+ <picture>
176
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/buttons-dark.webp">
177
+ <img alt="Buttons in primary, secondary, ghost, link, and tone variants, with sizes, icon buttons, and a disabled state" src="docs/images/buttons-light.webp" width="100%">
178
+ </picture>
179
+ <p align="center"><b>Buttons</b><br><sub>Variants, tones, sizes, icon buttons, and states</sub></p>
180
+ </td>
181
+ <td width="50%" valign="top">
182
+ <picture>
183
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/forms-dark.webp">
184
+ <img alt="Text fields with validation, a textarea with a counter, and date and time pickers" src="docs/images/forms-light.webp" width="100%">
185
+ </picture>
186
+ <p align="center"><b>Forms</b><br><sub>Fields with validation, counters, and date and time pickers</sub></p>
187
+ </td>
188
+ </tr>
189
+ <tr>
190
+ <td width="50%" valign="top">
191
+ <picture>
192
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/data-table-dark.webp">
193
+ <img alt="Data table with selection, status badges, sorting, and pagination" src="docs/images/data-table-light.webp" width="100%">
194
+ </picture>
195
+ <p align="center"><b>Data table</b><br><sub>Typed columns, selection, animated sorting, and pagination</sub></p>
196
+ </td>
197
+ <td width="50%" valign="top">
198
+ <picture>
199
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/stats-dark.webp">
200
+ <img alt="Stat cards with trends, a stat strip, and an animated total" src="docs/images/stats-light.webp" width="100%">
201
+ </picture>
202
+ <p align="center"><b>Stats and numbers</b><br><sub>Stat cards, the compact stat strip, and numbers that count up</sub></p>
203
+ </td>
204
+ </tr>
205
+ <tr>
206
+ <td width="50%" valign="top">
207
+ <picture>
208
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/navigation-dark.webp">
209
+ <img alt="Horizontal and vertical tabs above horizontal and vertical steppers" src="docs/images/navigation-light.webp" width="100%">
210
+ </picture>
211
+ <p align="center"><b>Navigation</b><br><sub>Tabs with a gliding indicator and horizontal or vertical steppers</sub></p>
212
+ </td>
213
+ <td width="50%" valign="top">
214
+ <picture>
215
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/alerts-dark.webp">
216
+ <img alt="Alerts in neutral, success, warning, and error tones" src="docs/images/alerts-light.webp" width="100%">
217
+ </picture>
218
+ <p align="center"><b>Alerts</b><br><sub>Tone surfaces for neutral, success, warning, and error messages</sub></p>
219
+ </td>
220
+ </tr>
221
+ <tr>
222
+ <td width="50%" valign="top">
223
+ <picture>
224
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/toasts-dark.webp">
225
+ <img alt="A stack of success, info, and warning toasts fanned out in the corner" src="docs/images/toasts-light.webp" width="100%">
226
+ </picture>
227
+ <p align="center"><b>Toasts</b><br><sub>Stacked toasts that fan out on hover and can be swiped away</sub></p>
228
+ </td>
229
+ <td width="50%" valign="top">
230
+ <picture>
231
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/dialog-dark.webp">
232
+ <img alt="A destructive confirm dialog asking to delete three invoices" src="docs/images/dialog-light.webp" width="100%">
233
+ </picture>
234
+ <p align="center"><b>Confirm dialog</b><br><sub>Promise-based dialogs with async handlers</sub></p>
235
+ </td>
236
+ </tr>
237
+ <tr>
238
+ <td width="50%" valign="top">
239
+ <picture>
240
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/palette-dark.webp">
241
+ <img alt="The command palette searching the demo sections" src="docs/images/palette-light.webp" width="100%">
242
+ </picture>
243
+ <p align="center"><b>Command palette</b><br><sub>Searchable command menu with keyboard navigation</sub></p>
244
+ </td>
245
+ <td width="50%" valign="top">
246
+ <picture>
247
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/live-preview-dark.webp">
248
+ <img alt="A small dashboard built only with UI Vintage: stat cards, goals, and activity" src="docs/images/live-preview-light.webp" width="100%">
249
+ </picture>
250
+ <p align="center"><b>Built with UI Vintage</b><br><sub>A small dashboard made only from library components</sub></p>
251
+ </td>
252
+ </tr>
253
+ <tr>
254
+ <td width="50%" valign="top">
255
+ <picture>
256
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/blog-dark.webp">
257
+ <img alt="The blog home with an announcement, a highlighted title, stats, and the featured carousel" src="docs/images/blog-light.webp" width="100%">
258
+ </picture>
259
+ <p align="center"><b>Blog home</b><br><sub>Hero header with stats and a featured carousel</sub></p>
260
+ </td>
261
+ <td width="50%" valign="top">
262
+ <picture>
263
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/post-dark.webp">
264
+ <img alt="A blog post header with breadcrumbs, labelled details, tags, and the cover image" src="docs/images/post-light.webp" width="100%">
265
+ </picture>
266
+ <p align="center"><b>Blog post</b><br><sub>Post header with labelled details, tags, and the share sidebar</sub></p>
267
+ </td>
268
+ </tr>
269
+ </table>
270
+
271
+ ## Components
272
+
273
+ 84 entry points, grouped as in the catalog. Each name is also its import subpath, for example `@brumaombra/ui-vintage/data-table`.
274
+
275
+ | Category | Entry points |
276
+ | --- | --- |
277
+ | **Actions** | `button`, `dropdown-menu`, `kbd`, `load-more-button`, `text-link`, `toggle-group` |
278
+ | **Forms** | `calendar`, `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` |
279
+ | **Data display** | `animated-number`, `avatar`, `badge`, `card`, `card-grid`, `chip`, `data-list`, `data-table`, `info-card`, `progress`, `progress-component`, `scroll-area`, `separator`, `single-value-card`, `skeleton`, `stat-strip`, `table` |
280
+ | **Navigation** | `accordion`, `breadcrumb`, `collapsible`, `pagination`, `sidebar`, `stepper`, `tabs` |
281
+ | **Overlays** | `alert-dialog`, `command`, `dialog`, `hover-card`, `popover`, `sheet`, `tooltip` |
282
+ | **Feedback** | `alert`, `busy`, `busy-indicator`, `confirm-dialog`, `cookie-consent`, `empty-state-card`, `loading-state-card`, `message-dialog`, `message-toast`, `spinner` |
283
+ | **Layouts and app shell** | `background-grid`, `dashboard-shell`, `error-page`, `landing`, `landing-content`, `landing-footer`, `landing-navbar`, `landing-shell`, `language-flag`, `language-selector`, `page-header`, `theme-selector` |
284
+ | **Content and blog** | `blog`, `content` |
285
+ | **Utilities and types** | `common-types`, `utils` |
286
+
287
+ ### Component catalog
288
+
289
+ [`COMPONENTS.md`](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`.
290
+
291
+ ### Using UI Vintage with AI coding agents
292
+
293
+ The package ships [`AGENTS.md`](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.
294
+
295
+ Point your agent to it from your app's own instructions file, so it always reads the version you installed:
296
+
297
+ - **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:
298
+
299
+ ```md
300
+ Before writing UI, read and follow node_modules/@brumaombra/ui-vintage/AGENTS.md.
301
+ ```
302
+
303
+ - **Claude Code:** import it in your app's `CLAUDE.md`:
304
+
305
+ ```md
306
+ @node_modules/@brumaombra/ui-vintage/AGENTS.md
307
+ ```
308
+
309
+ Both the instructions and the catalog update with the package, so you never need to copy them again.
310
+
311
+ ## Theming
312
+
313
+ ### Tokens and dark mode
314
+
315
+ 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.
316
+
317
+ The module injects the stylesheet automatically. Import it explicitly only when you need to control the loading order:
231
318
 
232
- ### 🎛️ UI primitives
233
-
234
- `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`.
319
+ ```js
320
+ import '@brumaombra/ui-vintage/style.css';
321
+ ```
235
322
 
236
- ### 🧱 Composite components
323
+ `LandingNavbar`, `LandingFooter`, and `DashboardShell` accept `app-logo` and `app-logo-dark`, so your logo can follow the theme too.
237
324
 
238
- `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`.
325
+ ### Motion
239
326
 
240
- ### 💬 Runtime flows and integrations
327
+ Every component uses the same small vocabulary, and your app can reuse it:
241
328
 
242
- `busy`, `busy-indicator`, `confirm-dialog`, `message-dialog`, `message-toast`, `language-flag`, `language-selector`, `blog`, `content`, and `utils`.
329
+ - **Easings:** `ease-spring`, `ease-bounce`, `ease-out-expo`, and `ease-snappy`.
330
+ - **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).
331
+ - **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`.
332
+ - **Elevation:** `shadow-elevated-sm` through `shadow-elevated-xl`.
243
333
 
244
- ### 📚 Component catalog
334
+ 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.
245
335
 
246
- [`COMPONENTS.md`](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`.
336
+ ### Localization
247
337
 
248
- Regenerate it after changing a public API, adding an entry point, or editing `scripts/components-meta.mjs`:
338
+ 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.
249
339
 
250
- ```bash
251
- # Rewrite COMPONENTS.md from the source
252
- npm run docs:components
253
-
254
- # Fail when COMPONENTS.md is out of date (runs in the publish workflow)
255
- npm run docs:components:check
256
- ```
340
+ ## How it works
257
341
 
258
- New entry points need a description and category in `scripts/components-meta.mjs`; the generator fails until they have one.
342
+ When the module is registered, it:
259
343
 
260
- ### 🤖 AI assistant skill
344
+ 1. Injects `src/styles.css` into the app once.
345
+ 2. Adds the published `src/` directory to Nuxt transpilation.
346
+ 3. Installs `@nuxt/image` when the app has not registered it.
347
+ 4. Registers the library i18n plugin, which merges the locale messages when Vue I18n is present.
261
348
 
262
- 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:
349
+ The module does not auto-register components. Always import from a public subpath; files under `src/components/` are implementation details and may change between versions.
263
350
 
264
- ```bash
265
- # Copy the skill shipped with the installed version
266
- mkdir -p .claude/skills
267
- cp -r node_modules/@brumaombra/ui-vintage/skills/ui-vintage .claude/skills/
351
+ ```text
352
+ src/
353
+ components/ Public components and UI primitives
354
+ i18n/ Library locale messages
355
+ lib/ Shared helpers and token utilities
356
+ runtime/ Nuxt runtime plugin
357
+ styles.css Design tokens, motion, and component styles
358
+ module.mjs Nuxt module entry point
359
+ demo-app/ Private showcase app (not published)
268
360
  ```
269
361
 
270
- 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.
271
-
272
- ### 🎬 Motion system
273
-
274
- `src/styles.css` ships a small motion vocabulary that every component uses and that apps can reuse:
275
-
276
- - Easing utilities: `ease-spring`, `ease-bounce`, `ease-out-expo`, and `ease-snappy`.
277
- - Surface utilities: `uv-floating-motion` (popovers, menus, tooltips), `uv-modal-motion`, `uv-overlay-motion`, `uv-collapsible-motion`, and `uv-field` (focus glow and invalid states for inputs).
278
- - Animations: `animate-uv-pop`, `animate-uv-fade-up`, `animate-uv-shake`, `animate-uv-shimmer`, `animate-uv-float`, and `animate-uv-ping-soft`.
279
- - Elevation: `shadow-elevated-sm` through `shadow-elevated-xl`, plus `shadow-glow`.
280
-
281
- 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.
282
-
283
362
  <a id="demo-app"></a>
284
- ## 🖥️ Demo App
363
+ ## Demo app
285
364
 
286
- 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`.
365
+ `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.
287
366
 
288
- 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.
289
-
290
- ### ▶️ Run the showcase locally
291
-
292
- From the repository root:
367
+ It needs Node.js 24.21 or newer. Nuxt Content uses the built-in `node:sqlite` module instead of a native SQLite build.
293
368
 
294
369
  ```bash
295
- # Install dependencies for the demo app
370
+ # Install and start the demo
296
371
  npm --prefix demo-app install
297
-
298
- # Start the Nuxt development server
299
372
  npm --prefix demo-app run dev
300
- ```
301
-
302
- Open the local URL printed by Nuxt. To create a production build of the showcase:
303
373
 
304
- ```bash
305
- # Build the private verification app
374
+ # Production build
306
375
  npm --prefix demo-app run build
307
376
  ```
308
377
 
309
- <a id="configuration"></a>
310
- ## ⚙️ Configuration
311
-
312
- ### 🎨 Styles
378
+ ## Development
313
379
 
314
- The module injects the shared stylesheet automatically. The stylesheet is also available as an explicit export when an app needs to control loading order:
380
+ The tests live in `demo-app/tests`: `unit` covers the helpers, the dialog and toast state, theme storage, and the locale files in plain Node, while `app` mounts the components inside the demo Nuxt app with `@nuxt/test-utils`.
315
381
 
316
- ```js
317
- // Import the shared tokens and component styles explicitly when needed
318
- import '@brumaombra/ui-vintage/style.css';
319
- ```
382
+ There is no build step: the published files are `module.mjs`, `src/`, `COMPONENTS.md`, and `AGENTS.md`. When a version tag is pushed, the GitHub Actions workflow runs the type check, the catalog check, and the tests, and publishes to npm only if they pass. Before opening a pull request or publishing, run the same checks locally:
320
383
 
321
- 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.
322
-
323
- ### 🌍 Localization
324
-
325
- 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.
326
-
327
- Application-specific translations remain owned by the consuming app. The library only contributes messages under its own `uiVintage` namespace.
328
-
329
- ### 🧭 Imports
330
-
331
- Use explicit subpaths for public imports:
332
-
333
- ```js
334
- // Import a component from its stable public entrypoint
335
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '@brumaombra/ui-vintage/tabs';
336
-
337
- // Import shared helper behavior from its dedicated entrypoint
338
- import { showMessageToast } from '@brumaombra/ui-vintage/message-toast';
339
- ```
340
-
341
- Do not import internal files from `src/components/` in application code. Internal paths are implementation details and may change independently of public entrypoints.
342
-
343
- <a id="publishing"></a>
344
- ## 📦 Publishing Model
345
-
346
- UI Vintage publishes the Nuxt module entrypoint and source files directly:
384
+ ```bash
385
+ # Type-check the published TypeScript and Vue source
386
+ npm run typecheck
347
387
 
348
- - `module.mjs` is the package entrypoint registered by Nuxt.
349
- - `src/` contains the published components, styles, helpers, locale messages, and runtime plugin.
350
- - `package.json` defines the public component subpath exports.
351
- - `COMPONENTS.md` and `skills/` ship the generated component catalog and the AI assistant skill.
352
- - `dist/` is generated repository output and is not the source of truth for consumers.
388
+ # Run the tests (unit tests in Node, component tests inside the demo Nuxt app)
389
+ npm test
353
390
 
354
- There is no required library build step before publishing. The normal verification command is:
391
+ # Regenerate the component catalog after changing a public API
392
+ npm run docs:components
355
393
 
356
- ```bash
357
- # Check the published TypeScript and Vue source without generating dist output
358
- npm run typecheck
394
+ # Fail when COMPONENTS.md is out of date
395
+ npm run docs:components:check
359
396
  ```
360
397
 
361
- For the release process, see the project skill in [`.claude/skills/publish-npm/SKILL.md`](.claude/skills/publish-npm/SKILL.md). The package is published under the public npm scope `@brumaombra/ui-vintage`.
398
+ 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`](.claude/skills/publish-npm/SKILL.md).
362
399
 
363
- <a id="requirements"></a>
364
- ## 🧰 Requirements
400
+ ## Requirements
365
401
 
366
- - Node.js compatible with the Nuxt 4 version used by the consuming app.
367
- - Nuxt 4.
368
- - Vue 3.5 or newer.
369
- - `@nuxt/image` for image-enabled components. The module installs it when needed.
370
- - `vue-i18n` when using the library’s localization integration.
402
+ - Nuxt 4 and Vue 3.5 or newer.
403
+ - Tailwind CSS v4 with `@tailwindcss/vite` registered in `vite.plugins`.
404
+ - `@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.
405
+ - `vue-i18n` 9, 10, or 11 with an i18n setup, such as `@nuxtjs/i18n` (the components call `useI18n()`).
406
+ - `@nuxt/image` 2 for image components (the module installs it when needed).
407
+ - `@nuxt/content` 3, only for the `blog` and `content` components.
371
408
 
372
- <a id="troubleshooting"></a>
373
- ## 🛠️ Troubleshooting
409
+ ## Troubleshooting
374
410
 
375
- - **Components cannot resolve Nuxt imports:** confirm the app is Nuxt 4 and that `@brumaombra/ui-vintage` is registered in `nuxt.config.js`.
376
- - **Styles are missing:** check that the module is registered once and restart the Nuxt dev server after changing `nuxt.config.js`.
377
- - **`NuxtImg` is unavailable:** install `@nuxt/image` or allow the UI Vintage module to install it during Nuxt setup.
378
- - **Translations do not appear:** configure Vue I18n or `@nuxtjs/i18n`; the runtime plugin merges messages only when an i18n composer is available.
379
- - **A component import fails:** use the public subpath listed in `package.json`, such as `@brumaombra/ui-vintage/button`, rather than an internal `src/` path.
380
- - **The demo app does not start:** run `npm --prefix demo-app install` from the repository root, then retry `npm --prefix demo-app run dev`.
411
+ - **Components can't resolve Nuxt imports:** check that the app runs Nuxt 4 and that `@brumaombra/ui-vintage` is registered in `nuxt.config`.
412
+ - **Styles are missing:** register the module once, add `tailwindcss()` from `@tailwindcss/vite` to `vite.plugins`, and restart the dev server after changing `nuxt.config`.
413
+ - **`NuxtImg` is unavailable:** install `@nuxt/image`, or let the module install it during setup.
414
+ - **`useI18n` errors or missing translations:** register `@nuxtjs/i18n` (or install Vue I18n yourself); the components need an i18n composer, and the library messages are merged into it.
415
+ - **An import fails:** use the public subpath from `package.json`, such as `@brumaombra/ui-vintage/button`, never an internal `src/` path.
381
416
 
382
- <a id="license"></a>
383
- ## 📄 License
417
+ ## License
384
418
 
385
- This project is released under the MIT License. See [LICENSE](LICENSE) for the full license text.
419
+ [MIT](LICENSE) © Mauro Brambilla