Writing styled-jsx without selector - next.js

Without specifying/wrapping with a selector (button {}), the following styled-jsx style declarations will work. The button is properly styled which is great. However, usage like this is not documented in website so i wonder if this syntax is officially supported and safe to use?
<button>
<style jsx>{`
background-color: red;
:hover {
background-color: #ccc;
}
`}
</style>
Test
</button>
Another example, using resolve tag that works too:
const { className, styles } = css.resolve`
font-weight: bold;
`;

styled-jsx uses stylis css preprocessor under the hood. This is how styled-jsx transforms content of a style tag:
transformedCss = transform(
isGlobal ? '' : getPrefix(dynamic, staticClassName),
plugins(css, pluginsOptions),
{ splitRules, vendorPrefixes }
)
Note, that transform here is a wrapper function around stylis.
Thus styles declared inside <style jsx> tag will be wrapped with dynamically generated class and then transformed with stylis.
In your case styled-jsx will produce this css:
If you use global selector no class selector will be added to the generated code and so produced css wont be applied to any elements on the page.
From my point of view it won't be a mistake to use styles without a selector, however, you should do it carefully with a <style jsx> tag because in this case styles will be applied to every element inside a component.
Using this feature with css.resolve looks much more safe since you may manually pick the elements to apply css.
As far as I know, the official documentation misses the explanation of such an important detail.

Related

Styling angular material components from child components

HomeComponent templates includes a router outlet
/* home.component.ts */
...
<mat-sidenav-container>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
that will render a ListComponent which style should set the overflow of mat-sidenav-content.
Why is the following not working?
/* list.component.css */
:host-context(mat-sidenav-content) { /* same for .mat-sidenav-content */
overflow: unset !important;
}
From my understanding the selector should pick any mat-sidenav-content traversing all the DOM to the root.
I'm afraid this isn't how :host-context works. Host context is for applying styles to the component host conditionally based on it's ancestors. In your example the list component should have overflow: unset but if there was another instance of it without mat-sidenav-content as an ancestor it would not have this. This can be useful for applying styles based on a theme as shown in the documentation here:
https://angular.io/guide/component-styles#host-context
I have included an example on stackblitz with your overflow css and I also set the text to be red in that case so it's more obvious and you can see how it applies to one and not the other. Unfortunately there is not a way to do what I believe you are trying to do which is apply a style to a parent when the component is inside that parent.
The example I have also has ::ng-deep in the other component for completeness, as I have seen used to apply styles to other components by some. I would recommend you stay away from this as well as this style will remain after being first applied(try going to that link and then back to list) and ::ng-deep is deprecated anyway.
https://stackblitz.com/edit/angular-material-sidenav-generate-nav-5fvfjq?file=src/app/list/list.component.css
Edit: The stackblitz has been updated to include an example of :host
It's better to think of the host element in HTML terms rather than as a component, so in the dom <app-list> is the host element rather than what's in the template. If you wanted to style stuff in there you wouldn't need to use :host. I've updated my stackblitz example to include host and if you inspect the css you might get a better idea of how it works.
The HTML looks like this
<app-list _nghost-qnl-c20="">
<p _ngcontent-qnl-c20=""> This text should be red only when inside mat-sidenav-content. The font family is set by host.
</p>
</app-list>
And the css:
mat-sidenav-content[_nghost-qnl-c20], mat-sidenav-content [_nghost-qnl-c20] {
color: red;
}
[_nghost-qnl-c20] {
font-family: Arial, Helvetica, sans-serif;
}
So you can see that host uses an attribute generated by angular and :host by itself is simply that attribute and :host-context(element) becomes element [attribute] in the css.

Vue: how to change the style of scoped child components

I use vant ui components in vue, like buttons, I want to make a litte changes of it's style, like color, border ...., but I don't how to complete it, anyone could help me solve the problem? Thanks in advance!
I have tried add css or inline-style on element, but don't work!
Any custom component's css can be changed by using deep selector
GitHub Reference- https://github.com/vuejs/vue-loader/issues/913
Use ::v-deep in this case, as /deep/ will get deprecated.
Reference - Deep Selector
Just inspect class of the rendered element which you want to modify using devtools in chrome or any browser console.
Then, In you consuming component, modify it
<style scoped>
::v-deep vant-component-class {
background: red; //
}
</style>

How and where to use ::ng-deep?

