Setting dynamic global styles programmatically in Vue - css

I have a Vue component that needs to control the background color of the entire page dynamically. It has an internal state, and that state has the side-effect of causing the background of the page to change.
Ordinarily at the component level this would be accomplished by using v-bind in CSS:
<style lang="scss">
.some-class {
background-color: v-bind("some.variable");
}
</style>
This will work and the component's background will become the color defined in some.variable.
However, trying to use v-bind on :root (or anything outside of the component's scope) will fail. i.e.:
<style lang="scss">
html {
background-color: v-bind("some.variable");
}
</style>
… will not set the CSS property correctly. This is because the variable is declared at the component level. i.e.:
CSS:
html {
background-color: var(--something)
}
HTML:
<html>
<div style="--something">
</html>
What is therefore the "correct" or suggested practice? Do I manage it as a side-effect with watchers? Is there a flag to pass to v-bind?

The best practice really depends on what is your goal.
If you want to set the background for your app only (maybe not the entry document), you can put your code inside the App.vue component. Now your variable will take the root scope of your app and apply to any class that is inside the #app element.
If you have to set the background for the body or html element, you have no choice but to use a watcher. The code is something like that:
watch(someVariable, ()=>{
document.body.setProperty('--background-color', someVariable.value);
})

Related

Vue Vite scoped css won't assign style

I'm trying to add a background-image to a view in vue vite. I don
't want to set this background-image on every view so I tried to add it inside the views site using a scoped css style.
But once I assign scoped to the style tag it won't use the style...
Example code which doesn't work:
<style lang="css" scoped>
body{
background: url("../../assets/images/background.jpg") !important;
}
</style>
Once I remove the scoped it would work but it would add the background to every view which shouldn't happen. Does anyone know why this happens?
From the docs
When a <style> has the scoped attribute, its CSS will apply to elements of the current component only.
This means thst only the elements in your <template> will get the style and since there is no <body> in your template, then it doesn't get style.

What scoped CSS selector allows me to override the mdDialog container width?

Given a typical Angular Material dialog, which has a max-width of 80vw set on .mat-dialog-container, how can I formulate a selector to override it? I'd like a true full-width dialog.
The problem is scoping--Angular-CLI compiles component CSS with scope attribute selectors.
Therefore, this:
.parent .child
becomes:
.parent[some_attr] .child[some_other_attr]
However, this particular element doesn't seem attached to any component--it doesn't have a dynamically-generated attribute on it.
I've attempted overrides in both the dialog stylesheet and the host component's stylesheet with no success.
Angular special selectors
Dialog Plunkr
Let me try again. I'm not doing a good job of explaining the issue.
Let's say I add this to my host component stylesheet:
.mat-dialog-container {
max-width: 100%;
}
I have a build watch running, so the app is recompiled. The CSS output is now this:
.mat-dialog-container[_ngcontent-c6] {
max-width: 100%;
}
However, that element doesn't actually have the attribute _ngcontent-c6 on it. That attribute is applied to other elements which are inside siblings of ancestors of .mat-dialog-container. The selector is just wrong.
If I add that CSS to the dialog component's stylesheet, something similar happens, but with a different attribute ID.
If you can add an id to your main body tag and you want it to be on all of these dialogs you can use this
<style>
#bodyID .mat-dialog-container {
max-width:100vw;
background-color: blue;
}
</style>
It will override the current style, at least in the plunker you supplied.
If you need specific style for each dialog, did you look into this?
how-to-style-child-components-from-parent-components-css-file
You don't need a body id, because as you've mentioned the selector is rewritten by Angular such that it stops matching the element altogether.
But yeah it seems the only way around this is to just throw your hands up and forget about component stylesheet scoping and add your CSS rule to the page stylesheet. The caveat, of course, is that this rule needs to be added to every page that uses the component, which can be seen as absurd depending on what your component is intended to be used for and by whom.

Overriding the encapsulated CSS of external component

