@plutocms/supabase 0.0.1-alpha.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 (99) hide show
  1. package/.agents/skills/nuxt/GENERATION.md +5 -0
  2. package/.agents/skills/nuxt/SKILL.md +55 -0
  3. package/.agents/skills/nuxt/references/advanced-hooks.md +289 -0
  4. package/.agents/skills/nuxt/references/advanced-layers.md +299 -0
  5. package/.agents/skills/nuxt/references/advanced-module-authoring.md +554 -0
  6. package/.agents/skills/nuxt/references/best-practices-data-fetching.md +357 -0
  7. package/.agents/skills/nuxt/references/best-practices-ssr.md +355 -0
  8. package/.agents/skills/nuxt/references/core-cli.md +263 -0
  9. package/.agents/skills/nuxt/references/core-config.md +162 -0
  10. package/.agents/skills/nuxt/references/core-data-fetching.md +236 -0
  11. package/.agents/skills/nuxt/references/core-deployment.md +224 -0
  12. package/.agents/skills/nuxt/references/core-directory-structure.md +269 -0
  13. package/.agents/skills/nuxt/references/core-modules.md +292 -0
  14. package/.agents/skills/nuxt/references/core-routing.md +226 -0
  15. package/.agents/skills/nuxt/references/features-components-autoimport.md +328 -0
  16. package/.agents/skills/nuxt/references/features-components.md +264 -0
  17. package/.agents/skills/nuxt/references/features-composables.md +276 -0
  18. package/.agents/skills/nuxt/references/features-server.md +265 -0
  19. package/.agents/skills/nuxt/references/features-state.md +194 -0
  20. package/.agents/skills/nuxt/references/rendering-modes.md +237 -0
  21. package/.agents/skills/nuxt-ui/SKILL.md +334 -0
  22. package/.agents/skills/nuxt-ui/references/components.md +377 -0
  23. package/.agents/skills/nuxt-ui/references/composables.md +127 -0
  24. package/.agents/skills/nuxt-ui/references/layouts/chat.md +195 -0
  25. package/.agents/skills/nuxt-ui/references/layouts/dashboard.md +220 -0
  26. package/.agents/skills/nuxt-ui/references/layouts/docs.md +141 -0
  27. package/.agents/skills/nuxt-ui/references/layouts/editor.md +167 -0
  28. package/.agents/skills/nuxt-ui/references/layouts/page.md +260 -0
  29. package/.agents/skills/nuxt-ui/references/theming.md +427 -0
  30. package/.agents/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
  31. package/.agents/skills/supabase-postgres-best-practices/CLAUDE.md +1 -0
  32. package/.agents/skills/supabase-postgres-best-practices/README.md +116 -0
  33. package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
  34. package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
  35. package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
  36. package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
  37. package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
  38. package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
  39. package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
  40. package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
  41. package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
  42. package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
  43. package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
  44. package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
  45. package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
  46. package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
  47. package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
  48. package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
  49. package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
  50. package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
  51. package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
  52. package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
  53. package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
  54. package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
  55. package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
  56. package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
  57. package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
  58. package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
  59. package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
  60. package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
  61. package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
  62. package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
  63. package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
  64. package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
  65. package/.editorconfig +12 -0
  66. package/.env.example +2 -0
  67. package/.eslintignore +1 -0
  68. package/.prettierrc +9 -0
  69. package/.vscode/extensions.json +10 -0
  70. package/.vscode/settings.json +61 -0
  71. package/README.md +62 -0
  72. package/app/components/navbar/NavbarAdmin.vue +169 -0
  73. package/app/composables/auth.ts +81 -0
  74. package/app/layouts/setup.vue +13 -0
  75. package/app/middleware/auth.ts +27 -0
  76. package/app/middleware/setup-check.ts +25 -0
  77. package/app/pages/admin/(auth)/login.vue +84 -0
  78. package/app/pages/admin/(auth)/signup.vue +128 -0
  79. package/app/pages/admin/settings.vue +116 -0
  80. package/app/pages/admin/setup.vue +300 -0
  81. package/app/pages/admin.vue +38 -0
  82. package/bun.lock +2685 -0
  83. package/eslint.config.mjs +88 -0
  84. package/nuxt.config.ts +55 -0
  85. package/package.json +44 -0
  86. package/public/schema.sql +103 -0
  87. package/scripts/supabase-typegen.ts +36 -0
  88. package/server/api/category/list.get.ts +9 -0
  89. package/server/api/category/new.post.ts +31 -0
  90. package/server/api/settings/first_setup.get.ts +39 -0
  91. package/server/api/settings/index.get.ts +27 -0
  92. package/server/api/settings/update.post.ts +36 -0
  93. package/server/api/setup/create.post.ts +58 -0
  94. package/server/api/signup.post.ts +28 -0
  95. package/server/api/users/[id].get.ts +21 -0
  96. package/server/api/users/index.get.ts +16 -0
  97. package/shared/types/supabase.ts +406 -0
  98. package/skills-lock.json +20 -0
  99. package/tsconfig.json +18 -0
