From 479de29c3b9100da7ef3bef69f40509deb4005a0 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Tue, 24 Dec 2024 11:59:09 +0100 Subject: [PATCH 1/6] feat(templates): generate tailwindcss theme colors --- src/runtime/index.css | 1 + src/templates.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/runtime/index.css b/src/runtime/index.css index 725196d5b9..3621ce2c99 100644 --- a/src/runtime/index.css +++ b/src/runtime/index.css @@ -1,3 +1,4 @@ +@import '#build/ui.css'; @import './keyframes.css'; @variant dark (&:where(.dark, .dark *)); diff --git a/src/templates.ts b/src/templates.ts index 6a41418eda..02e20c30dd 100644 --- a/src/templates.ts +++ b/src/templates.ts @@ -69,6 +69,15 @@ export function getTemplates(options: ModuleOptions, uiConfig: Record `@theme default { + ${[...(options.theme?.colors || []), 'neutral'].map(color => [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950].map(shade => `--color-${color}-${shade}: var(--ui-color-${color}-${shade});`).join('\n\t')).join('\n\t')} +} +` + }) + templates.push({ filename: 'ui/index.ts', write: true, From dac5f6a5888124b6aa069db1e7a118aa35accc20 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Tue, 24 Dec 2024 11:59:20 +0100 Subject: [PATCH 2/6] docs(theme): remove limitation on colors --- docs/content/1.getting-started/3.theme.md | 57 ----------------------- 1 file changed, 57 deletions(-) diff --git a/docs/content/1.getting-started/3.theme.md b/docs/content/1.getting-started/3.theme.md index 3e506b7b7d..f1c1afaa8c 100644 --- a/docs/content/1.getting-started/3.theme.md +++ b/docs/content/1.getting-started/3.theme.md @@ -286,63 +286,6 @@ export default defineConfig({ ::: :: -::warning -These color aliases are not automatically defined as Tailwind CSS colors, so classes like `text-primary-500 dark:text-primary-400` won't be available by default as in Nuxt UI v2. This approach provides more flexibility and prevents overwriting of user-defined Tailwind CSS colors.

- -However, you can generate these classes using Tailwind's `@theme` directive, allowing you to use custom color utility classes while maintaining dynamic color aliases: - -::module-only -#ui -:::div{class="*:!mb-0 *:!mt-2.5"} - -```css [main.css] -@import "tailwindcss"; -@import "@nuxt/ui"; - -@theme { - --color-primary-50: var(--ui-color-primary-50); - --color-primary-100: var(--ui-color-primary-100); - --color-primary-200: var(--ui-color-primary-200); - --color-primary-300: var(--ui-color-primary-300); - --color-primary-400: var(--ui-color-primary-400); - --color-primary-500: var(--ui-color-primary-500); - --color-primary-600: var(--ui-color-primary-600); - --color-primary-700: var(--ui-color-primary-700); - --color-primary-800: var(--ui-color-primary-800); - --color-primary-900: var(--ui-color-primary-900); - --color-primary-950: var(--ui-color-primary-950); -} -``` - -::: - -#ui-pro -:::div{class="*:!mb-0 *:!mt-2.5"} - -```css [main.css] -@import "tailwindcss"; -@import "@nuxt/ui-pro"; - -@theme { - --color-primary-50: var(--ui-color-primary-50); - --color-primary-100: var(--ui-color-primary-100); - --color-primary-200: var(--ui-color-primary-200); - --color-primary-300: var(--ui-color-primary-300); - --color-primary-400: var(--ui-color-primary-400); - --color-primary-500: var(--ui-color-primary-500); - --color-primary-600: var(--ui-color-primary-600); - --color-primary-700: var(--ui-color-primary-700); - --color-primary-800: var(--ui-color-primary-800); - --color-primary-900: var(--ui-color-primary-900); - --color-primary-950: var(--ui-color-primary-950); -} -``` - -::: -:: - -:: - ### Tokens Nuxt UI leverages a robust system of CSS variables as design tokens to ensure consistent and flexible component styling. These tokens form the foundation of the theming system, offering smooth support for both light and dark modes. From 627bec37d3ee8a33495446a973a8dc094d57192e Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 6 Feb 2025 14:42:48 +0100 Subject: [PATCH 3/6] up --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0036271c39..ead121c762 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ } }, "imports": { - "#build/ui/*": "./.nuxt/ui/*.ts" + "#build/ui/*": "./.nuxt/ui/*.ts", + "#build/ui.css": "./.nuxt/ui.css" }, "bin": { "nuxt-ui": "./cli/index.mjs" From 32ebe92748ba4b33772d9ddeef35e1de7c0e3317 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 6 Feb 2025 15:50:46 +0100 Subject: [PATCH 4/6] up --- src/runtime/plugins/colors.ts | 2 +- src/templates.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/runtime/plugins/colors.ts b/src/runtime/plugins/colors.ts index 0f5485b76f..31bc592651 100644 --- a/src/runtime/plugins/colors.ts +++ b/src/runtime/plugins/colors.ts @@ -10,7 +10,7 @@ export default defineNuxtPlugin(() => { const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] function generateShades(key: string, value: string) { - return `${shades.map(shade => `--ui-color-${key}-${shade}: var(--color-${value}-${shade});`).join('\n ')}` + return `${shades.map(shade => `--ui-color-${key}-${shade}: var(--color-${value === 'neutral' ? 'old-neutral' : value}-${shade});`).join('\n ')}` } function generateColor(key: string, shade: number) { return `--ui-${key}: var(--ui-color-${key}-${shade});` diff --git a/src/templates.ts b/src/templates.ts index a76f59ffe1..341941995b 100644 --- a/src/templates.ts +++ b/src/templates.ts @@ -5,6 +5,7 @@ import type { Nuxt, NuxtTemplate, NuxtTypeTemplate } from '@nuxt/schema' import type { Resolver } from '@nuxt/kit' import type { ModuleOptions } from './module' import * as theme from './theme' +import colors from 'tailwindcss/colors' export function buildTemplates(options: ModuleOptions) { return Object.entries(theme).reduce((acc, [key, component]) => { @@ -73,6 +74,17 @@ export function getTemplates(options: ModuleOptions, uiConfig: Record `@theme default { + --color-old-neutral-50: ${colors.neutral[50]}; + --color-old-neutral-100: ${colors.neutral[100]}; + --color-old-neutral-200: ${colors.neutral[200]}; + --color-old-neutral-300: ${colors.neutral[300]}; + --color-old-neutral-400: ${colors.neutral[400]}; + --color-old-neutral-500: ${colors.neutral[500]}; + --color-old-neutral-600: ${colors.neutral[600]}; + --color-old-neutral-700: ${colors.neutral[700]}; + --color-old-neutral-800: ${colors.neutral[800]}; + --color-old-neutral-900: ${colors.neutral[900]}; + --color-old-neutral-950: ${colors.neutral[950]}; ${[...(options.theme?.colors || []), 'neutral'].map(color => [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950].map(shade => `--color-${color}-${shade}: var(--ui-color-${color}-${shade});`).join('\n\t')).join('\n\t')} } ` From a1d04d07662fa56e2ebe4ac523e7cbf25f50d478 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 6 Feb 2025 17:08:52 +0100 Subject: [PATCH 5/6] up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Sébastien Chopin --- README.md | 2 +- docs/app/app.config.ts | 3 +- docs/app/app.vue | 8 +- .../components/theme-picker/ThemePicker.vue | 132 ++++++++++-------- .../theme-picker/ThemePickerButton.vue | 36 ++--- docs/app/plugins/theme.ts | 26 +++- docs/content/1.getting-started/1.index.md | 6 +- docs/content/1.getting-started/3.theme.md | 102 ++++++++------ 8 files changed, 187 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 7a1a58b3db..d7c0714d55 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![License][license-src]][license-href] [![Nuxt][nuxt-src]][nuxt-href] -We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Tailwind CSS v4](https://tailwindcss.com/docs/v4-beta), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces. +We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Tailwind CSS v4](https://tailwindcss.com/), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces. > [!NOTE] > You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui/tree/dev) for Nuxt UI v2. diff --git a/docs/app/app.config.ts b/docs/app/app.config.ts index 68049a6073..bf927c4904 100644 --- a/docs/app/app.config.ts +++ b/docs/app/app.config.ts @@ -5,7 +5,8 @@ export default defineAppConfig({ duration: 5000 }, theme: { - radius: 0.25 + radius: 0.25, + blackAsPrimary: false }, ui: { colors: { diff --git a/docs/app/app.vue b/docs/app/app.vue index 2fb7b36721..8432169f43 100644 --- a/docs/app/app.vue +++ b/docs/app/app.vue @@ -50,7 +50,10 @@ const links = computed(() => [{ const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white') const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`) - +const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}') +watchEffect(() => { + console.log(appConfig.theme.blackAsPrimary) +}) useHead({ meta: [ { name: 'viewport', content: 'width=device-width, initial-scale=1' }, @@ -61,7 +64,8 @@ useHead({ { rel: 'canonical', href: `https://ui.nuxt.com${withoutTrailingSlash(route.path)}` } ], style: [ - { innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 } + { innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 }, + { innerHTML: blackAsPrimary, id: 'nuxt-ui-black-as-primary', tagPriority: -2 } ], htmlAttrs: { lang: 'en' diff --git a/docs/app/components/theme-picker/ThemePicker.vue b/docs/app/components/theme-picker/ThemePicker.vue index 9c49c2761b..25ef96868a 100644 --- a/docs/app/components/theme-picker/ThemePicker.vue +++ b/docs/app/components/theme-picker/ThemePicker.vue @@ -1,3 +1,66 @@ + + - - diff --git a/docs/app/components/theme-picker/ThemePickerButton.vue b/docs/app/components/theme-picker/ThemePickerButton.vue index bc0104316b..27ec5c373a 100644 --- a/docs/app/components/theme-picker/ThemePickerButton.vue +++ b/docs/app/components/theme-picker/ThemePickerButton.vue @@ -1,3 +1,12 @@ + + - - diff --git a/docs/app/plugins/theme.ts b/docs/app/plugins/theme.ts index 59ee50f47d..666f5341d6 100644 --- a/docs/app/plugins/theme.ts +++ b/docs/app/plugins/theme.ts @@ -18,9 +18,17 @@ export default defineNuxtPlugin({ } } + function updateBlackAsPrimary() { + const blackAsPrimary = localStorage.getItem('nuxt-ui-black-as-primary') + if (blackAsPrimary) { + appConfig.theme.blackAsPrimary = blackAsPrimary === 'true' + } + } + updateColor('primary') updateColor('neutral') updateRadius() + updateBlackAsPrimary() } if (import.meta.server) { @@ -31,10 +39,12 @@ export default defineNuxtPlugin({ if (localStorage.getItem('nuxt-ui-primary')) { const primaryColor = localStorage.getItem('nuxt-ui-primary'); - html = html.replace( - /(--ui-color-primary-\\d{2,3}:\\s*var\\()--color-${appConfig.ui.colors.primary}-(\\d{2,3}\\))/g, - \`$1--color-\${primaryColor}-$2\` - ); + if (primaryColor !== 'black') { + html = html.replace( + /(--ui-color-primary-\\d{2,3}:\\s*var\\()--color-${appConfig.ui.colors.primary}-(\\d{2,3}\\))/g, + \`$1--color-\${primaryColor}-$2\` + ); + } } if (localStorage.getItem('nuxt-ui-neutral')) { const neutralColor = localStorage.getItem('nuxt-ui-neutral'); @@ -56,6 +66,14 @@ export default defineNuxtPlugin({ `.replace(/\s+/g, ' '), type: 'text/javascript', tagPriority: -1 + }, { + innerHTML: ` + if (localStorage.getItem('nuxt-ui-black-as-primary') === 'true') { + document.querySelector('style#nuxt-ui-black-as-primary').innerHTML = ':root { --ui-primary: black; } .dark { --ui-primary: white; }'; + } else { + document.querySelector('style#nuxt-ui-black-as-primary').innerHTML = ''; + } + `.replace(/\s+/g, ' ') }] }) } diff --git a/docs/content/1.getting-started/1.index.md b/docs/content/1.getting-started/1.index.md index ec1437bad9..649a7210ab 100644 --- a/docs/content/1.getting-started/1.index.md +++ b/docs/content/1.getting-started/1.index.md @@ -23,15 +23,15 @@ This transition empowers Nuxt UI to become a more comprehensive and flexible UI ### Tailwind CSS v4 -Nuxt UI v3 integrates the latest Tailwind CSS v4 beta (released Nov 21, 2024), bringing significant improvements: +Nuxt UI v3 integrates the latest Tailwind CSS v4, bringing significant improvements: - **Built for performance**: Full builds in the new engine are up to 5x faster, and incremental builds are over 100x faster — and measured in microseconds. - **Unified toolchain**: Built-in import handling, vendor prefixing, and syntax transforms, with no additional tooling required. - **CSS-first configuration**: A reimagined developer experience where you customize and extend the framework directly in CSS instead of a JavaScript configuration file. - **Designed for the modern web**: Built on native cascade layers, wide-gamut colors, and including first-class support for modern CSS features like container queries, @starting-style, popovers, and more. -::note{to="https://tailwindcss.com/docs/v4-beta" target="_blank" aria-label="Tailwind CSS v4 beta documentation"} -For a comprehensive overview of Tailwind CSS v4 beta features, read the **prerelease documentation**. +::note{to="https://tailwindcss.com/docs/upgrade-guide" target="_blank" aria-label="Tailwind CSS v4 upgrade guide"} +Learn how to upgrade your project from Tailwind CSS v3 to v4. :: ### Tailwind Variants diff --git a/docs/content/1.getting-started/3.theme.md b/docs/content/1.getting-started/3.theme.md index f1c1afaa8c..2e4d5374d5 100644 --- a/docs/content/1.getting-started/3.theme.md +++ b/docs/content/1.getting-started/3.theme.md @@ -5,11 +5,11 @@ description: 'Learn how to customize Nuxt UI components using Tailwind CSS v4, C ## Tailwind CSS -Nuxt UI v3 uses Tailwind CSS v4 beta, you can read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta) for more information. +Nuxt UI v3 uses Tailwind CSS v4, you can read the [upgrade guide](https://tailwindcss.com/docs/upgrade-guide) to learn how to upgrade your project from v3 to v4. ### `@theme` -Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your theme with CSS variables inside a `@theme` directive: +Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your theme with CSS variables inside a [`@theme`](https://tailwindcss.com/docs/functions-and-directives#theme-directive) directive to define your project's custom design tokens, like fonts, colors, and breakpoints: ::module-only #ui @@ -71,15 +71,15 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file. -::note{to="https://tailwindcss.com/docs/v4-beta#css-first-configuration" target="_blank"} -Learn more about Tailwind CSS v4 CSS-first configuration approach. +::note{to="https://tailwindcss.com/docs/theme" target="_blank"} +Learn more about customizing your theme in the theme variables documentation. :: ### `@source` -You can use the `@source` directive to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected. +You can use the [`@source` directive](https://tailwindcss.com/docs/functions-and-directives#source-directive) to explicitly specify source files that aren't picked up by Tailwind's automatic content detection: -This can be useful when writing Tailwind classes in markdown files with [`@nuxt/content`](https://github.com/nuxt/content): +This can be useful when writing Tailwind classes in markdown files with [`@nuxt/content`](https://github.com/nuxt/content) for example: ::module-only #ui @@ -107,42 +107,8 @@ This can be useful when writing Tailwind classes in markdown files with [`@nuxt/ ::: :: -::note{to="https://tailwindcss.com/docs/v4-beta#adding-content-sources"} -Learn how to add content sources in Tailwind CSS v4. -:: - -### `@plugin` - -You can use the `@plugin` directive to import Tailwind CSS plugins. - -::module-only -#ui -:::div - -```css [main.css] -@import "tailwindcss"; -@import "@nuxt/ui"; - -@plugin "@tailwindcss/typography"; -``` - -::: - -#ui-pro -:::div - -```css [main.css] -@import "tailwindcss"; -@import "@nuxt/ui-pro"; - -@plugin "@tailwindcss/typography"; -``` - -::: -:: - -::note{to="https://tailwindcss.com/docs/v4-beta#using-plugins"} -Learn more about using plugins in Tailwind CSS v4. +::note{to="https://tailwindcss.com/docs/detecting-classes-in-source-files"} +Learn more about automatic content detection in the detecting classes in source files documentation. :: ## Design system @@ -367,6 +333,58 @@ You can change which shade is used for each color on light and dark mode: :: +#### Black as Primary Color + +::framework-only +#nuxt +:::p +You cannot set `primary: 'black'`{lang="ts-type"} in your [`app.config.ts`](#config) because this color has no shade, instead you can override the primary color in your `main.css` file to create a black & white theme: +::: + +#vue +:::p +You cannot set `primary: 'black'`{lang="ts-type"} in your [`vite.config.ts`](#config) because this color has no shade, instead you can override the primary color in your `main.css` file to create a black & white theme: +::: +:: + +::module-only +#ui +:::div{class="*:!mb-0 *:!mt-2.5"} + +```css [main.css] +@import "tailwindcss"; +@import "@nuxt/ui"; + +:root { + --ui-primary: black; +} + +.dark { + --ui-primary: white; +} +``` + +::: + +#ui-pro +:::div{class="*:!mb-0 *:!mt-2.5"} + +```css [main.css] +@import "tailwindcss"; +@import "@nuxt/ui-pro"; + +:root { + --ui-primary: black; +} + +.dark { + --ui-primary: white; +} +``` + +::: +:: + #### Neutral Palette Nuxt UI provides a comprehensive set of design tokens for the `neutral` color palette, ensuring consistent and accessible UI styling across both light and dark modes. These tokens offer fine-grained control over text, background, and border colors: From a20d8ec8c83e17aedf55ea1f171c36b1c32ef722 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 6 Feb 2025 17:16:29 +0100 Subject: [PATCH 6/6] up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Sébastien Chopin --- docs/app/app.vue | 4 +--- docs/app/components/theme-picker/ThemePicker.vue | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/app/app.vue b/docs/app/app.vue index 8432169f43..e02325e974 100644 --- a/docs/app/app.vue +++ b/docs/app/app.vue @@ -51,9 +51,7 @@ const links = computed(() => [{ const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white') const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`) const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}') -watchEffect(() => { - console.log(appConfig.theme.blackAsPrimary) -}) + useHead({ meta: [ { name: 'viewport', content: 'width=device-width, initial-scale=1' }, diff --git a/docs/app/components/theme-picker/ThemePicker.vue b/docs/app/components/theme-picker/ThemePicker.vue index 25ef96868a..9170190f06 100644 --- a/docs/app/components/theme-picker/ThemePicker.vue +++ b/docs/app/components/theme-picker/ThemePicker.vue @@ -5,8 +5,6 @@ import { omit } from '#ui/utils' const appConfig = useAppConfig() const colorMode = useColorMode() -// Computed - const neutralColors = ['slate', 'gray', 'zinc', 'neutral', 'stone'] const neutral = computed({ get() {