I was wondering how to override the encapsulated CSS of an external component.
So I am using material2 in my project and the tabs component has a the attribute overflow set on tab-body. Is it possible to override the overflow value?
You can use the special css /deep/ instruction. See the documentation
So, if you have
app
sub-component
target-component
<div class="target-class">...</div>
You can put in your apps css (or less):
/deep/ .target-class {
width: 20px;
background: #ff0000;
}
Obviously, you can put this css fragment in sub-component as well.
From this article
Although the style of a component is well isolated, it can still be easily overridden if necessary. For that, we just need to add an attribute to the body of the page:
<body override>
<app></app>
</body>
The name of the attribute can be anything. No value is needed and the name override makes it apparent what its being used for. To override component styles, we can then do the following:
[override] hello-world h1 {
color:red;
}
Where override is the attribute, hello-world is the target component, and h1 is whatever you are trying to restyle. (get this right or it wont work).
Your component hello-world would be
selector: 'hello-world',
styles: [`
h1 {
color: blue;
}
`],
template: ` <h1>Hello world</h1> `
I think this is the most elegant way.
Alternatively if you are building a library of some sort, you can reset the styling altogether by doing something fancy in your css like:
:host-context(.custom-styles) {
//.. css here will only apply when there is a css class custom-styles in any parent elem
}
So then to use your component you'd use
<hello-world class="custom-styles">
But this is way less convenient than the first option.
::ng-deep .tag-or-css-class-you-want-to-override {
/* Add your custom css property value. */
}
The syntax ::ng-deep is used to override outside css class or tags without using ViewEncapsulation.None.
I see variations of this question a lot and since this is the top question on the subject I want to give the simplest answer. ng-deep and similar functionality is deprecated, so it's best to just rely on vanilla CSS.
Simply create a CSS selector with a higher specificity.
Most people (including myself) get hung up trying to do that because they don't understand two things:
Angular View Encapsulation
CSS Specificity
Angular View Encapsulation
View Encapsulation ensures CSS within a component only affects that component. To affect other components, you need some global CSS. You can do this by using a global style file like styles.css or by disabling View Encapsulation on a component.
#Component({
...
encapsulation: ViewEncapsulation.None
})
CSS Specificity
When two selectors select the same element, the CSS that actually gets applied is based on specificity: https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
You can increase specificity by simply adding more elements to your CSS selector. For example p.className is more specific than just .className. If you're lazy, you can just repeat a class name to increase specificity. .className.className is more specific than .className.
So to override any CSS in an Angular project, go into styles.css and repeat the class selector until your CSS has a higher specificity than the original.
.className.className.className {
color: red;
}
Didn't work? Add another .className.
Just check the class that is being applied to the tabs by the external component (use Inspector or any other tool). In your style css file, add the same name of the class for the tabs and set the overflow property along with adding !important to it to make sure it overwrites the previous one. Also make sure your css link to the page is added after the external component css link if any.
Hope this helps.
::ng-deep .css-class-you-want-to-override{
/*your custom css property value. like below */
background: white !important;
}

Issue with upgrade to Polymer 0.5.1 and styling paper-dialog

So I just updated my project from Polymer v0.4.2 to v0.5.1 of the Polymer library. One thing that seemed to have changed is how the paper-dialog element is implemented.
In v0.4.2, when I had a paper-dialog inside of my custom element, I could customize it with CSS inside of my element using core-style elements.
In v0.5.1, if I understand correctly, the paper-dialog is no longer implemented inside my component, but instead it's implemented in the core-overlay-layer element which is in the html page outside of my component.
So does that mean that I now have to add a CSS style sheet to the html page that contains my component? If so, then I can no longer use core-style along with the benefits of the CoreStyle.g object. It also means that everything related to my component is no long all encapsulated inside of my component.
Please tell me that I am wrong and that there is a way for me to style the paper-dialog from within my component still.
Thanks!
In Polymer 0.5.1 the layered property (doc: https://www.polymer-project.org/docs/elements/core-elements.html#core-overlay) defaults to true which allows it to always display above page content. If layered is false, the dialog may not display on top if there is something after it in DOM with a higher stacking context.
However because layered reparents the dialog to a global core-overlay-layer it's not possible to style it from an outer scope. There are a couple options for styling:
If you know you don't have any DOM with a higher stacking context than the dialog, set layered="false" to get the non-layered behavior and you can style it from the outer scope.
Style the dialog with a /deep/ rule in a global style. You can still use core-style by referencing the style in the global scope. You can also include it in the same file as your element definition, e.g.
<core-style id="x-dialog">
html /deep/ #dialog {
color: red;
}
</core-style>
<core-style ref="x-dialog"></core-style>
<polymer-element name="my-element" noscript>
<template>
<paper-dialog id="dialog"></paper-dialog>
</template>
</polymer-element>
Extend paper-dialog and style the new element:
<polymer-element name="my-paper-dialog" extends="paper-dialog" noscript>
<template>
<!-- or use core-style -->
<style>
:host {
color: red;
}
</style>
</template>
</polymer-element>
Live examples: http://jsbin.com/subanakuna/1/edit?html,output

How to avoid copy-paste when styling web components from the outside?

Say, I am using a <core-input> inside my web component, and I want to style the <input> element inside the <core-input>.
For that I would need to do
<style>
:host::shadow input {
background-color: green
}
</style>
Now, the selector is okay, but I don't want to define the style properties here, because I have a CSS file made by a designer guy, that looks like this:
.input-like-things {
background-color: green
}
On the other hand, the designer guy is not going to produce almost identical CSS files for each web component I use that has something looking like an input.
How do I correctly apply the ".input-like-things" class (or its contents) to the <input> field inside <core-input>?
I am not considering /deep/ as an option.
I created a custom component that can copy the contents of the ".input-like-things" class into the <style> tag, and that works, but only in Chrome. I could not get it to work in Firefox as long as data binding does not work inside <style> in ShadowDOM polyfill.
Help?

Resources