@@ -0,0 +1,260 @@
1
+ # Page Layout
2
+
3
+ Build public-facing pages — landing, blog, changelog, pricing — using the Header + Main + Footer shell with Page components.
4
+
5
+ ## App shell
6
+
7
+ ```vue [app.vue]
8
+ <script setup lang="ts">
9
+ import type { NavigationMenuItem } from '@nuxt/ui'
10
+
11
+ const items = computed<NavigationMenuItem[]>(() => [{
12
+ label: 'Features',
13
+ to: '#features'
14
+ }, {
15
+ label: 'Pricing',
16
+ to: '/pricing'
17
+ }, {
18
+ label: 'Blog',
19
+ to: '/blog'
20
+ }])
21
+ </script>
22
+
23
+ <template>
24
+ <UApp>
25
+ <UHeader>
26
+ <template #title>
27
+ <Logo class="h-6 w-auto" />
28
+ </template>
29
+
30
+ <UNavigationMenu :items="items" />
31
+
32
+ <template #right>
33
+ <UColorModeButton />
34
+ <UButton label="Sign in" color="neutral" variant="ghost" />
35
+ <UButton label="Get started" />
36
+ </template>
37
+
38
+ <template #body>
39
+ <UNavigationMenu :items="items" orientation="vertical" class="-mx-2.5" />
40
+ </template>
41
+ </UHeader>
42
+
43
+ <UMain>
44
+ <NuxtPage />
45
+ </UMain>
46
+
47
+ <UFooter>
48
+ <template #left>
49
+ <p class="text-muted text-sm">Copyright © {{ new Date().getFullYear() }}</p>
50
+ </template>
51
+ <template #right>
52
+ <UButton icon="i-simple-icons-github" color="neutral" variant="ghost" to="https://github.com" target="_blank" />
53
+ </template>
54
+ </UFooter>
55
+ </UApp>
56
+ </template>
57
+ ```
58
+
59
+ ## Landing page
60
+
61
+ ```vue [pages/index.vue]
62
+ <template>
63
+ <UPageHero
64
+ title="Build faster with Nuxt UI"
65
+ description="A comprehensive Vue UI component library."
66
+ :links="[
67
+ { label: 'Get started', to: '/docs', icon: 'i-lucide-square-play' },
68
+ { label: 'Learn more', color: 'neutral', variant: 'subtle', trailingIcon: 'i-lucide-arrow-right' }
69
+ ]"
70
+ orientation="horizontal"
71
+ >
72
+ <img src="/hero-image.png" alt="App screenshot" class="rounded-lg shadow-2xl ring ring-default" />
73
+ </UPageHero>
74
+
75
+ <UPageSection
76
+ id="features"
77
+ headline="Features"
78
+ title="Everything you need"
79
+ description="A comprehensive suite of components and utilities."
80
+ :features="[
81
+ { title: 'Accessible', description: 'Built on Reka UI with full ARIA support.', icon: 'i-lucide-accessibility' },
82
+ { title: 'Customizable', description: 'Tailwind Variants theming with full control.', icon: 'i-lucide-palette' },
83
+ { title: 'Responsive', description: 'Mobile-first components.', icon: 'i-lucide-monitor-smartphone' }
84
+ ]"
85
+ />
86
+
87
+ <UPageCTA
88
+ title="Trusted by thousands of developers"
89
+ description="Join the community and start building today."
90
+ :links="[
91
+ { label: 'Get started', color: 'neutral' },
92
+ { label: 'Star on GitHub', color: 'neutral', variant: 'subtle', trailingIcon: 'i-lucide-arrow-right' }
93
+ ]"
94
+ />
95
+
96
+ <UPageSection id="pricing" headline="Pricing" title="Simple, transparent pricing">
97
+ <UPricingPlans
98
+ :plans="[
99
+ { title: 'Free', price: '$0', description: 'For personal projects', features: ['10 components', 'Community support'] },
100
+ { title: 'Pro', price: '$99', description: 'For teams', features: ['All components', 'Priority support'], highlight: true },
101
+ { title: 'Enterprise', price: 'Custom', description: 'For large teams', features: ['Custom components', 'Dedicated support'] }
102
+ ]"
103
+ />
104
+ </UPageSection>
105
+ </template>
106
+ ```
107
+
108
+ ## Blog listing
109
+
110
+ ```vue [pages/blog/index.vue]
111
+ <script setup lang="ts">
112
+ const { data: posts } = await useAsyncData('posts', () => queryCollection('posts').all())
113
+ </script>
114
+
115
+ <template>
116
+ <UPage>
117
+ <UPageHero title="Blog" description="The latest news and updates from our team." />
118
+
119
+ <UPageBody>
120
+ <UContainer>
121
+ <UBlogPosts>
122
+ <UBlogPost
123
+ v-for="(post, index) in posts"
124
+ :key="index"
125
+ v-bind="post"
126
+ :to="post.path"
127
+ />
128
+ </UBlogPosts>
129
+ </UContainer>
130
+ </UPageBody>
131
+ </UPage>
132
+ </template>
133
+ ```
134
+
135
+ ## Blog article
136
+
137
+ ```vue [pages/blog/[slug].vue]
138
+ <script setup lang="ts">
139
+ const route = useRoute()
140
+
141
+ const { data: post } = await useAsyncData(route.path, () => {
142
+ return queryCollection('posts').path(route.path).first()
143
+ })
144
+ </script>
145
+
146
+ <template>
147
+ <UPage>
148
+ <UPageHeader :title="post.title" :description="post.description" />
149
+
150
+ <UPageBody>
151
+ <ContentRenderer :value="post" />
152
+ </UPageBody>
153
+
154
+ <template #right>
155
+ <UContentToc :links="post.body.toc.links" />
156
+ </template>
157
+ </UPage>
158
+ </template>
159
+ ```
160
+
161
+ ## Changelog
162
+
163
+ ```vue [pages/changelog.vue]
164
+ <script setup lang="ts">
165
+ const { data: versions } = await useAsyncData('versions', () => queryCollection('changelog').all())
166
+ </script>
167
+
168
+ <template>
169
+ <UPage>
170
+ <UPageHero title="Changelog" />
171
+
172
+ <UPageBody>
173
+ <UContainer>
174
+ <UChangelogVersions>
175
+ <UChangelogVersion v-for="(version, index) in versions" :key="index" v-bind="version" />
176
+ </UChangelogVersions>
177
+ </UContainer>
178
+ </UPageBody>
179
+ </UPage>
180
+ </template>
181
+ ```
182
+
183
+ ## Key components
184
+
185
+ ### Page sections
186
+
187
+ - `UPageHero` — Hero with title, description, links, and optional media (`orientation`: horizontal/vertical)
188
+ - `UPageSection` — Content section with headline, title, description, and `features` grid
189
+ - `UPageCTA` — Call to action block
190
+ - `UPageHeader` — Page title and description
191
+ - `UPageBody` — Main content area with prose styling
192
+
193
+ ### Grids & cards
194
+
195
+ - `UPageGrid` / `UPageColumns` — Grid layouts
196
+ - `UPageCard` — Content card for grids
197
+ - `UPageFeature` — Individual feature item
198
+ - `UPageLogos` — Logo wall
199
+
200
+ ### Blog & changelog
201
+
202
+ - `UBlogPosts` — Responsive grid of posts (`orientation`: horizontal/vertical)
203
+ - `UBlogPost` — Individual post card
204
+ - `UChangelogVersions` / `UChangelogVersion` — Changelog entries
205
+
206
+ ### Pricing
207
+
208
+ - `UPricingPlans` — Pricing plan cards
209
+ - `UPricingTable` — Feature comparison table
210
+
211
+ ### Footer
212
+
213
+ - `UFooterColumns` — Multi-column footer with link groups
214
+
215
+ ## Variations
216
+
217
+ ### Alternating sections
218
+
219
+ ```vue
220
+ <UPageSection title="Feature A" orientation="horizontal">
221
+ <img src="/feature-a.png" />
222
+ </UPageSection>
223
+
224
+ <UPageSection title="Feature B" orientation="horizontal" reverse>
225
+ <img src="/feature-b.png" />
226
+ </UPageSection>
227
+ ```
228
+
229
+ ### Feature grid
230
+
231
+ ```vue
232
+ <UPageSection headline="Features" title="Why choose us">
233
+ <UPageGrid>
234
+ <UPageCard v-for="feature in features" :key="feature.title" v-bind="feature" />
235
+ </UPageGrid>
236
+ </UPageSection>
237
+ ```
238
+
239
+ ### Blog with sidebar
240
+
241
+ ```vue [layouts/blog.vue]
242
+ <template>
243
+ <UPage>
244
+ <template #left>
245
+ <UPageAside>
246
+ <UNavigationMenu
247
+ :items="[
248
+ { label: 'All posts', to: '/blog', icon: 'i-lucide-newspaper' },
249
+ { label: 'Tutorials', to: '/blog/tutorials', icon: 'i-lucide-graduation-cap' },
250
+ { label: 'Announcements', to: '/blog/announcements', icon: 'i-lucide-megaphone' }
251
+ ]"
252
+ orientation="vertical"
253
+ />
254
+ </UPageAside>
255
+ </template>
256
+
257
+ <slot />
258
+ </UPage>
259
+ </template>
260
+ ```
@@ -0,0 +1,427 @@
1
+ # Theming
2
+
3
+ ## Semantic colors
4
+
5
+ | Color | Default | Purpose |
6
+ |---|---|---|
7
+ | `primary` | green | CTAs, active states, brand |
8
+ | `secondary` | blue | Secondary actions |
9
+ | `success` | green | Success messages |
10
+ | `info` | blue | Informational |
11
+ | `warning` | yellow | Warnings |
12
+ | `error` | red | Errors, destructive actions |
13
+ | `neutral` | slate | Text, borders, disabled |
14
+
15
+ ## Configuring colors
16
+
17
+ ```ts
18
+ // Nuxt — app.config.ts
19
+ export default defineAppConfig({
20
+ ui: {
21
+ colors: {
22
+ primary: 'indigo',
23
+ secondary: 'violet',
24
+ success: 'emerald',
25
+ error: 'rose',
26
+ neutral: 'zinc'
27
+ }
28
+ }
29
+ })
30
+ ```
31
+
32
+ ```ts
33
+ // Vue — vite.config.ts
34
+ ui({
35
+ ui: {
36
+ colors: { primary: 'indigo', secondary: 'violet', neutral: 'zinc' }
37
+ }
38
+ })
39
+ ```
40
+
41
+ You can only use colors that exist in your theme — either [Tailwind's default colors](https://tailwindcss.com/docs/colors) or custom colors defined with `@theme`.
42
+
43
+ ## Adding custom colors
44
+
45
+ 1. Define all 11 shades in CSS:
46
+
47
+ ```css
48
+ /* assets/css/main.css */
49
+ @theme static {
50
+ --color-brand-50: #fef2f2;
51
+ --color-brand-100: #fee2e2;
52
+ --color-brand-200: #fecaca;
53
+ --color-brand-300: #fca5a5;
54
+ --color-brand-400: #f87171;
55
+ --color-brand-500: #ef4444;
56
+ --color-brand-600: #dc2626;
57
+ --color-brand-700: #b91c1c;
58
+ --color-brand-800: #991b1b;
59
+ --color-brand-900: #7f1d1d;
60
+ --color-brand-950: #450a0a;
61
+ }
62
+ ```
63
+
64
+ 2. Assign it as a semantic color value: `ui: { colors: { primary: 'brand' } }`
65
+
66
+ You can only use colors that have all shades defined — either from Tailwind's defaults or custom `@theme` definitions.
67
+
68
+ ### Extending with new semantic color names
69
+
70
+ If you need a new semantic color beyond the defaults (e.g., `tertiary`), register it in `theme.colors`:
71
+
72
+ ```ts
73
+ // Nuxt — nuxt.config.ts
74
+ export default defineNuxtConfig({
75
+ ui: {
76
+ theme: {
77
+ colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
78
+ }
79
+ }
80
+ })
81
+ ```
82
+
83
+ ```ts
84
+ // Vue — vite.config.ts
85
+ ui({
86
+ theme: {
87
+ colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
88
+ }
89
+ })
90
+ ```
91
+
92
+ Then assign it: `ui: { colors: { tertiary: 'indigo' } }` and use it via the `color` prop: `<UButton color="tertiary">`.
93
+
94
+ ## CSS utilities
95
+
96
+ ### Text
97
+
98
+ | Class | Use | Light value | Dark value |
99
+ |---|---|---|---|
100
+ | `text-default` | Body text | `neutral-700` | `neutral-200` |
101
+ | `text-muted` | Secondary text | `neutral-500` | `neutral-400` |
102
+ | `text-dimmed` | Placeholders, hints | `neutral-400` | `neutral-500` |
103
+ | `text-toned` | Subtitles | `neutral-600` | `neutral-300` |
104
+ | `text-highlighted` | Headings, emphasis | `neutral-900` | `white` |
105
+ | `text-inverted` | On dark/light backgrounds | `white` | `neutral-900` |
106
+
107
+ ### Background
108
+
109
+ | Class | Use | Light value | Dark value |
110
+ |---|---|---|---|
111
+ | `bg-default` | Page background | `white` | `neutral-900` |
112
+ | `bg-muted` | Subtle sections | `neutral-50` | `neutral-800` |
113
+ | `bg-elevated` | Cards, modals | `neutral-100` | `neutral-800` |
114
+ | `bg-accented` | Hover states | `neutral-200` | `neutral-700` |
115
+ | `bg-inverted` | Inverted sections | `neutral-900` | `white` |
116
+
117
+ ### Border
118
+
119
+ | Class | Use | Light value | Dark value |
120
+ |---|---|---|---|
121
+ | `border-default` | Default borders | `neutral-200` | `neutral-800` |
122
+ | `border-muted` | Subtle borders | `neutral-200` | `neutral-700` |
123
+ | `border-accented` | Emphasized borders | `neutral-300` | `neutral-700` |
124
+ | `border-inverted` | Inverted borders | `neutral-900` | `white` |
125
+
126
+ ### Semantic color utilities
127
+
128
+ Each semantic color (`primary`, `secondary`, `success`, `info`, `warning`, `error`) is available as a Tailwind utility: `text-primary`, `bg-primary`, `border-primary`, `ring-primary`, etc.
129
+
130
+ They resolve to shade **500** in light mode and shade **400** in dark mode (via `--ui-<color>` CSS variables). This is generated at runtime by the colors plugin — you don't need to write dark-mode variants manually.
131
+
132
+ To adjust which shade is used, override `--ui-primary` (or any semantic color) in your `main.css`:
133
+
134
+ ```css
135
+ :root { --ui-primary: var(--ui-color-primary-600); }
136
+ .dark { --ui-primary: var(--ui-color-primary-300); }
137
+ ```
138
+
139
+ ### CSS variables
140
+
141
+ All customizable in `main.css`:
142
+
143
+ ```css
144
+ :root {
145
+ --ui-radius: 0.25rem; /* base radius for all components */
146
+ --ui-container: 80rem; /* UContainer max-width */
147
+ --ui-header-height: 4rem; /* UHeader height */
148
+ --ui-primary: var(--ui-color-primary-500); /* adjust shade used */
149
+ }
150
+
151
+ .dark {
152
+ --ui-primary: var(--ui-color-primary-400);
153
+ }
154
+ ```
155
+
156
+ ### Solid colors (black/white)
157
+
158
+ ```css
159
+ :root { --ui-primary: black; }
160
+ .dark { --ui-primary: white; }
161
+ ```
162
+
163
+ ## Component theme customization
164
+
165
+ ### How it works
166
+
167
+ Components are styled with [Tailwind Variants](https://www.tailwind-variants.org/). The theme defines:
168
+
169
+ - **`slots`** — named style targets (e.g., `root`, `base`, `label`, `leadingIcon`)
170
+ - **`variants`** — styles applied based on props (e.g., `color`, `variant`, `size`)
171
+ - **`compoundVariants`** — styles for specific prop combinations (e.g., `color: 'primary'` + `variant: 'outline'`)
172
+ - **`defaultVariants`** — default prop values when none are specified
173
+
174
+ ### Override priority
175
+
176
+ **`ui` prop / `class` prop > global config > theme defaults**
177
+
178
+ The `ui` prop overrides slots **after** variants are computed. If the `size: 'md'` variant applies `size-5` to `trailingIcon`, and you set `:ui="{ trailingIcon: 'size-3' }"`, the `size-3` wins.
179
+
180
+ Tailwind Variants uses [tailwind-merge](https://github.com/dcastil/tailwind-merge) under the hood so conflicting classes are resolved automatically.
181
+
182
+ ### Understanding the generated theme
183
+
184
+ Every component's full resolved theme is generated at build time. Always read this file before customizing a component — it shows exactly what classes are applied where.
185
+
186
+ - **Nuxt**: `.nuxt/ui/<component>.ts`
187
+ - **Vue**: `node_modules/.nuxt-ui/ui/<component>.ts`
188
+
189
+ For example, the card theme:
190
+
191
+ ```ts
192
+ {
193
+ slots: {
194
+ root: "rounded-lg overflow-hidden",
195
+ header: "p-4 sm:px-6",
196
+ body: "p-4 sm:p-6",
197
+ footer: "p-4 sm:px-6"
198
+ },
199
+ variants: {
200
+ variant: {
201
+ outline: { root: "bg-default ring ring-default divide-y divide-default" },
202
+ soft: { root: "bg-elevated/50 divide-y divide-default" }
203
+ }
204
+ },
205
+ defaultVariants: { variant: "outline" }
206
+ }
207
+ ```
208
+
209
+ ### Global config
210
+
211
+ Override the theme for all instances of a component:
212
+
213
+ ```ts
214
+ // Nuxt — app.config.ts
215
+ export default defineAppConfig({
216
+ ui: {
217
+ button: {
218
+ slots: {
219
+ base: 'font-bold rounded-full'
220
+ },
221
+ variants: {
222
+ size: {
223
+ md: { leadingIcon: 'size-4' }
224
+ }
225
+ },
226
+ compoundVariants: [{
227
+ color: 'neutral',
228
+ variant: 'outline',
229
+ class: { base: 'ring-2' }
230
+ }],
231
+ defaultVariants: {
232
+ color: 'neutral',
233
+ variant: 'outline'
234
+ }
235
+ }
236
+ }
237
+ })
238
+ ```
239
+
240
+ ```ts
241
+ // Vue — vite.config.ts
242
+ ui({
243
+ ui: {
244
+ button: {
245
+ slots: { base: 'font-bold rounded-full' },
246
+ defaultVariants: { color: 'neutral', variant: 'outline' }
247
+ }
248
+ }
249
+ })
250
+ ```
251
+
252
+ ### Per-instance (`ui` prop)
253
+
254
+ Overrides slots after variant computation:
255
+
256
+ ```vue
257
+ <UButton :ui="{ base: 'font-mono', trailingIcon: 'size-3 rotate-90' }" />
258
+ <UCard :ui="{ root: 'shadow-xl', body: 'p-8' }" />
259
+ ```
260
+
261
+ ### Per-instance (`class` prop)
262
+
263
+ Overrides the `root` or `base` slot:
264
+
265
+ ```vue
266
+ <UButton class="rounded-none">Square</UButton>
267
+ ```
268
+
269
+ Components without slots (e.g., `UContainer`, `USkeleton`, `UMain`) only have the `class` prop.
270
+
271
+ ### Theme structure patterns
272
+
273
+ **Slots-based** (most components — `slots` is an object in the generated theme):
274
+
275
+ ```ts
276
+ // global config
277
+ ui: {
278
+ button: {
279
+ slots: { base: 'font-bold' }
280
+ }
281
+ }
282
+ // per instance
283
+ <UButton :ui="{ base: 'font-bold' }" />
284
+ ```
285
+
286
+ **Flat base** (`base` is a top-level string in the generated theme):
287
+
288
+ ```ts
289
+ // global config
290
+ ui: {
291
+ container: {
292
+ base: 'max-w-lg'
293
+ }
294
+ }
295
+ // per instance — class prop only
296
+ <UContainer class="max-w-lg" />
297
+ ```
298
+
299
+ Always check the generated theme file to see which pattern applies.
300
+
301
+ ## Dark mode
302
+
303
+ ```ts
304
+ const colorMode = useColorMode()
305
+ colorMode.preference = 'dark' // 'light', 'dark', 'system'
306
+ ```
307
+
308
+ ```vue
309
+ <UColorModeButton /> <!-- Toggle -->
310
+ <UColorModeSelect /> <!-- Dropdown -->
311
+ ```
312
+
313
+ ## Fonts
314
+
315
+ ```css
316
+ /* assets/css/main.css */
317
+ @theme {
318
+ --font-sans: 'Public Sans', system-ui, sans-serif;
319
+ --font-mono: 'JetBrains Mono', monospace;
320
+ }
321
+ ```
322
+
323
+ In Nuxt, fonts defined with `@theme` are automatically loaded by the `@nuxt/fonts` module.
324
+
325
+ ## Brand customization playbook
326
+
327
+ Follow these steps to fully rebrand Nuxt UI (e.g., "make a Ghibli theme", "match our corporate brand"):
328
+
329
+ ### Step 1 — Define the color palette
330
+
331
+ Pick colors that match the brand. Map them to semantic roles:
332
+
333
+ ```ts
334
+ // app.config.ts (Nuxt) or vite.config.ts (Vue)
335
+ ui: {
336
+ colors: {
337
+ primary: 'emerald', // brand accent
338
+ secondary: 'amber', // secondary accent
339
+ success: 'green',
340
+ info: 'sky',
341
+ warning: 'orange',
342
+ error: 'rose',
343
+ neutral: 'stone' // affects all text, borders, backgrounds
344
+ }
345
+ }
346
+ ```
347
+
348
+ If no Tailwind default color fits, define custom shades in CSS (see [Adding custom colors](#adding-custom-colors)):
349
+
350
+ ```css
351
+ @theme static {
352
+ --color-forest-50: #f0fdf4;
353
+ /* ... all 11 shades (50–950) ... */
354
+ --color-forest-950: #052e16;
355
+ }
356
+ ```
357
+
358
+ Then use it: `primary: 'forest'`.
359
+
360
+ ### Step 2 — Set fonts
361
+
362
+ ```css
363
+ /* assets/css/main.css */
364
+ @theme {
365
+ --font-sans: 'Quicksand', system-ui, sans-serif;
366
+ }
367
+ ```
368
+
369
+ ### Step 3 — Adjust CSS variables
370
+
371
+ ```css
372
+ :root {
373
+ --ui-radius: 0.75rem; /* rounder = softer/playful, smaller = sharper/corporate */
374
+ --ui-primary: var(--ui-color-primary-600); /* adjust which shade is used */
375
+ }
376
+
377
+ .dark {
378
+ --ui-primary: var(--ui-color-primary-400);
379
+ }
380
+ ```
381
+
382
+ ### Step 4 — Override key components globally
383
+
384
+ Read the generated theme files to find slot names, then apply global overrides:
385
+
386
+ ```ts
387
+ // app.config.ts (Nuxt) or vite.config.ts (Vue)
388
+ ui: {
389
+ // ... colors from Step 1
390
+ button: {
391
+ slots: {
392
+ base: 'rounded-full font-semibold'
393
+ },
394
+ defaultVariants: {
395
+ variant: 'soft'
396
+ }
397
+ },
398
+ card: {
399
+ slots: {
400
+ root: 'rounded-2xl shadow-lg'
401
+ }
402
+ },
403
+ badge: {
404
+ slots: {
405
+ base: 'rounded-full'
406
+ }
407
+ }
408
+ }
409
+ ```
410
+
411
+ > **Tip**: Read `.nuxt/ui/button.ts` (Nuxt) or `node_modules/.nuxt-ui/ui/button.ts` (Vue) to see all available slots and variants before overriding.
412
+
413
+ ### Step 5 — Verify dark mode
414
+
415
+ Check that both modes look correct. Adjust `--ui-primary` shade per mode and test contrast. Use `useColorMode()` to toggle during development.
416
+
417
+ ### Quick checklist
418
+
419
+ | Step | What to change | Where |
420
+ |---|---|---|
421
+ | Colors | `primary`, `secondary`, `neutral` | `app.config.ts` / `vite.config.ts` |
422
+ | Custom palette | 11 shades per color | `main.css` (`@theme static`) |
423
+ | Fonts | `--font-sans`, `--font-mono` | `main.css` (`@theme`) |
424
+ | Radius | `--ui-radius` | `main.css` (`:root`) |
425
+ | Primary shade | `--ui-primary` | `main.css` (`:root` + `.dark`) |
426
+ | Component shapes | Global slot overrides | `app.config.ts` / `vite.config.ts` |
427
+ | Dark mode | Verify contrast, adjust variables | `main.css` (`.dark`) |