Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e8f6242
feat(UTheme): variants poc
Bobakanoosh Feb 13, 2026
5bb24e4
Merge branch 'v4' into feat/theme-variants
benjamincanac Apr 17, 2026
fcb206e
Merge branch 'v4' into feat/theme-variants
benjamincanac Apr 21, 2026
d7b5565
feat(Theme): add variants prop and unify theme context
benjamincanac Apr 21, 2026
d12961b
feat(Theme): replace `:variants` with `:props` for any-prop overrides
benjamincanac Apr 28, 2026
c342c29
Merge branch 'v4' into feat/theme-variants
benjamincanac Apr 28, 2026
59e7e92
refactor(Theme): rename useComponentDefaults to useComponentProps and…
benjamincanac Apr 28, 2026
c47df55
refactor(Theme): switch ThemeDefaults to flat literal interface
benjamincanac Apr 29, 2026
cf80cda
feat(cli): scaffold components with useComponentProps and auto-regist…
benjamincanac Apr 29, 2026
9f23628
docs(contributing): align component templates with useComponentProps …
benjamincanac Apr 29, 2026
e299d0c
feat(Theme): migrate all components to :props proxy
benjamincanac Apr 29, 2026
09b8dfe
test(Theme): cover closer-context precedence for FieldGroup and Avata…
benjamincanac Apr 29, 2026
52fce9c
docs(contributing): correct useComponentProps chain and fix CLI prose…
benjamincanac Apr 29, 2026
4d138d4
fix(Theme): catch bare prop refs from inherited interfaces
benjamincanac Apr 29, 2026
1753a5a
fix(Theme): route remaining script-block prop reads through the proxy
benjamincanac Apr 29, 2026
02e00c5
fix(Carousel,DashboardSidebar): align proxy reads and JSDoc with runtime
benjamincanac Apr 29, 2026
c47bac7
fix(ContentNavigation): route `level` reads through the proxy
benjamincanac Apr 29, 2026
90dcd77
fix(form): respect <UTheme :props> on bare inputs without UFormField
benjamincanac Apr 30, 2026
f148b79
docs(theme): document _props + ?? props.X fallback in form/group comp…
benjamincanac Apr 30, 2026
ac90ac2
test(theme): cover bare form inputs and cross-component nested :props
benjamincanac Apr 30, 2026
c8c1025
feat(Listbox): expose color prop matching theme variant
benjamincanac Apr 30, 2026
299276a
docs(theme): clarify nested inheritance and form component precedence
benjamincanac Apr 30, 2026
eddf64c
fix: use local proxy-aware `useForwardProps` for the rest of the comp…
sandros94 Apr 30, 2026
aa2c602
fix(Theme): drop ThemeDefaults entries that don't honor :props
sandros94 Apr 30, 2026
a98ea77
test(Theme): pin ThemeDefaults to the #build/ui registry
sandros94 Apr 30, 2026
f66ba55
chore(components): clean spacing
benjamincanac Apr 30, 2026
ffc484f
docs(theme): update
benjamincanac May 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions playgrounds/nuxt/app/pages/components/button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
</Navbar>

<Matrix v-slot="props" :attrs="attrs">
<UButton label="Button" v-bind="props" />
<UButton label="Link" to="/" v-bind="props" />
<UTheme :variants="{ button: { variant: 'soft', color: 'warning' } }" :ui="{ button: { base: 'font-bold' } }">
<UButton label="Button" v-bind="props" />

Check failure on line 29 in playgrounds/nuxt/app/pages/components/button.vue

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 22)

Trailing spaces not allowed
<UButton label="Link" to="/" v-bind="props" />
</UTheme>

<UButton label="Disabled" disabled v-bind="props" />
<UButton label="Disabled link" to="#" disabled v-bind="props" />
<UButton label="Loading" loading v-bind="props" />
Expand Down
11 changes: 7 additions & 4 deletions src/runtime/components/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { defu } from 'defu'
import { useForwardProps } from 'reka-ui'
import { useAppConfig } from '#imports'
import { useComponentUI } from '../composables/useComponentUI'
import { useComponentVariant } from '../composables/useComponentVariant'
import { useComponentIcons } from '../composables/useComponentIcons'
import { useFieldGroup } from '../composables/useFieldGroup'
import { formLoadingInjectionKey } from '../composables/useFormField'
Expand All @@ -64,6 +65,8 @@ const slots = defineSlots<ButtonSlots>()

