I want to separate the styles in a Vue.js component in modules.
Each style module will have far more than just a class, and new classes will be added regularly. So, it will be hard to change the entire component's template. So, I'm looking for a more practical solution.
I came with the idea of, using a v-if in the styles, but not exactly sure how it should be implemented or if such thing is possible after all.
It will be way more practical, if just depending on the name sent with props, the entire styles changes.
<template>
<div class="color-text">
Text in the color of the class with just the name
</div>
</template>
<script>
export default {
name: 'comp',
props: ['name']
}
</script>
<!-- General styles -->
<style scoped>
div{
float: right;
}
</style>
<!-- red styles -->
<style module v-if="name === 'red'">
.color-text{
color: red;
}
</style>
<!-- blue styles -->
<style module v-if="name === 'blue'">
.color-text{
color: blue;
}
</style>
<!-- green styles -->
<style module v-if="name === 'green'">
.color-text{
color: green;
}
</style>
If I was tackling this I'd use a transitive class value. and not worry about props at all.
<template>
<div class="color-text">
Text in the color of the class with just the name
</div>
</template>
<script>
export default {
name: 'comp'
}
</script>
<!-- General styles -->
<style scoped>
div{
float: right;
}
.red .color-text{
color: red;
}
.blue .color-text{
color: blue;
}
.green .color-text{
color: green;
}
</style>
then you can use the class property to pass in your color type
<div id="app">
<comp class="red"></comp>
<comp class="green"></comp>
<comp class="blue"></comp>
</div>
I've put together an example jsfiddle though it may need some tweaking when it comes to scoped styles and how webpack handles the injection
Related
I am doing something like StackOverflow editor. Fetch markdown text, transfer it to HTML, and inject it into the preview area. But when I tried to apply CSS to the injected HTML elements, it was ignored. In browser inspection, I can not find the stylesheet I write for the elements. I guess this is about the order of CSS rendering. Any suggestion will be appreciated.
<template>
<div>
<div class="item">
<label for="content">Contents</label>
<textarea name="content" id="content" v-model="mdtext"></textarea>
</div>
<div id="preview"></div>
</div>
</template>
<script>
import marked from 'marked'
export default {
data () {
return {
mdtext: ''
}
},
watch: {
mdtext: function () {
document.getElementById('preview').innerHTML = marked(this.mdtext)
}
}
}
</script>
<style scoped>
...
#preview p{
width: 100%;
word-break: break-all;
word-wrap: break-word;
}
...
</style>
You're using scoped css, which makes Vue add that data-v- attribute to the p as well. However, the generated <p> doesn't not have that same data-v- attribute, which is why it's not working.
You can easily fix it by using the deep selector:
<style scoped>
#preview >>> p {
}
</style>
Reference: https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors
I have a parent component:
<template>
<ChildComponent :styles="styles" />
</template>
<script>
export default {
data: () => ({
styles: `
p {
color: red
}
`
})
}
</script>
And this is the child component:
<template>
<p>Hello World</p>
</template>
<script>
export default {
props: {
styles: {
type: String,
required: true
}
}
}
</script>
<style scoped>
</style>
Now I want to use those styles provided by the parent component in child as scoped styles. Like for example:
<!-- ChildComponent.vue -->
<style scoped>
p {
color: red
}
</style>
Is there any way to do so?
If you want to target the child elements with scoped styling you have to use the deep selector.
Which can be done with
a >>> b { color : red; }
/deep/ a b { color : red; }
a::v-deep b { color : red; }
Here is the full explanation: https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements
If you wanna add the style in your child component, based on the parent component which is calling it, you can pass an attribute as a prop and use it as a class into the child component. Following your example:
Parent component:
<template>
<ChildComponent styles="parent-style" />
</template>
Child component:
<template>
<section :class="styles">
<p>Hello World</p>
</section>
</template>
<script>
export default {
props: {
styles: {
type: String,
required: true
}
}
}
</script>
<style lang="scss" scoped>
.parent-style {
p {
color: red;
}
}
</style>
Please note that it does not answer scoped CSS.
I am using CSS modules, still might help or just to improve following approach.
Send class names to child component from parent, such as:
Parent Template
<template>
<header :class="$style.Header">
<!-- send class names to **Child-Component** using `v-bind` through the className attribute -->
<HeaderLogo :className="$style.Header__logo" />
</header>
</template>
Child-Template
<template>
<div :class="className">Blah</div>
</template>
CSS Module
<style lang="scss" module>
.Header {
white-space: nowrap;
&__logo {
width: 100%;
}
}
</style>
How would I do something like this:
<style>
Nested {
color: blue;
}
</style>
<Nested />
i.e. How do I apply a style to a component from its parent?
You need to pass props to the parent component with export let, then tie those props to class or style in the child component.
You can either put a style tag on the element in the child you want to style dynamically and use a variable you export for the parent to determine the value of a style directly, then assign the color on the tag like this:
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested color="green"/>
<!-- in Nested.svelte -->
<script>
export let color;
</script>
<p style="color: {color}">
Yes this will work
</p>
Upside here is flexibility if you only have one or two styles to adjust, downside is that you won't be able to adjust multiple CSS properties from a single prop.
or
You can still use the :global selector but just add a specific ref to the element being styled in the child like so:
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested ref="green"/>
<style>
:global([ref=green]) {
background: green;
color: white;
padding: 5px;
border-radius: .5rem;
}
</style>
<!-- in Nested.svelte -->
<script>
export let ref;
</script>
<p {ref}>
Yes this will work also
</p>
This ensures global only affects the exact ref element inside the child it's intended for and not any other classes or native elements. You can see it in action at this REPL link
The only way I can think of is with an additional div element.
App.svelte
<script>
import Nested from './Nested.svelte'
</script>
<style>
div :global(.style-in-parent) {
color: green;
}
</style>
<div>
<Nested />
</div>
Nested.svelte
<div class="style-in-parent">
Colored based on parent style
</div>
Multiple Nested elements
You could even allow the class name to be dynamic and allow for different colors if you use multiple Nested components. Here's a link to a working example.
You could use inline styles and $$props...
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested style="background: green; color: white; padding: 10px; text-align: center; font-weight: bold" />
<!-- in Nested.svelte -->
<script>
let stylish=$$props.style
</script>
<div style={stylish}>
Hello World
</div>
REPL
using :global(*) is the simplest solution.
No need to specify a class in the child if you want to style all immediate children for example
In the parent component:
<style>
div > :global(*) {
color: blue;
}
<style>
<div>
<Nested />
<div>
Nested will be blue.
I take a look and found nothing relevant (maybe here), so here is an alternative by adding <div> around your custom component.
<style>
.Nested {
color: blue;
}
</style>
<div class="Nested">
<Nested />
</div>
Maybe you will found something but this one works.
The way I do it is like this:
<style lang="stylus">
section
// section styles
:global(img)
// image styles
</style>
This generates css selectors like section.svelte-15ht3eh img that only affects the children img tag of the section tag.
No classes or tricks involved there.
I am writing a basic Polymer Component that should be able also to change its style at the change of a property.
When I try to change the theme file, I use the id to retrieve it but I always get null.
Here is my code:
[other polymer imports]
<link rel="import" href="/themes/my-theme.html" id="current_theme"/>
<dom-module id="my-app">
<template>
<style>
:host{
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
}
:host ::content app-header{
background: var(--app-primary-color);
color: var(--app-text-color);
}
:host ::content user-app-toolbar{
background: var(--app-primary-color);
color: var(--app-text-color);
}
...
</style>
...
<script>
Polymer({
is: 'my-app',
properties: {
state: {
type: String,
reflectToAttribute: true,
observer: '_stateChanged',
},
},
_stateChanged: function(page) {
// get theme file
theme.href = '/theme/red.html';
}
});
</script>
</template>
</dom-module>
my-theme.html
<style is="custom-style">
my-app {
--app-primary-color: grey;
--app-secondary-color: black;
--app-text-color: white;
}
</style>
The problem is how to implement the "get theme file". I tried many things:
document.querySelector('#current_theme'):
[returns null, I think it is because it uses the main document and not the one of the element]
Polymer(dom).querySelector('current_theme'): [undefined]
this.$$["current_theme"] [undefined]
Any idea of how to do this?
P.S: the idea of changing the theme in this way is taken from this stack overflow question
Static
You need to put a style tag with your theme id (Polymer 2.0)
<link rel="import" href="/themes/my-theme.html"/>
<dom-module id="my-app">
<template>
<style include="my-theme-xxx"></style>
<style>
:host{
...
my-theme.html
<dom-module id="my-theme-xxx"><template><style>
my-app {
--app-primary-color: grey;
--app-secondary-color: black;
--app-text-color: white;
}
</style></template></dom-module>
Dynamic
Yet I found only this way to change element css by theme dynamicaly.
<link rel="import" href="my-css.html">
<dom-module id="my-app">
<template>
<style include="my-css"></style>
<button class="btn-primary-dark">Dark button</button>
<button class="btn-primary-light">Light button</button>
<button class="btn-primary-default">Default button</button>
<br><br>
<button on-click="setTheme1">Set theme 1</button>
<button on-click="setTheme2">Set theme 2</button>
</template>
<script>
class MyApp extends Polymer.Element {
static get is() { return 'my-app'; }
// Dynamicaly change vars
setTheme1() {
Polymer.updateStyles({
'--dark-primary-color' : '#689F38',
'--default-primary-color' : '#8BC34A',
'--light-primary-color' : '#DCEDC8',
'--text-primary-color' : '#212121'
});
}
setTheme2() {
Polymer.updateStyles({
'--dark-primary-color' : '#1f8f37',
'--default-primary-color' : '#818bbf',
'--light-primary-color' : '#f0e82f',
'--text-primary-color' : '#333333'
});
}
}
window.customElements.define(MyApp.is, MyApp);
</script>
</dom-module>
my-css.html
<dom-module id="my-css">
<template>
<style>
.btn-primary-dark {
background-color: var(--dark-primary-color);
color: var(--secondary-text-color);
}
.btn-primary-light {
background-color: var(--light-primary-color);
color: var(--secondary-text-color);
}
.btn-primary-default {
background-color: var(--default-primary-color);
color: var(--secondary-text-color);
}
</style>
</template>
</dom-module>
Digging into documentation of Polymer I found this quote from this official Polymer doc page
Note: You should only use custom-style to define styles for the main document. To define styles for an element's local DOM, just use a < style> block.
And moreover, from this older documentation
Frequently you want to define custom property values at the document level, to set a theme for an entire application, for example. Because custom properties aren't built into most browsers yet, you need to use a special custom-style tag to define custom properties outside of a Polymer element. Try adding the following code inside the tag of your index.html file
Moving the my-theme.html into the index.html page, I am able to retrieve it from my polymer element using
document.querySelector('#current_theme')
Beside this, I am not able to change the theme dynamically. But since the title of the question was about accessing the resource, I solved the problem moving it. As craPkit said, I am not sure what I was trying to do is actually possible.
I was trying to be able to color an element based on the attribute. I know, that binding inside is not supported, so I decided to try
<core-style id="block-elem">
:host {
background-color: {{g.bgcolor}};}
</core-style>
When I tried it with:
<polymer-element name="block-elem" attributes="bgcolor" noscript>
...
<script>
CoreStyle.g.bgcolor = 'red';
</script>
everything worked. But what I really want to do is to create similar objects with different colors. So I tried
<polymer-element name="block-elem" attributes="bgcolor">
<script>
Polymer('block-elem', {
bgcolor: "",
}
);CoreStyle.g.bgcolor = bgcolor;
</script>
I am creating object with
<block-elem bgcolor="red">TEST</block-elem>
and nothing. Is it possible to implement that funcionality? Maybe there is another option which I didn't even think of.
Summary
If all you want is to style each element differently based on a
(possibly data bound) attribute. Then it is simpler to use
an observer to set the elements style element instead of using the
core-style element.
observe: {
bgcolor: function() {
console.log("Setting color to " + this.bgcolor);
this.style.backgroundColor = this.bgcolor;
}
}
But if you are want to do something more complex with core-style such as setting the theme of an element based on one attribute. Then using a css attribute selector works in both chrome and in polyfill browsers (tested with firefox).
The below example shows the difference of using:
global: changes all elements on the page at once. Good if you want a
consistent theme across elements
nested themes: my favorite but doesn't work in polyfill browsers currently
css attribute selector: works across at least chrome and firefox on linux
element styling: setting the each element style directly
Components
<link rel="import" href="../../webcomponents/bower_components/polymer/polymer.html">
<link rel="import" href="../../webcomponents/bower_components/core-style/core-style.html">
<!-- Based on ideas from https://www.polymer-project.org/0.5/docs/elements/core-style.html -->
<!-- Using global sets all core-styles that use the global variable -->
<core-style id="demo-bindingcorestyle-style-global">
:host {
display: block;
background-color: {{g.bgcolor}};
}
</core-style>
<polymer-element name="demo-bindingcorestyle-global" attributes="bgcolor">
<template>
<core-style ref="demo-bindingcorestyle-style-global"></core-style>
GLOBAL: This is a test trying to set the background to {{bgcolor}} but all elements share global and will be the last color
</template>
<script>
Polymer({
observe: {
bgcolor: function() {
console.log("Setting color to " + this.bgcolor);
// All elements get this global style
CoreStyle.g.bgcolor = this.bgcolor;
}
}
});
</script>
</polymer-element>
<!-- To specify different colors use a Common base style and then themed styles that can be changed -->
<!-- NOTE: id cannot use "-" in name if it is going to be used with list. syntax because it is treated as subtractions -->
<core-style id="demobindingcorestylestylecommon">
:host {
display: block;
}
</core-style>
<core-style id="demo-bindingcorestyle-style-red">
{{list.demobindingcorestylestylecommon.cssText}}
:host {
background-color: red;
color: yellow;
}
</core-style>
<core-style id="demo-bindingcorestyle-style-blue">
{{list.demobindingcorestylestylecommon.cssText}}
:host {
background-color: blue;
color: white;
}
</core-style>
<core-style id="demo-bindingcorestyle-style-green">
{{list.demobindingcorestylestylecommon.cssText}}
:host {
background-color: green;
color: black;
}
</core-style>
<polymer-element name="demo-bindingcorestyle-substyles" attributes="theme">
<template>
<core-style ref={{themename}}></core-style>
Themed: This is a test trying to specify the theme as {{theme}} works in latest chrome with shadowdom but fails in polyfill browsers.
</template>
<script>
Polymer({
theme: 'blue',
computed: {
themename: '"demo-bindingcorestyle-style-"+theme'
},
observe: {
theme: function() {
console.log("Setting theme to " + this.theme);
// All elements get this global style
//this.$.mystyle.ref = "demo-bindingcorestyle-style-" + this.theme;
}
}
});
</script>
</polymer-element>
<!-- Using attribute selectors works both with shadowdom and polyfill -->
<core-style id="demo-bindingcorestyle-style-themeable">
:host {
display: block;
}
:host([theme="red"]) {
background-color: red;
<!-- Probably will want other themed color here -->
}
:host([theme="green"]) {
background-color: green;
<!-- Probably will want other themed color here -->
}
:host([theme="blue"]) {
background-color: blue;
<!-- Probably will want other themed color here -->
}
</core-style>
<polymer-element name="demo-bindingcorestyle-themeable" attributes="theme" noscript>
<template>
<core-style ref="demo-bindingcorestyle-style-themeable"></core-style>
Themed: This is a test trying to specify the theme as {{theme}} it should actually work.
</template>
</polymer-element>
<!-- Set background based on bgcolor attribute -->
<polymer-element name="demo-bindingcorestyle-justdoit" attributes="bgcolor">
<template>
Just set bgcolor: This is a test trying to set the background to {{bgcolor}} but all elements share global and will be the last color
</template>
<script>
Polymer({
observe: {
bgcolor: function() {
console.log("Setting color to " + this.bgcolor);
this.style.backgroundColor = this.bgcolor;
}
}
});
</script>
</polymer-element>
Usage
<html lang="en">
<head>
<script src="../../webcomponents/bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="import" href="../../webcomponents/bower_components/polymer/polymer.html">
<link rel="import" href="demo-bindingCoreStyle.html">
<title>demo-bindingCoreStyle test page</title>
</head>
<body>
<h1>Demo</h1>
<h2>Using Global</h2>
<demo-bindingcorestyle-global bgcolor="red"></demo-bindingcorestyle-global>
<hr>
<demo-bindingcorestyle-global bgcolor="green"></demo-bindingcorestyle-global>
<hr>
<demo-bindingcorestyle-global bgcolor="blue"></demo-bindingcorestyle-global>
<h2>Using substyles</h2>
<demo-bindingcorestyle-substyles theme="blue"></demo-bindingcorestyle-substyles>
<hr>
<demo-bindingcorestyle-substyles theme="red"></demo-bindingcorestyle-substyles>
<hr>
<demo-bindingcorestyle-substyles theme="green"></demo-bindingcorestyle-substyles>
<h2>Using theming with attribute css selector</h2>
<demo-bindingcorestyle-themeable theme="blue"></demo-bindingcorestyle-themeable>
<hr>
<demo-bindingcorestyle-themeable theme="green"></demo-bindingcorestyle-themeable>
<hr>
<demo-bindingcorestyle-themeable theme="red"></demo-bindingcorestyle-themeable>
<h2>Just set background</h2>
<demo-bindingcorestyle-justdoit bgcolor="blue"></demo-bindingcorestyle-justdoit>
<hr>
<demo-bindingcorestyle-justdoit bgcolor="green"></demo-bindingcorestyle-justdoit>
<hr>
<demo-bindingcorestyle-justdoit bgcolor="red"></demo-bindingcorestyle-justdoit>
</body>
</html>