I'm building a Vue JS plguin with some scoped styling, it works perfectly fine within another website when including it as a component, e.g: <my-component></my-component>.
However, the styling that I've added to the component is scoped to the component which means it doesn't affect the parent's site styling, however the parent's site styling DOES affect my component's styling, is there a way to prevent this without being super specific with my component's styling and using !important for everything?
Scoped styles work by adding a special key to your selectors. So your component would not affect other components. Parent components still may affect your component styling. And global styles also can intervene.
Well, you have 2 options if you don't want to use !important
First, and preferred - use specific BEM naming.
It is really easy to implement by using sass (scss).
e.g.
.mycompoment{
background:#fff;
&--body{
color:#eb0b0;
&--title{
font-size:5rem;
}
}
&--footer{
position:relative;
}
//etc
}
which would compile to the following css:
.mycompoment {
background: #fff;
}
.mycompoment--body {
color: #eb0b0;
}
.mycompoment--body--title {
font-size: 5rem;
}
.mycompoment--footer {
position: relative;
}
The other option is to increase your selector's specificity.
Try to use more direct descendant selectors. >
But still, outer css may still affect the values you don't even think of specifying.
For example position:absolute; top:-20px; or box-sizing/display/opacity and many different props your aren't aware of.
Related
Using VueJS, I need to display different colors for each user. The color depends on the user settings.
In my vuetify.js, I have:
export default new Vuetify({
theme: {
themes: {
light: {
primary: user.colorMain ? user.colorMain : '#F39200',
It works when I use:
$vuetify.theme.themes.light.primary
in my components.
But I would need to override the Sass variables too, in my variable.scss file:
$primary-color: #f39200;
Is there a way to override my sass variables dynamically from a JS variable?
tl:dr; no, it's not possible to change Sass variable values at runtime, because they no longer exist at runtime. They have been translated into plain (static) CSS.
However, like with any CSS values, you can override them.
Sass variables are only used to pre-process SCSS into CSS at compile time. The result of compilation is static CSS, loaded when the app is mounted. In simpler terms, the app doesn't know that CSS was preprocessed from an SCSS source. For it, it's static CSS.
Example:
$primary-color: #f39200;
.my-button { color: $primary-color; }
will output the following CSS code:
.my-button { color: #f39200; }
If you want runtime dynamic values, you have two options:
Use CSS variables.
Produce the following CSS, via your preferred method (from SCSS/CSS/Stylus, doesn't matter, as long as this is the output):
.my-button { color: var(--primary-color); }
... and, anywhere in the chain of parents or on the element itself:
<div :style="{'--primary-color': someDynamicColor }" />
With the above in place, when you change someDynamicColor, at runtime, the color changes in DOM.
Use Vue3's "reactive styles" feature:
<script>
export default {
data: () => ({ someDynamicColor: 'red' })
}
</script>
<style>
.my-button {
color: v-bind('someDynamicColor');
}
</style>
Again, this is dynamic. If you change/animate the value of someDynamicColor on the element, the CSS value will be applied in DOM. It doesn't have to be a data prop, it can be a prop, computed, ...
Important notes:
when using CSS variables (1.), the value of var(--primary-color) doesn't have to be set in the same component, but it has to be set on a direct ancestor of the current DOM element.
when using reactive styles (2.), the prop/computed referenced in CSS/SCSS has to be set in the current component's scope.
Under the hood, reactive styles also use CSS variables: they're uniquely named at compile time.
CSS variables don't use specificity. If you override the value set by some grand-parent at parent level, the child has no way of reading the grand-parent's value, regardless of specificity. If you have such a case, you probably want to manage the grandparent value in external state and provide it to both grand-parent and child.
This question already has an answer here:
How do I create a Vue 3 custom element, including child component styles?
(1 answer)
Closed 11 months ago.
I am using Vue 3 to create some Web Components. I would like to use Single File Components and be able to nest them and deploy, but the styling is missing on the child component.
I have simplified what I am trying to do with the following example.
MDivElement.ce.vue - a div that wraps MButton
<template>
<div id="mdiv">
MDiv
<MButtonElement>MButton</MButtonElement>
</div>
</template>
<script>
import MButtonElement from "./MButtonElement.ce.vue";
export default {
components: {
MButtonElement,
},
};
</script>
<style scoped>
#mdiv {
color: blue;
}
</style>
MButtonElement.ce.vue - a simple Button
<template>
<button id="mbutton">MButton</button>
</template>
<script>
export default {};
</script>
<style scoped>
#mbutton {
color: red;
}
</style>
Example
My problem is that all the style is gone from the button when using <m-div></m-div>
Can SFC be nested (with styles) and used as Web Components?
I will compile using Vite and distribute.
Thanks!
As of April 2022 and Vue 3.2 there is open bug #4662 about this behavior, as in the question How do I create a Vue 3 custom element, including child component styles?. One of the workarounds suggested in the bug is to make sure you are consuming the custom element by name in your template rather than using Vue and import to compose your components in custom elements.
You may need to update your SFC build plugin version so that it properly understands your .ce.vue extensions as custom elements that treat their styles differently. See the SFC as Custom Element docs:
defineCustomElement also works with Vue Single-File Components (SFCs). However, with the default tooling setup, the <style> inside the SFCs will still be extracted and merged into a single CSS file during production build. When using an SFC as a custom element, it is often desirable to inject the <style> tags into the custom element's shadow root instead.
The official SFC toolings support importing SFCs in "custom element mode" (requires #vitejs/plugin-vue#^1.4.0 or vue-loader#^16.5.0). An SFC loaded in custom element mode inlines its <style> tags as strings of CSS and exposes them under the component's styles option. This will be picked up by defineCustomElement and injected into the element's shadow root when instantiated.
Finally, as another potential workaround, you can to put the <style> within the element itself in order to accommodate the Shadow DOM technique that powers Web Components, or otherwise include the styles in the defineCustomElement Vue call specific to Custom Elements.
As in the MDN web components documentation:
You can affect the nodes in the shadow DOM in exactly the same way as non-shadow nodes — for example appending children or setting attributes, styling individual nodes using element.style.foo, or adding style to the entire shadow DOM tree inside a <style> element. The difference is that none of the code inside a shadow DOM can affect anything outside it, allowing for handy encapsulation.
In contrast, Vue SFC scoping rolls up the styles into the element itself, but applies the styles to the whole document with an arbitrary data element (data-v-f3f3eg9 in the docs). Those styles are applied outside of the shadow dom, and consequently your styles set in SFC <style> tags aren't inherited inside of it.
I'm trying to have a Dark Theme button on my application and change the whole theme in a click. It is already working but I wanted to find an easier way to accomplish that.
I have created a button in the navbar that sets a localStorage variable to "dark" or "light" on click. Upon loading the application, its store will read the localStorage and have it available to the whole application.
Excerpt from store.js:
state {
...
theme: localStorage.getItem('theme') || 'light',
...
}
In my application, if I want to change the theme to a breadcrumb, I would do:
<b-breadcrumb :data-theme="theme">
<b-breadcrumb-item active>Start</b-breadcrumb-item>
</b-breadcrumb>
import {mapState} from 'vuex'
export default {
...
computed: {
...mapState(['theme'])
}
}
and in the custom.scss file:
[data-theme="dark"] {
$breadcrumb-bg: $dark !important;
}
And it would have changed the whole component color.
This does NOT work.
However, this DOES:
.breadcrumb[data-theme="dark"] {
background-color: $dark !important;
}
My question is: Is there an easy way to change all components using data attributes and SCSS variables or do I have to mannually select classes and change the components I want?
Unfortunately it is not possible to use Sass variables in custom data attributes as I wanted because the specification for data attributes won't allow them. They expect a DOMString name, not a Sass variable.
https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute
I have a single-file vue3 component.
Its template looks like this.
<template>
<div ref="elements"></div>
</template>
I have scoped styles in the same file:
<style scoped>
.el {
color: red;
}
</style>
Now I want to add element in the script.
<script>
export default {
name: "App",
mounted() {
const div = this.$refs.elements;
const el = document.createElement("div");
el.setAttribute("class", "el");
el.innerText = "Hello World!";
div.appendChild(el);
},
};
</script>
The result shows that the element is not styled according to the class in the scoped styles.
Is there a way to apply styling to the elements added through script, while keeping styles in the scope?
After some research this seems to be an answer.
Vue scoped styling is the internal Vue feature. So, there should be a way to distinguish same class names on different components. Vue does it by adding a special id to each class name. You can find it by inspecting elements in the browser (e.g. data-v-a2c3afe).
When building a component, Vue processes its template and properly tracks only nodes you have declared for rendering there. It does not know anything it might get from somewhere. It actually makes sense and pushes you to write everything you expect to see in the template, so that it does not happen that you suddenly see something wrong in the DOM (especially if you are taking someone's code).
I have rewritten code, so that I still have scoped styles, but with no elements appending from the script and the code now allows to clearly see what is being rendered. This is probably the property of any framework - it makes you to stick to some patterns, so that everyone in the team/community knows what behavior to expect and writes more consistent code.
I'm trying to apply styling to MTableEditRow actions component which comes from MUI. I've tried doing it with actionCellStyle and overriding action components but they do not apply to that specific element. The only solution which worked for me so far is using styled components (or styled MUI API) like so :
const StyledEditRow = styled(MTableEditRow)({
verticalAlign: "top",
height: "110px",
"& .MuiTableCell-root": { padding: "20px 5px !important" }
})
I would like to avoid using !important to override the style applied in the component if at all possible. It's a little bit weird that actionsCellStyle works only for one set of actions, or is there something I'm missing? Any help would be great!