I'm starting to implement a reusable Vue component library for in-house use on a team of 20+ developers across a couple of applications. A common pattern I have used in the past in Vue 2 is applying a class to a child component to be able to to style it as needed from the parent. This was an intentional feature by the Vue team, according to this documentation. In Vue 2, even when inheritAttrs
was false
, the class
(and style
) attributes still fell through to the root element, perhaps for this reason. Something like this:
Parent:
<template>
<div>
<MyInput class="my-child" />
</div>
</template>
<style scoped>
/* This style gets applied to the root <div> */
.my-child {
width: 300px;
}
</style>
MyInput:
<template>
<!-- Even with inheritAttrs: false, classes are bound here in Vue 2 -->
<div>
<label>...</label>
<input v-bind="$attrs" ... />
</div>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
In Vue 3, this was a breaking change introduced - the class
now gets applied to the child that the attributes are bound to using v-bind="$attrs"
(if applicable), whether using inheritAttrs: false
or in the case of a multi-root component. But that means that (I believe) there is no simple way to style the root element. Nor the child element that is inheriting attributes with scoped styles, for that matter, because of how scoping works.
Parent:
<template>
<div>
<MyInput class="my-child" />
</div>
</template>
<style scoped>
/* This style does nothing in Vue 3 - to the root element nor the child element */
.my-child {
width: 300px;
}
</style>
MyInput:
<template>
<div>
<label>...</label>
<!-- With inheritAttrs: false, classes are bound here in Vue 3, but can't be targeted by scoped styles -->
<input v-bind="$attrs" ... />
</div>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
In this situation, without creating global style rules,
is there any way to apply styles to the MyInput root
div
from the parent? AFAIK, there isn't, except maybe something like this:Parent:
<style scoped> div { ... } </style>
Not very useful. Or to add something like a
containerStyles
prop that can be applied to the rootdiv
:<template> <div :style="containerStyles"> <label>...</label> <input v-bind="$attrs" ... /> </div> </template> <script> export default { inheritAttrs: false, props: { containerStyles: { type: Object, default: null } } } </script>
what are my options for applying styles to the child
input
? Here are the solutions I'm aware of:- Use the style attribute.
<template> <MyInput style="width:300px" /> </template>
- Use the
:deep()
selector.<template> <MyInput class="my-child" /> <template> <style scoped> :deep(.my-child) { width: 300px; } </style>
- Use
module
styling.<template> <MyInput class="$style.myChild" /> </template> <style module> .myChild { max-width: 300px; } </style>
- Use the style attribute.
Is there a best practice for something like this? Especially if with multiple components, say for example,
MyButton
has the classes and styles bound to a rootbutton
element,MyInput
has them bound to a childinput
element, andMyDialog
is a multi-root component with them bound to adialog
element. How do you make these use cases consistent?