const appConfig = useAppConfig() as Button['AppConfig']
const uiProp = useComponentUI('button', props)
const variantProp = useComponentVariant('button', props)

const { orientation, size: buttonSize } = useFieldGroup<ButtonProps>(props)

const linkProps = useForwardProps(pickLinkProps(props))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -104,12 +107,12 @@ const ui = computed(() => tv({
}
}, appConfig.ui?.button || {})
})({
color: props.color,
variant: props.variant,
color: variantProp.value.color,
variant: variantProp.value.variant,
size: buttonSize.value,
loading: isLoading.value,
block: props.block,
square: props.square || (!slots.default && !props.label),
block: variantProp.value.block,
square: variantProp.value.square || (!slots.default && !props.label),
leading: isLeading.value,
trailing: isTrailing.value,
fieldGroup: orientation.value
Expand Down
10 changes: 8 additions & 2 deletions src/runtime/components/Theme.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
import { computed } from 'vue'
import { provideThemeContext } from '../composables/useComponentUI'
import type { ThemeUI } from '../composables/useComponentUI'
import { provideVariantContext, type VariantUI } from '../composables/useComponentVariant'

export interface ThemeProps {
ui: ThemeUI
ui?: ThemeUI
variants?: VariantUI
}

export interface ThemeSlots {
Expand All @@ -16,7 +18,11 @@
const props = defineProps<ThemeProps>()

provideThemeContext({
ui: computed(() => props.ui)
ui: computed(() => props.ui ?? {}),

Check failure on line 21 in src/runtime/components/Theme.vue

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 22)

Unexpected trailing comma
})

provideVariantContext({
variant: computed(() => props.variants ?? {})
})
</script>

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/composables/useComponentUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export type ThemeUI = {
[K in keyof typeof ui]?: ThemeSlotOverrides<(typeof ui)[K]>
}

export type ThemeRootContext = {
export type ThemeUIContext = {
ui: ComputedRef<ThemeUI>
}

const [injectThemeContext, provideThemeContext] = createContext<ThemeRootContext>('UTheme', 'RootContext')
const [injectThemeContext, provideThemeContext] = createContext<ThemeUIContext>('UThemeUI', 'RootContext')

export { provideThemeContext }

Expand Down
40 changes: 40 additions & 0 deletions src/runtime/composables/useComponentVariant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { ComputedRef } from 'vue'
import { computed } from 'vue'
import defu from 'defu'
import { createContext } from 'reka-ui'
import type { ComponentConfig, TVConfig } from '../types/tv'
import type * as ui from '#build/ui'
import { get } from '../utils'
import type { AppConfig } from '@nuxt/schema'

type UIConfig = TVConfig<typeof ui>
type ComponentVariants<C extends keyof UIConfig> = ComponentConfig<(typeof ui)[C], AppConfig, C>['variants']

type VariantValue<V> = [V] extends ['true' | 'false'] ? boolean : V
type ThemeVariantOverrides<T> = {
[K in keyof T]?: VariantValue<T[K]>

Check failure on line 15 in src/runtime/composables/useComponentVariant.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 22)

Expected indentation of 2 spaces but found 4
};

Check failure on line 16 in src/runtime/composables/useComponentVariant.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 22)

Extra semicolon

export type ThemeVariantContext = {
variant: ComputedRef<VariantUI>
}

export type VariantUI = {
[K in keyof UIConfig]?: ThemeVariantOverrides<ComponentVariants<K>>

Check failure on line 23 in src/runtime/composables/useComponentVariant.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, 22)

Expected indentation of 2 spaces but found 4
}

const [injectVariantContext, provideVariantContext] = createContext<ThemeVariantContext>('UThemeVariant', 'RootContext')

export { provideVariantContext }

type ComponentVariantProps<C extends keyof UIConfig> = ThemeVariantOverrides<ComponentVariants<C>>

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)
})
Comment thread
benjamincanac marked this conversation as resolved.
Outdated
}
Loading