diff --git a/docs/app/components/AdsCarbon.vue b/docs/app/components/AdsCarbon.vue index 3c21b8cf..a5715f83 100644 --- a/docs/app/components/AdsCarbon.vue +++ b/docs/app/components/AdsCarbon.vue @@ -38,7 +38,7 @@ onMounted(() => { } .carbon-poweredby { - @apply block text-[10px] text-center text-(--ui-text-dimmed) pt-2; + @apply block text-xs text-center text-(--ui-text-muted) pt-2; } &:hover { diff --git a/docs/app/components/content/examples/table/TableColumnSortingReusableExample.vue b/docs/app/components/content/examples/table/TableColumnSortingReusableExample.vue index 0c13b248..c6825032 100644 --- a/docs/app/components/content/examples/table/TableColumnSortingReusableExample.vue +++ b/docs/app/components/content/examples/table/TableColumnSortingReusableExample.vue @@ -97,10 +97,11 @@ function getHeader(column: Column, label: string) { const isSorted = column.getIsSorted() return h(UDropdownMenu, { - content: { + 'content': { align: 'start' }, - items: [{ + 'aria-label': 'Actions dropdown', + 'items': [{ label: 'Asc', type: 'checkbox', icon: 'i-lucide-arrow-up-narrow-wide', @@ -126,11 +127,12 @@ function getHeader(column: Column, label: string) { } }] }, () => h(UButton, { - color: 'neutral', - variant: 'ghost', + 'color': 'neutral', + 'variant': 'ghost', label, - icon: isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down', - class: '-mx-2.5 data-[state=open]:bg-(--ui-bg-elevated)' + 'icon': isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down', + 'class': '-mx-2.5 data-[state=open]:bg-(--ui-bg-elevated)', + 'aria-label': `Sort by ${isSorted === 'asc' ? 'descending' : 'ascending'}` })) } diff --git a/docs/app/components/content/examples/table/TableExample.vue b/docs/app/components/content/examples/table/TableExample.vue index 366dca7b..375a45d7 100644 --- a/docs/app/components/content/examples/table/TableExample.vue +++ b/docs/app/components/content/examples/table/TableExample.vue @@ -145,12 +145,12 @@ const columns: TableColumn[] = [{ header: ({ table }) => h(UCheckbox, { 'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value), - 'ariaLabel': 'Select all' + 'aria-label': 'Select all' }), cell: ({ row }) => h(UCheckbox, { 'modelValue': row.getIsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), - 'ariaLabel': 'Select row' + 'aria-label': 'Select row' }), enableSorting: false, enableHiding: false @@ -242,15 +242,17 @@ const columns: TableColumn[] = [{ }] return h('div', { class: 'text-right' }, h(UDropdownMenu, { - content: { + 'content': { align: 'end' }, - items + items, + 'aria-label': 'Actions dropdown' }, () => h(UButton, { - icon: 'i-lucide-ellipsis-vertical', - color: 'neutral', - variant: 'ghost', - class: 'ml-auto' + 'icon': 'i-lucide-ellipsis-vertical', + 'color': 'neutral', + 'variant': 'ghost', + 'class': 'ml-auto', + 'aria-label': 'Actions dropdown' }))) } }] @@ -294,6 +296,7 @@ function randomize() { variant="outline" trailing-icon="i-lucide-chevron-down" class="ml-auto" + aria-label="Columns select dropdown" /> diff --git a/docs/app/components/content/examples/table/TableFetchExample.vue b/docs/app/components/content/examples/table/TableFetchExample.vue index b1154a31..3007ef34 100644 --- a/docs/app/components/content/examples/table/TableFetchExample.vue +++ b/docs/app/components/content/examples/table/TableFetchExample.vue @@ -17,7 +17,7 @@ const { data, status } = await useFetch('https://jsonplaceholder.typicod transform: (data) => { return data?.map(user => ({ ...user, - avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } + avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, alt: `${user.name} avatar` } })) || [] }, lazy: true diff --git a/docs/app/components/content/examples/table/TableRowActionsExample.vue b/docs/app/components/content/examples/table/TableRowActionsExample.vue index 76e86ef2..7d15b200 100644 --- a/docs/app/components/content/examples/table/TableRowActionsExample.vue +++ b/docs/app/components/content/examples/table/TableRowActionsExample.vue @@ -97,15 +97,17 @@ const columns: TableColumn[] = [{ id: 'actions', cell: ({ row }) => { return h('div', { class: 'text-right' }, h(UDropdownMenu, { - content: { + 'content': { align: 'end' }, - items: getRowItems(row) + 'items': getRowItems(row), + 'aria-label': 'Actions dropdown' }, () => h(UButton, { - icon: 'i-lucide-ellipsis-vertical', - color: 'neutral', - variant: 'ghost', - class: 'ml-auto' + 'icon': 'i-lucide-ellipsis-vertical', + 'color': 'neutral', + 'variant': 'ghost', + 'class': 'ml-auto', + 'aria-label': 'Actions dropdown' }))) } }] diff --git a/docs/app/components/content/examples/table/TableRowExpandableExample.vue b/docs/app/components/content/examples/table/TableRowExpandableExample.vue index cbaf7cc4..05e9300e 100644 --- a/docs/app/components/content/examples/table/TableRowExpandableExample.vue +++ b/docs/app/components/content/examples/table/TableRowExpandableExample.vue @@ -48,14 +48,15 @@ const data = ref([{ const columns: TableColumn[] = [{ id: 'expand', cell: ({ row }) => h(UButton, { - color: 'neutral', - variant: 'ghost', - icon: 'i-lucide-chevron-down', - square: true, - ui: { + 'color': 'neutral', + 'variant': 'ghost', + 'icon': 'i-lucide-chevron-down', + 'square': true, + 'aria-label': 'Expand', + 'ui': { leadingIcon: ['transition-transform', row.getIsExpanded() ? 'duration-200 rotate-180' : ''] }, - onClick: () => row.toggleExpanded() + 'onClick': () => row.toggleExpanded() }) }, { accessorKey: 'id', diff --git a/docs/app/components/content/examples/table/TableRowSelectionEventExample.vue b/docs/app/components/content/examples/table/TableRowSelectionEventExample.vue index b7238fdc..1ea9b5c9 100644 --- a/docs/app/components/content/examples/table/TableRowSelectionEventExample.vue +++ b/docs/app/components/content/examples/table/TableRowSelectionEventExample.vue @@ -50,12 +50,12 @@ const columns: TableColumn[] = [{ header: ({ table }) => h(UCheckbox, { 'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value), - 'ariaLabel': 'Select all' + 'aria-label': 'Select all' }), cell: ({ row }) => h(UCheckbox, { 'modelValue': row.getIsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), - 'ariaLabel': 'Select row' + 'aria-label': 'Select row' }) }, { accessorKey: 'date', diff --git a/docs/app/components/content/examples/table/TableRowSelectionExample.vue b/docs/app/components/content/examples/table/TableRowSelectionExample.vue index a29a459e..c6e481f3 100644 --- a/docs/app/components/content/examples/table/TableRowSelectionExample.vue +++ b/docs/app/components/content/examples/table/TableRowSelectionExample.vue @@ -50,12 +50,12 @@ const columns: TableColumn[] = [{ header: ({ table }) => h(UCheckbox, { 'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value), - 'ariaLabel': 'Select all' + 'aria-label': 'Select all' }), cell: ({ row }) => h(UCheckbox, { 'modelValue': row.getIsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), - 'ariaLabel': 'Select row' + 'aria-label': 'Select row' }) }, { accessorKey: 'date', diff --git a/docs/app/components/content/examples/table/TableSlotsExample.vue b/docs/app/components/content/examples/table/TableSlotsExample.vue index 3a4b5a97..fd187d18 100644 --- a/docs/app/components/content/examples/table/TableSlotsExample.vue +++ b/docs/app/components/content/examples/table/TableSlotsExample.vue @@ -95,7 +95,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] { diff --git a/docs/app/pages/[...slug].vue b/docs/app/pages/[...slug].vue index 72e09f8a..47944930 100644 --- a/docs/app/pages/[...slug].vue +++ b/docs/app/pages/[...slug].vue @@ -136,7 +136,7 @@ const communityLinks = computed(() => [{ v-bind="link" > diff --git a/docs/app/pages/components.vue b/docs/app/pages/components.vue index 1d3f2d69..9ca5a4be 100644 --- a/docs/app/pages/components.vue +++ b/docs/app/pages/components.vue @@ -169,6 +169,7 @@ onMounted(() => { :loading="index >= 4 ? 'lazy' : 'eager'" width="640" height="360" + :alt="`${component.name} preview`" /> diff --git a/docs/app/pages/index.vue b/docs/app/pages/index.vue index fb16a310..60738b00 100644 --- a/docs/app/pages/index.vue +++ b/docs/app/pages/index.vue @@ -106,6 +106,9 @@ useIntersectionObserver(contributorsRef, ([entry]) => { :light="`${component.path.replace('/components/', '/components/light/')}.png`" :dark="`${component.path.replace('/components/', '/components/dark/')}.png`" :alt="`${component.title} preview`" + width="290" + height="163" + format="webp" class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0" loading="lazy" /> @@ -132,6 +135,9 @@ useIntersectionObserver(contributorsRef, ([entry]) => { :light="`${component.path.replace('/components/', '/components/light/')}.png`" :dark="`${component.path.replace('/components/', '/components/dark/')}.png`" :alt="`${component.title} preview`" + width="290" + height="163" + format="webp" class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0" loading="lazy" /> @@ -155,7 +161,9 @@ useIntersectionObserver(contributorsRef, ([entry]) => { :in-view-options="{ once: true }" class="flex items-start gap-x-3 relative group" > - + + Go to {{ feature.title }} +
@@ -221,26 +229,32 @@ useIntersectionObserver(contributorsRef, ([entry]) => { class="border-b border-(--ui-border)" >

- {{ format(module?.stats?.downloads ?? 0) }}+ -

-

monthly downloads

- +
  • + +

    + {{ format(module?.stats?.downloads ?? 0) }}+ +

    +

    monthly downloads

    +
    +
  • - -

    - {{ format(module?.stats?.stars ?? 0) }}+ -

    -

    GitHub stars

    -
    +
  • + +

    + {{ format(module?.stats?.stars ?? 0) }}+ +

    +

    GitHub stars

    +
    +
  • - -

    - 175+ -

    -

    Contributors

    -
    +
  • + +

    + 175+ +

    +

    Contributors

    +
    +
  • diff --git a/docs/app/pages/pro/templates.vue b/docs/app/pages/pro/templates.vue index 6da93dab..11cdb3b9 100644 --- a/docs/app/pages/pro/templates.vue +++ b/docs/app/pages/pro/templates.vue @@ -56,6 +56,7 @@ useSeoMeta({ v-if="template.thumbnail" v-bind="template.thumbnail" class="w-full h-auto border lg:border-y lg:border-x-0 border-(--ui-border) rounded-(--ui-radius) lg:rounded-none" + :alt="`Template ${index} thumbnail`" width="656" height="369" loading="lazy" diff --git a/docs/content/1.getting-started/1.index.md b/docs/content/1.getting-started/1.index.md index 169762cb..caf04ac4 100644 --- a/docs/content/1.getting-started/1.index.md +++ b/docs/content/1.getting-started/1.index.md @@ -6,7 +6,7 @@ navigation.icon: i-lucide-house -### Reka UI +## Reka UI We've transitioned from [Headless UI](https://headlessui.com/) to [Reka UI](https://reka-ui.com/) as our core component foundation. This shift brings several key advantages: @@ -17,7 +17,7 @@ We've transitioned from [Headless UI](https://headlessui.com/) to [Reka UI](http This transition empowers Nuxt UI to become a more comprehensive and flexible UI library, offering developers greater power and customization options. -### Tailwind CSS v4 +## Tailwind CSS v4 Nuxt UI integrates the latest Tailwind CSS v4, bringing significant improvements: @@ -30,7 +30,7 @@ Nuxt UI integrates the latest Tailwind CSS v4, bringing significant improvements Learn about all the breaking changes in Tailwind CSS v4. :: -### Tailwind Variants +## Tailwind Variants We've adopted [Tailwind Variants](https://www.tailwind-variants.org/) to manage our design system, offering: @@ -40,7 +40,7 @@ We've adopted [Tailwind Variants](https://www.tailwind-variants.org/) to manage This integration unifies the styling of components, ensuring consistency and code maintainability. -### TypeScript Integration +## TypeScript Integration Nuxt UI offers significantly improved TypeScript integration, providing a superior developer experience: @@ -60,7 +60,7 @@ Nuxt UI offers significantly improved TypeScript integration, providing a superi Check out an example of the Accordion component with auto-completion for props and slots. :: -### Vue compatibility +## Vue compatibility You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and Vue plugins to your configuration. This provides: @@ -72,7 +72,7 @@ You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and V Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**. :: -### Nuxt DevTools Integration +## Nuxt DevTools Integration You can play with Nuxt UI components as well as your app components directly from Nuxt Devtools with the [compodium](https://github.com/romhml/compodium) module, providing a powerful development experience: diff --git a/docs/content/3.components/table.md b/docs/content/3.components/table.md index 4492a92d..5c4bcaba 100644 --- a/docs/content/3.components/table.md +++ b/docs/content/3.components/table.md @@ -23,7 +23,7 @@ class: '!p-0' --- :: -::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/tree/v3/docs/app/components/content/examples/table/TableExample.vue"} +::callout{icon="i-simple-icons-github" to="https://github.com/nuxt/ui/tree/v3/docs/app/components/content/examples/table/TableExample.vue" aria-label="View source code"} This example demonstrates the most common use case of the `Table` component. Check out the source code on GitHub. :: @@ -85,7 +85,7 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility. -::tip{to="#with-slots"} +::tip{to="#with-slots" aria-label="Table columns with slots"} You can also use slots to customize the header and data cells of the table. :: diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts index 1ba4c38b..27b5f578 100644 --- a/docs/nuxt.config.ts +++ b/docs/nuxt.config.ts @@ -225,6 +225,7 @@ export default defineNuxtConfig({ }, image: { + format: ['webp', 'jpeg', 'jpg', 'png', 'svg'], provider: 'ipx' }, diff --git a/playground/app/pages/components/table.vue b/playground/app/pages/components/table.vue index 8ecaccea..f57808b0 100644 --- a/playground/app/pages/components/table.vue +++ b/playground/app/pages/components/table.vue @@ -148,12 +148,12 @@ const columns: TableColumn[] = [{ header: ({ table }) => h(UCheckbox, { 'modelValue': table.getIsSomePageRowsSelected() ? 'indeterminate' : table.getIsAllPageRowsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value), - 'ariaLabel': 'Select all' + 'aria-label': 'Select all' }), cell: ({ row }) => h(UCheckbox, { 'modelValue': row.getIsSelected(), 'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), - 'ariaLabel': 'Select row' + 'aria-label': 'Select row' }), enableSorting: false, enableHiding: false @@ -251,15 +251,17 @@ const columns: TableColumn[] = [{ }] return h('div', { class: 'text-right' }, h(UDropdownMenu, { - content: { + 'content': { align: 'end' }, - items + items, + 'aria-label': 'Actions dropdown' }, () => h(UButton, { - icon: 'i-lucide-ellipsis-vertical', - color: 'neutral', - variant: 'ghost', - class: 'ms-auto' + 'icon': 'i-lucide-ellipsis-vertical', + 'color': 'neutral', + 'variant': 'ghost', + 'class': 'ms-auto', + 'aria-label': 'Actions dropdown' }))) } }]