Theming
Every Nuxt Motion Block component is built on a tailwind-variants theme, the same technique Nuxt UI uses for its own components. Each theme declares a set of named slots — root, header, content, and so on — and every slot holds the default utility classes for that part of the markup.
You never have to touch the theme file to restyle a component. There are three override layers, and they all target the same slot names.
The ui prop
The ui prop takes an object keyed by slot name. Only the slots you name are changed; the rest keep their defaults.
<template>
<MSpotlight
:ui="{
root: 'size-80 bg-primary-500/30'
}"
/>
</template>
Slot classes are merged with conflict resolution, so a utility you pass replaces the matching default instead of both being emitted. The same merge applies to the class prop, which targets the component's primary slot. Use class for quick tweaks and ui when you need to reach a specific inner element.
Global configuration with app.config
To restyle a component everywhere in your app, add it to app.config.ts under ui.<componentName>. The overrides live under a slots key, mirroring the theme's own shape:
export default defineAppConfig({
ui: {
spotlight: {
slots: {
root: 'bg-primary-500/30'
}
}
}
})
The <componentName> is the component's camelCase name — spotlight, magneticButton, dynamicIsland, codeBlock, and so on. Each component page lists its slots and default classes at the bottom, so you can see exactly which keys are available.
Merge priority
Overrides are applied in this order, from lowest to highest:
- The component's theme defaults
app.config.ts(ui.<componentName>.slots)- The
uiprop - The
classprop
Because class sits on top, it is the right tool for one-off instances, while app.config is the place for app-wide brand decisions.
Authoring your own components
If you build a component that should follow the same contract, use the useMotionBlockUI composable. It merges a tailwind-variants theme with the user's app.config overrides and returns a tv instance you can call per slot.
<script setup lang="ts">
import { useMotionBlockUI } from 'nuxt-motion-block/runtime/composables/useMotionBlockUI'
import spotlight from 'nuxt-motion-block/runtime/theme/spotlight'
const props = withDefaults(defineProps<{
class?: string
ui?: Record<string, any>
}>(), {
class: undefined,
ui: undefined,
})
const { props: uiProps, ui } = useMotionBlockUI({
name: 'spotlight',
props,
theme: spotlight,
})
</script>
<template>
<div :class="ui.root({ class: [uiProps.ui?.root, uiProps.class] })" />
</template>
Components registered through Nuxt Motion Block expose their themes from nuxt-motion-block/runtime/theme, so you can also import a theme and extend it directly.
Theme colours in components and canvas
Nuxt UI exposes its semantic colours as CSS variables — --ui-primary, --ui-text-muted, --ui-border, and so on — which is exactly what you want in a stylesheet. A <canvas> fill, a 2D stroke or a WebGL uniform cannot take a var() reference, though; it needs a concrete colour.
Nuxt Motion Block closes that gap for you. Every component that paints to canvas or WebGL accepts a Nuxt UI token (or any CSS colour) and converts it to the right format on its own:
<template>
<!-- DottedGlowBackground: pass any --ui-* or --color-* variable -->
<MDottedGlowBackground
color-light-var="--ui-text-dimmed"
glow-color-light-var="--ui-primary"
glow-color-dark-var="--ui-primary"
/>
<!-- AiOrb: colours accept any CSS colour, including var(--ui-*) -->
<MAiOrb :colors="['var(--ui-primary)', 'var(--ui-secondary)']" />
<!-- DitherChart: omit `palette` and it follows --ui-primary -->
<MDitherChart :data="data" />
<!-- Cursors: options.color is a theme colour name -->
<canvas ref="canvasRef" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const canvasRef = ref()
useCanvasCursor(canvasRef, { color: 'primary' })
</script>
A token always resolves to the colour your theme is actually rendering — light or dark — and re-resolves when the colour mode changes. You can equally set a colour directly: every one of these props also accepts a plain CSS colour string, so a token is a convenience, not a requirement.
When you need the same behaviour in your own code, the useThemeColor composable hands you the resolved colour reactively:
<script setup lang="ts">
const primary = useThemeColor('primary') // Ref<'rgb(0, 220, 130)' | null>
</script>