How and where can one use ::ng-deep in Angular 4?
Actually I want to overwrite some of the CSS properties of the child components from the parent components. Moreover is it supported on IE11?
Usually /deep/ “shadow-piercing” combinator can be used to force a style down to child components. This selector had an alias >>> and now has another one called ::ng-deep.
since /deep/ combinator has been deprecated, it is recommended to use ::ng-deep
For example:
<div class="overview tab-pane" id="overview" role="tabpanel" [innerHTML]="project?.getContent( 'DETAILS')"></div>
and css
.overview {
::ng-deep {
p {
&:last-child {
margin-bottom: 0;
}
}
}
}
it will be applied to child components
I would emphasize the importance of limiting the ::ng-deep to only children of a component by requiring the parent to be an encapsulated css class.
For this to work it's important to use the ::ng-deep after the parent, not before otherwise it would apply to all the classes with the same name the moment the component is loaded.
Using the :host keyword before ::ng-deep will handle this automatically:
:host ::ng-deep .mat-checkbox-layout
Alternatively you can achieve the same behavior by adding a component scoped CSS class before the ::ng-deep keyword:
.my-component ::ng-deep .mat-checkbox-layout {
background-color: aqua;
}
Component template:
<h1 class="my-component">
<mat-checkbox ....></mat-checkbox>
</h1>
Resulting (Angular generated) css will then include the uniquely generated name and apply only to its own component instance:
.my-component[_ngcontent-c1] .mat-checkbox-layout {
background-color: aqua;
}
USAGE
::ng-deep, >>> and /deep/ disable view encapsulation for specific CSS rules, in other words, it gives you access to DOM elements, which are not in your component's HTML. For example, if you're using Angular Material (or any other third-party library like this), some generated elements are outside of your component's area (such as dialog) and you can't access those elements directly or using a regular CSS way. If you want to change the styles of those elements, you can use one of those three things, for example:
::ng-deep .mat-dialog {
/* styles here */
}
For now Angular team recommends making "deep" manipulations only with EMULATED view encapsulation.
DEPRECATION
"deep" manipulations are actually deprecated too, BUT it's still working for now, because Angular does pre-processing support (don't rush to refuse ::ng-deep today, take a look at deprecation practices first).
Anyway, before following this way, I recommend you to take a look at disabling view encapsulation approach (which is not ideal too, it allows your styles to leak into other components), but in some cases, it's a better way. If you decided to disable view encapsulation, it's strongly recommended to use specific classes to avoid CSS rules intersection, and finally, avoid a mess in your stylesheets. It's really easy to disable right in the component's .ts file:
#Component({
selector: '',
template: '',
styles: [''],
encapsulation: ViewEncapsulation.None // Use to disable CSS Encapsulation for this component
})
You can find more info about the view encapsulation in this article.
Make sure not to miss the explanation of :host-context which is directly above ::ng-deep in the angular guide : https://angular.io/guide/component-styles. I missed it up until now and wish I'd seen it sooner.
::ng-deep is often necessary when you didn't write the component and don't have access to its source, but :host-context can be a very useful option when you do.
For example I have a black <h1> header inside a component I designed, and I want the ability to change it to white when it's displayed on a dark themed background.
If I didn't have access to the source I may have to do this in the css for the parent:
.theme-dark widget-box ::ng-deep h1 { color: white; }
But instead with :host-context you can do this inside the component.
h1
{
color: black; // default color
:host-context(.theme-dark) &
{
color: white; // color for dark-theme
}
// OR set an attribute 'outside' with [attr.theme]="'dark'"
:host-context([theme='dark']) &
{
color: white; // color for dark-theme
}
}
This will look anywhere in the component chain for .theme-dark and apply the css to the h1 if found. This is a good alternative to relying too much on ::ng-deep which while often necessary is somewhat of an anti-pattern.
In this case the & is replaced by the h1 (that's how sass/scss works) so you can define your 'normal' and themed/alternative css right next to each other which is very handy.
Be careful to get the correct number of :. For ::ng-deep there are two and for :host-context only one.
Just an update:
You should use ::ng-deep instead of /deep/ which seems to be deprecated.
Per documentation:
The shadow-piercing descendant combinator is deprecated and support is
being removed from major browsers and tools. As such we plan to drop
support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until
then ::ng-deep should be preferred for a broader compatibility with
the tools.
You can find it here
I looked through all those answers and found nobody mentioned a child component can pass a style CSS in from its parent.
In component ts file, you can use this:
#Input() styles: any = {};
In component html file, you use this:
[ngStyle]="styles"
in parent, you use this :
<yourComponent [styles]="{backgroundColor: 'blue', 'font-size': '16px'}">
Please see more details here: Best way to pass styling to a component
In this way, we didn't break encapsulation, which is one of those most important Object orientation principles
Use ::ng-deep with caution. I used it throughout my app to set the material design toolbar color to different colors throughout my app only to find that when the app was in testing the toolbar colors step on each other. Come to find out it is because these styles becomes global, see this article Here is a working code solution that doesn't bleed into other components.
<mat-toolbar #subbar>
...
</mat-toolbar>
export class BypartSubBarComponent implements AfterViewInit {
#ViewChild('subbar', { static: false }) subbar: MatToolbar;
constructor(
private renderer: Renderer2) { }
ngAfterViewInit() {
this.renderer.setStyle(
this.subbar._elementRef.nativeElement, 'backgroundColor', 'red');
}
}

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;
}

Can I make a template style external?

In Google Polymer lesson 1, they have an example that includes an inline stylesheet.
I'd like to move it to a css file, but the style is inside a template tag.
To make matters worse, it says it's
Used inside a shadow DOM tree, the :host pseudo-class matches the
element that hosts the tree. In this case, it matches the
element.
Q: Can I move this style to a css file?
If you download the seed element from this page (or just look at the listed contents), you'll see that it uses an external css file. You can examine the element html to see how it is used, but it's very simple:
<polymer-element name="seed-element" attributes="notitle author">
<template>
<link rel="stylesheet" href="seed-element.css" />
<h1>Hello from seed-element</h1>
<content></content>
</template>
<script>
Polymer('seed-element', {
//...
}
</script>
</polymer-element>
Then in seed-element.css by default you have the following:
:host {
display: block;
}
The way I understand it (in my limited knowledge) is that if you were to rename seed-element to my-element, this would be the same as doing
my-element {
display: block;
}
If you want to style the elements that are part of a custom elements Shadow DOM you have to use the ::shadow selector, or /deep/ selector if you want to select elements regardless how many levels deep they sit inside of Shadow DOM.
So from the outside, you would style a p element that's inside x-foo's Shadow DOM like this:
x-foo::shadow p {
}
You can read about all that stuff here: http://www.polymer-project.org/articles/styling-elements.html

Resources