Skip to content

Comments

feat(Theme): add variants prop for component theming#6031

Draft
Bobakanoosh wants to merge 1 commit intonuxt:v4from
Bobakanoosh:feat/theme-variants
Draft

feat(Theme): add variants prop for component theming#6031
Bobakanoosh wants to merge 1 commit intonuxt:v4from
Bobakanoosh:feat/theme-variants

Conversation

@Bobakanoosh
Copy link
Contributor

@Bobakanoosh Bobakanoosh commented Feb 13, 2026

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Follow-up to #4387, adding a variants prop to UTheme as discussed.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@github-actions github-actions bot added the v4 #4488 label Feb 13, 2026
@Bobakanoosh
Copy link
Contributor Author

@benjamincanac Here is a POC of the variants prop on UTheme, I pushed an example in the button playground.

Seeking guidance on the following, the types are a bit tricky:

  1. Passing this variant configuration to useFieldGroup
  2. Passing this variant configuration to useComponentIcons

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

This change introduces a new variant theming system for components by adding a useComponentVariant composable that enables components to derive variant overrides from theme context. The Theme component is extended to provide variant context alongside UI context, with the ui property made optional. The Button component is updated to use the new variant composable to source variant values from the theme context instead of directly from props. A playground example is updated to demonstrate the new UTheme wrapper with variant and UI configuration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into v4
Title check ✅ Passed The title accurately describes the main change: adding a variants prop to the Theme component for component theming.
Description check ✅ Passed The description relates to the changeset by referencing a follow-up to a previous PR and mentioning the addition of a variants prop to UTheme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/runtime/composables/useComponentVariant.ts`:
- Around line 32-39: The computed in useComponentVariant currently merges theme
overrides with props.variants which doesn't exist, so component props (e.g.,
color, variant) are ignored and not tracked; update the merge to use the actual
props object (e.g., defu(props, themeOverrides)) so explicit props take
precedence and Vue reactivity tracks prop changes inside the computed returned
by useComponentVariant; optionally, to reduce over-tracking, pick only the
variant-related keys from props before calling defu.
🧹 Nitpick comments (1)
src/runtime/components/Theme.vue (1)

20-26: Consider whether nested <UTheme> should merge contexts.

Currently, each <UTheme> fully replaces both the UI and variant contexts for its descendants. If a user nests <UTheme :variants="{ button: { color: 'red' } }"> inside <UTheme :variants="{ button: { variant: 'soft' } }">, the inner theme will lose the outer's variant: 'soft'.

This may be intentional for a POC, but worth considering whether defu-merging with the parent context (similar to how useComponentVariant merges props with theme overrides) would provide a more intuitive cascading behavior.

Comment on lines +32 to +39
export function useComponentVariant<C extends keyof UIConfig>(name: C, props: ComponentVariantProps<C>): ComputedRef<ThemeVariantOverrides<ComponentVariants<C>>> {
const { variant } = injectVariantContext({ variant: computed(() => ({})) })

return computed(() => {
const themeOverrides = (get(variant.value, name as string) || {})

return defu(props.variants ?? {}, themeOverrides)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: props.variants is always undefined — explicit component props are ignored.

ComponentVariantProps<C> maps variant keys (color, variant, block, square, …) as top-level properties. There is no .variants key on this type or on the actual ButtonProps object passed by the caller. As a result, props.variants ?? {} always evaluates to {}, and defu({}, themeOverrides) returns only the theme overrides — making it impossible for explicit component props (e.g., <UButton color="red">) to override theme-provided values.

Additionally, since no variant-related prop is actually read inside this computed, Vue won't track changes to props like color or variant, so the returned value won't reactively update when those props change.

Proposed fix — merge `props` directly with theme overrides
 export function useComponentVariant<C extends keyof UIConfig>(name: C, props: ComponentVariantProps<C>): ComputedRef<ThemeVariantOverrides<ComponentVariants<C>>> {
   const { variant } = injectVariantContext({ variant: computed(() => ({})) })
 
   return computed(() => {
     const themeOverrides = (get(variant.value, name as string) || {})
 
-    return defu(props.variants ?? {}, themeOverrides)
+    return defu(props, themeOverrides)
   })
 }

With defu(props, themeOverrides):

  • Explicit prop values (e.g., color="red") win over theme overrides.
  • undefined props (not passed by parent) fall through to theme overrides, thanks to defu skipping undefined.
  • Vue's reactivity tracking works because defu enumerates the reactive props proxy.

If you want to limit over-tracking (any prop change triggers recompute), consider picking only variant-relevant keys before merging.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function useComponentVariant<C extends keyof UIConfig>(name: C, props: ComponentVariantProps<C>): ComputedRef<ThemeVariantOverrides<ComponentVariants<C>>> {
const { variant } = injectVariantContext({ variant: computed(() => ({})) })
return computed(() => {
const themeOverrides = (get(variant.value, name as string) || {})
return defu(props.variants ?? {}, themeOverrides)
})
export function useComponentVariant<C extends keyof UIConfig>(name: C, props: ComponentVariantProps<C>): ComputedRef<ThemeVariantOverrides<ComponentVariants<C>>> {
const { variant } = injectVariantContext({ variant: computed(() => ({})) })
return computed(() => {
const themeOverrides = (get(variant.value, name as string) || {})
return defu(props, themeOverrides)
})
}
🤖 Prompt for AI Agents
In `@src/runtime/composables/useComponentVariant.ts` around lines 32 - 39, The
computed in useComponentVariant currently merges theme overrides with
props.variants which doesn't exist, so component props (e.g., color, variant)
are ignored and not tracked; update the merge to use the actual props object
(e.g., defu(props, themeOverrides)) so explicit props take precedence and Vue
reactivity tracks prop changes inside the computed returned by
useComponentVariant; optionally, to reduce over-tracking, pick only the
variant-related keys from props before calling defu.

@benjamincanac benjamincanac changed the title feat(UTheme): variants poc feat(Theme): add variants prop for component theming Feb 15, 2026
@benjamincanac benjamincanac marked this pull request as draft February 16, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant