Create custom themes to match your application's design.
A theme is an object defining CSS classes for each grid element:
// themes/my-theme.ts
import type { NuGridTheme } from '@nu-grid/nuxt/types'
export const myCustomTheme: NuGridTheme = {
root: 'overflow-auto rounded-lg shadow',
base: 'w-full border-separate border-spacing-0',
thead: '[&>tr]:bg-slate-100 dark:[&>tr]:bg-slate-800',
tbody: '',
tfoot: 'bg-slate-50 dark:bg-slate-900',
tr: '',
th: 'px-4 py-3 text-left font-semibold text-slate-900 dark:text-white',
td: 'px-4 py-2 border-b border-slate-200 dark:border-slate-700',
loading: 'opacity-50',
empty: 'text-center py-8 text-slate-500',
separator: 'h-4',
}
| Property | Description |
|---|---|
root | Container element |
base | Table element |
thead | Table header section |
tbody | Table body section |
tfoot | Table footer section |
tr | All table rows |
th | Header cells |
td | Data cells |
loading | Applied when loading |
empty | Empty state container |
separator | Separator between sections |
<script setup lang="ts">
import { myCustomTheme } from '~/themes/my-theme'
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:theme="myCustomTheme"
/>
</template>
Extend the default theme:
import { nuGridTheme } from '@nu-grid/nuxt/themes'
export const myTheme = {
...nuGridTheme,
th: `${nuGridTheme.th} bg-blue-50 dark:bg-blue-900/20`,
td: `${nuGridTheme.td} hover:bg-blue-50/50`,
}
Or extend the compact theme:
import { nuGridThemeCompact } from '@nu-grid/nuxt/themes'
export const myCompactTheme = {
...nuGridThemeCompact,
thead: '[&>tr]:bg-primary/10',
}
Create themes dynamically:
<script setup lang="ts">
const primaryColor = ref('blue')
const dynamicTheme = computed(() => ({
root: 'rounded-lg overflow-hidden',
thead: `[&>tr]:bg-${primaryColor.value}-100 dark:[&>tr]:bg-${primaryColor.value}-900/30`,
th: `text-${primaryColor.value}-900 dark:text-${primaryColor.value}-100`,
td: 'border-b border-gray-200 dark:border-gray-700',
}))
</script>
<template>
<div>
<USelect
v-model="primaryColor"
:items="['blue', 'green', 'purple', 'orange']"
class="mb-4"
/>
<NuGrid :data="data" :columns="columns" :theme="dynamicTheme" />
</div>
</template>
Create theme variants:
// themes/variants.ts
const baseTheme = {
root: 'overflow-auto',
base: 'w-full border-separate border-spacing-0',
td: 'px-4 py-2',
}
export const lightTheme = {
...baseTheme,
thead: '[&>tr]:bg-gray-100',
th: 'text-gray-900 font-semibold',
td: `${baseTheme.td} border-b border-gray-200`,
}
export const darkTheme = {
...baseTheme,
thead: '[&>tr]:bg-gray-800',
th: 'text-white font-semibold',
td: `${baseTheme.td} border-b border-gray-700`,
}
export const primaryTheme = {
...baseTheme,
thead: '[&>tr]:bg-primary/20',
th: 'text-primary font-semibold',
td: `${baseTheme.td} border-b border-primary/20`,
}
Apply styles based on cell/row state:
export const stateAwareTheme = {
tbody: `
[&>tr[data-selected=true]]:bg-primary/10
[&>tr[data-focused=true]]:ring-2
[&>tr[data-focused=true]]:ring-primary
[&>tr:hover]:bg-gray-50
dark:[&>tr:hover]:bg-gray-800/50
`,
td: `
[&[data-editing=true]]:bg-primary/5
[&[data-invalid=true]]:bg-error/10
`,
}
Use CSS custom properties:
export const cssVarTheme = {
root: 'grid-theme',
thead: '[&>tr]:bg-[var(--grid-header-bg)]',
th: 'text-[var(--grid-header-text)]',
td: 'border-b border-[var(--grid-border)]',
}
/* app.css */
:root {
--grid-header-bg: theme('colors.gray.100');
--grid-header-text: theme('colors.gray.900');
--grid-border: theme('colors.gray.200');
}
.dark {
--grid-header-bg: theme('colors.gray.800');
--grid-header-text: theme('colors.white');
--grid-border: theme('colors.gray.700');
}
Set a default theme for all grids:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nu-grid/nuxt'],
nuGrid: {
theme: 'custom', // Use your custom theme as default
},
})
// plugins/nu-grid.ts
import { myCustomTheme } from '~/themes/my-theme'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.provide('nuGridDefaultTheme', myCustomTheme)
})
// themes/corporate.ts
export const corporateTheme = {
root: 'rounded-xl overflow-hidden shadow-lg border border-gray-200 dark:border-gray-700',
base: 'w-full border-separate border-spacing-0',
thead: `
[&>tr]:bg-gradient-to-r
[&>tr]:from-gray-100
[&>tr]:to-gray-50
dark:[&>tr]:from-gray-800
dark:[&>tr]:to-gray-900
`,
th: `
px-4 py-3
text-left text-sm font-semibold
text-gray-700 dark:text-gray-200
first:rounded-tl-xl last:rounded-tr-xl
border-b-2 border-primary/20
`,
tbody: `
[&>tr]:transition-colors
[&>tr:hover]:bg-gray-50
dark:[&>tr:hover]:bg-gray-800/50
[&>tr:last-child>td]:border-b-0
`,
td: `
px-4 py-3
text-sm text-gray-600 dark:text-gray-300
border-b border-gray-100 dark:border-gray-800
[&[data-focused=true]]:bg-primary/5
[&[data-focused=true]]:ring-2
[&[data-focused=true]]:ring-inset
[&[data-focused=true]]:ring-primary/50
`,
loading: 'opacity-60 pointer-events-none',
empty: `
text-center py-12
text-gray-400 dark:text-gray-500
`,
}