Summary: I need to style the contents of a <slot>, from the child component. I'm using scoped css and the styles don't apply:
I have the following two components:
<!-- Parent.vue -->
<template>
<h1>{{ msg }} from Parent</h1>
<Child>
<h1>{{ msg }} from Child</h1>
</Child>
</template>
...
<style scoped>
h1 {
color: green;
}
</style>
<!-- Child.vue -->
<template>
<slot></slot>
</template>
...
<style scoped>
h1 {
color: red;
}
</style>
I want the 2nd <h1> to be red, but it's green, since the component is rendered with something like:
<h1 data-v-452d6c4c data-v-2dcc19c8-s>Hello from Child</h1>
<style>
h1[data-v-452d6c4c] {
color: green;
}
h1[data-v-2dcc19c8] {
color: red;
}
</style>
data-v-452d6c4c comes from Parent, and data-v-2dcc19c8-s from Child
If the second attribute, in the <h1> tag, was just data-v-2dcc19c8 the style I wanted would be applied, but since it has that -s suffix (slot?), it doesn't.
I could probably find some other solution with a class or something, but I rarely use <slot> and I want to understand the inner workings. That -s tells me that what I'm trying to do can be dealt with the help of the framework, what am I missing?
A working sample:
https://codesandbox.io/s/condescending-brown-ilfwn
Use the new :slotted selector in Vue 3:
Child.vue
<template>
<slot></slot>
</template>
<script>
export default {
name: "Child",
};
</script>
<style scoped>
:slotted(h1) {
color: red !important;
}
</style>
In Vue 3, child scoped styles don't affect slotted content by default.
In your particular example, the !important modifier is also necessary because the parent also defined an h1 style which would take precedence otherwise
Related
I'm using css variables in my component and set the width of a div based on data computed in the setup()
setup(props) {
const progressBar = PositionService.getProgressBar(props.position);
const progressWidth = `${progressBar}%`;
...
return { ..., progressWidth };
}
Then I'm using this variable as css variable.
<style lang="scss" scoped>
.progress-bar-width {
--progress-bar-width: v-bind("progressWidth");
width: var(--progress-bar-width);
}
</style>
When rendering the page i noticed that an inline style is added to the html parent component resulting in
.....
The CSP is blocking inline style so this approach doesn't work.
How can I use css variables without inline style?
It's a hacky solution but with the handicap of no inline styles it's the only thing I can think of:
Add a "style" component to your template. This will be replaced with <style> tags in the DOM. Inside the component add the desired CSS variable to :root
<component :is="`style`">
:root { --progress-bar-width: {{ progressWidth }}; }
</component>
<div class="progress-bar-width"></div>
<style lang="scss" scoped>
.progress-bar-width {
width: var(--progress-bar-width);
}
</style>
how about use inline style?
https://vuejs.org/guide/essentials/class-and-style.html#binding-inline-styles
<div :style="`--progress-bar-width:${progressWidth}`" >
your component
</div>
I have Single-File Components and I want to use it in different places like subscribe page and footer.
But I want to style to be different.
Is it possible to style local components?
Component file: Newsletter.vue
<template>
<form>
<label>Enter your email address here</label>
<input type="text" name="email" required/>
</form>
<button #click="subscribe" >
SUBSCRIBE
</button>
</template>
Subscribe page: Subscribe.vue
let say style Newsletter background-color green and align to left
<template>
<h2>
Subscribe to receive Newsletter
</h2>
<Newsletter></Newsletter>
</template>
<script>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<style scoped>
Newsletter{
background-color: green;
text-align:left;
}
</style>
Subscribe page: Footer.vue
but in Footer, I want to style Newsletter background-color red and align center
<template>
<h2>
JOIN OUR NEWSLETTER
</h2>
<Newsletter></Newsletter>
</template>
<script scoped>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<style>
Newsletter{
background-color: red;
text-align:center;
}
</style>
This is possible! All you have to do is add the scoped attribute to the style tag.
Here is the snippet from the docs just for reference here:
<template>
<button class="btn btn-close">X</button>
</template>
<style scoped>
.btn-close {
background-color: red;
}
</style>
This will only apply the styles locally, to the component which contains the style tag. Keep in mind though, you really should avoid using tag selectors because they are the most generic. It's a better practice to use a class selector because it has a higher specificity, meaning it is less likely to be over-written (id selectors or inline styles take precedence). I would suggest doing something like this:
<template>
<h2>
Subscribe to receive Newsletter
</h2>
<Newsletter class="newsletter"></Newsletter>
</template>
<script>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<style scoped>
.newsletter {
background-color: green;
text-align:left;
}
</style>
In the example above, the newsletter class will make the background-color green only in this single-file component.
There are many options. You may benefit from using the ::v-deep combinator which allows leaking the style to the child components.
Using your example with a small change:
<template>
<h2>
Subscribe to receive Newsletter
</h2>
<Newsletter class="newsletter"></Newsletter>
</template>
<script>
import Newsletter from "#/components/Newsletter.vue";
export default {
components: {
Newsletter
}
}
</script>
<!-- this is the change. Updated to have `::v-deep` to allow style to "leak" to the child component -->
<style scoped>
::v-deep .newsletter {
background-color: green;
text-align:left;
}
</style>
See more at:
https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
Another option to consider is using global style in place of the local style and be sure that each shared class element has a unique parent.
In one component such as Subscribe:
<style lang="scss">
.subscribe {
.newsletter {
// style for this specific version of newsletter
}
}
</style>
In another component where ".somethingelse" should be the specific type of component:
<style lang="scss">
.somethingelse {
.newsletter {
// style for this other specific version of newsletter
}
}
</style>
More info about global specificity:
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
I am trying to style my child components. Isit wrong to put the style in a parent component? It appears that does not work.
I put the style for .card-page in the top level element (containing expenses-module where I use it)
<dom-module id="expenses-app">
<template>
<style>
...
.card-page {
display: block;
width: 100%;
}
</style>
<app-drawer-layout>
<app-header-layout>
...
<iron-pages selected="{{routeData.view}}" attr-for-selected="name">
<dashboard-module name="dashboard" route="{{subroute}}"></dashboard-module>
<expenses-module name="expenses" route="{{subroute}}"></expenses-module>
<settings-module name="settings" route="{{subroute}}"></settings-module>
</iron-pages>
</app-header-layout>
</app-drawer-layout>
</template>
In expenses module,
<paper-card heading="Expenses" class="card-page">...
</paper-card>
Seems like if I move the styles into expenses-module it works.
You cannot directly style elements inside custom element from their parents like that, because Polymer processes the style within <dom-module> and will apply styles only to direct child members. It will not descend into child custom elements.
In other words, standard CSS selectors will only work within the scope of the declaring component. Both in Shadow and Shady DOM.
For your styles to work with nested elements, you should use CSS mixins and properties. All PolymerElements and many 3rd party elements will come with such styling extension points. The naming usually follow the convention, where the main mixin is called same as the element itself. Additionally, there may be more specific mixins and properties, which style only parts of the element. <paper-card> docs for example lists --paper-card mixin, --paper-card-content mixin, --paper-card-header-color and more.
If you want to better control the styling of elements you use, you would want to create your own CSS mixins/properties and #apply() them to selected elements. See how in the example below --my-elem-card-page applies only to one of the two paper cards.
<!DOCTYPE html>
<html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
<link href="paper-card/paper-card.html" rel="import"/>
</head>
<body>
<my-wrapper></my-wrapper>
<dom-module id="my-elem">
<template>
<style>
.card-page {
#apply(--my-elem-card-page);
}
</style>
<paper-card heading="my-elem specific style" class="card-page">
<div class="card-content">
Content here
</div>
</paper-card>
<paper-card heading="Default style" class="unstyled-page">
<div class="card-content">
Content here
</div>
</paper-card>
</template>
</dom-module>
<dom-module id="my-wrapper">
<template>
<style>
# will be ignored
paper-card {
width: 200px;
}
my-elem{
--paper-card: {
color: blue;
display: block;
}
}
my-elem {
--my-elem-card-page: {
color: red;
}
}
</style>
<my-elem></my-elem>
</template>
</dom-module>
<script>
Polymer({
is: 'my-elem'
});
Polymer({
is: 'my-wrapper'
});
</script>
</body>
</html>
I am building a Polymer Single Page Interface with a lot of custom elements.
Now I want my elements to have some sort of master style, which I can define in the index.html or my main content element. Think of it like this:
index.html
<style>
.classWhichWillBeUsedInCustomElements {
mainColor: #e0e0e0;
}
</style>
or
<script>
mainColor = "#e0e0e0";
</script>
my-cool-element.html
<polymer-element name="my-cool-element">
<template>
<paper-button style="color: {{mainColor}}"></paper-button>
</template>
</polymer-element>
or
<polymer-element name="my-cool-element">
<template>
<style>
.coolButton {
width: 300px;
color: {{mainColor}};
}
</style>
<paper-button class="coolButton"></paper-button>
</template>
</polymer-element>
Except that this doesn't work.
I have tried:
Creating a global variable window.defaultColor and using it like color: {{defaultColor}};
Using core-style in a parent element, without much luck
Creating a css class in my index.html and calling it in a custom element
What is the right way to achieve this? I am trying to avoid using Less
Use the following pattern in the index.html or a global stylesheet:
<style>
body /deep/ .classWhichWillBeUsedInCustomElements {
mainColor: #e0e0e0;
}
</style>
Then you could use the class within the custom element. The global style will punch the shadow boundary. You could replace body with any other element or selector under which you want to punch the shadow dom boundary.
More on deep here: https://www.polymer-project.org/0.5/articles/styling-elements.html#cat
I have one element that extends another and I'm having trouble getting the styling to override the parent using the example in the documentation. For example, say I want to style the praise in the parent element:
<polymer-element name="polymer-cool">
<template>
<style>
:host #p {
color: red;
}
</style>
You are <span id='p'>{{praise}}</span> <content></content>!
</template>
...
</polymer-element>
but change that in an extension of that element:
<polymer-element name="polymer-cooler" extends="polymer-cool">
<template>
<!-- A shadow element render's the extended
element's shadow dom here. -->
<style>
#p {
color: blue;
}
</style>
<shadow></shadow> <!-- "You are cool Matt" -->
</template>
...
</polymer-element>
You can see in the JSfiddle below, that I haven't been able to change the color of the span#p. I've tried a few other things like
polymer-cooler #p {
color: blue;
}
And tried putting the style inside of the tags, but no luck. Hoping it's possible and I'm just missing something.
http://jsfiddle.net/jamstooks/tpyL9/
Well, this looks like this works. I'd love to get some clarification from someone on whether this is the best way to do this:
<polymer-element name="polymer-cooler" extends="polymer-cool">
<template>
<style>
{
:host::shadow #p
color: blue;
}
</style>
<shadow></shadow>
</template>
...
</polymer-element>
http://jsfiddle.net/jamstooks/tpyL9/4/
EDIT: 7/22/14
Per the comment from Scott below, I have updated the code above from :host /deep/ #p to :host::shadow #p