Working with nested selectors & vuejs css modules - css

Is it possible to work with nested css selectors when using vuejs css modules?
For example, I want to scope this css (so that id does not affect child components):
.list {
...
.item {
...
}
}
In the documentation I could see only not-nested examples, but is it at all convenient, since I'll need then name them like .list-item which resembles BEM. But if I use BEM there is no point in using css modules, is there?

Yes, it's possible to work with nested css selectors so they will not affect child components; use css modules.
You need to use a preprocessor to enable nesting, either LESS or SASS.
If using Single File Components your component would look something like this
<template>
<ul :class="$style.list">
<li :class="$style.item"></li>
</ul>
</template>
<!-- Or lang="less" -->
<style module lang="scss">
.list {
...
.item {
...
}
}
</style>

Yes, nesting css selectors is called using scss. You will have to setup scss.
Example make your style tag in the vue component:
<style scoped lang="scss">
The scoped attribute tells it to apply only to this component.
In regards to bem you can do stuff like this in scss:
.list {
//styles-a
&-item {
//styles-b
}
}
which will convert to this in css:
.list {
//styles-a
}
.list-item {
//styles-b
}

If you want, for example, override CSS class of some UI library in Vue you can use :global keyword. Let's say you have a n-dropdown component from Naive UI library. And you want to customize it with overriding its native very deeeeeply nested n-dropdown-option-body__prefix--show-icon CSS class only in current component using CSS modules. Here is how you can do it:
<template>
<n-dropdown :class="$style.menu">
</n-dropdown>
</template>
<style module>
.menu:global.n-dropdown-menu .n-dropdown-option .n-dropdown-option-body .n-dropdown-option-body__prefix.n-dropdown-option-body__prefix--show-icon {
margin-left: 33px;
margin-right: 19px;
}
</style>
In the end you will get selector which looks something like this
.MobileNavMenu_menu_NhSka.n-dropdown-menu .n-dropdown-option .n-dropdown-option-body .n-dropdown-option-body__prefix.n-dropdown-option-body__prefix--show-icon {
margin-left: 33px;
margin-right: 19px;
}
So all classes after :global keyword will be untouched by module manipulations.
If .n-dropdown-menu should be a child of menu then :global should have a whitespace from both sides:
.menu :global .n-dropdown-menu
Vue will remind you about it with horrible crash

Related

CSS nesting for alternate renderings

The following is meant to toggle stylesheets:
body.light {
background: var(--bg);
color: var(--text);
& a {
color: var(--link_color);
}
}
body.dark {
color: var(--bg);
background: var(--text);
& a {
color: white;
}
}
This snippet is in the last loaded file, amongst a few CSS files.
Problem: while the attributes are executed properly, the nesting for the selectors is not being picked up (It was expected the ampersand was the proper way to go).
There is a framework CSS file (loaded before the file where this code is placed) and it is those attributes that are executed.
What are the syntactic requirements to run properly nested selectors (be they native tag selectors (such as `a) or user-defined selectors?
update
The theme is being generated via a cookies[:theme]
and the body tag adopts it this way:
<body class="<%= cookies[:theme] %>">
To make it work in regular CSS, rewrite without use of &.
Exmaple:
body.light {
background: var(--bg);
color: var(--text);
}
body.light a {
color: var(--link_color);
}
body.dark {
color: var(--bg);
background: var(--text);
}
body.dark a {
color: white;
}
Regarding the ampersand, according to Sass document:
The parent selector, &, is a special selector invented by Sass that’s used in nested selectors to refer to the outer selector. It makes it possible to re-use the outer selector in more complex ways, like adding a pseudo-class or adding a selector before the parent.
Currently, usage of & will require a css preprocessor like Sass, and also the & is widely adopted by many syntax such as Sass, SCSS, Less.
CSS nesting is not possible with standard CSS but only with CSS preprocessors like Sass and Less.
Caniuse.com says
CSS nesting provides the ability to nest one style rule inside another, with the selector of the child rule relative to the selector of the parent rule. Similar behavior previously required a CSS pre-processor.
but the page does not shows any supported browser.
There are some more information on developer.chrome.com. The examples use the > character.
This is because, you used Sass in your project. Sass is a preprocessor scripting language that compiled into CSS. Sass reduces repetition of CSS and therefore saves time.
Let say you have this kind of HTML.
<div class="parent">
parent
<div class="child">
child
<div class="container desc">container</div>
</div>
</div>
assume you need these requirements, you need to make container class to add background to green. and desc class to width 500px.
you can do this by,
.parent {
.container {
background: green;
&.desc {
width: 500px;
}
}
}
One Note, In order to use Sass, you have to enable it from your project.If you use Angular,Vue or React, use
npm i sass
I hope this make sense to you.

How to protect React component from global styles?

I have a react component that will be embedded into an old website. The problem is that this website has some global styles with tag selectors, e.g:
p {
font-size: 14px;
}
Removing these styles from the website is not an option.
So is there any way I can prevent these styles from reaching my component apart from just overriding them?
Create a particular css file for this component and again assign the style for p in this page particularly. It will replace the global one.
OR
You can use inline css because it has more preference than the external css.
You could wrap your .p inside your own class.
Example in plain HTML:
<div class = "you-wrapper">
...
...
...
<p></p>
...
</div>
And in you react code, add your own css:
.you-wrapper p { font-size: 15px; color:red;}
Edited:
/* your css */
.your-wrapper * { all: unset; }
.you-wrapper p { font-size: 15px; color:red;}

Why :host-context is required when :host can suffice the needs in Angular

As I understand :host-context is used to apply styles based on selector of parent.
Lets consider a rule as follows:
:host-context(.red-theme) { background-color: red; }
same can be written using :host selector as folows:
.red-theme :host { background-color: red; }
Then whh is host-context explicitly required?
Use :host if you want to style the component custom HTML element itself.
Where as :host-context is used when you also want to have a component apply a style taking into account context in which component is rendered.
So for example, you could do: (with host-context)
<div class="red-theme">
<app-component></app-component>
</div>
where app-component
<button class="btn btn-theme">Button</button>
and the component style is defined:
:host-context(.red-theme) .btn-theme {
background: red;
}
Useful if you want to have multiple alternative themes on your web application.
:host and :host-context are not Angular features, but CSS features.
Within the demo provided at stackblitz.com/edit/angular-z3xxtu, the Angular components are merged into the DOM and allow plain old CSS selector structure like .parent :host .child { color: red; }.
This changes when using Webcomponents and ShadowDOM.
The ShadowDOM acts as a barrier and encapsulates the contained markup and style, so the .parent cannot style the things on the :host and vice versa. Note cannot is not totally true, see ::part and CSS Custom Properties.
With a structure like <parent> <my-component> ShadowDOM <child> the above CSS rule no longer works and :host-context(.parent) (which could be read as .parent :host) is needed. Sadly as of 2023-01-10 this doesn't work in Firefox and Safari.
In the above demo, :host-context(.red) { color: red; } and .red :host { color: red; } produce the same output because there's no ShadowDOM involved. On top of that, Angular transforms the invalid .test :host in the CSS to .test [_nghost-some-id] which is a valid CSS selector.

Assign Styles to a selector from another selector CSS

Is there a way to have a class or any selector with specific styles, and then share, import or assign those styles to a another selector. This is to avid creating multiple classes and not having to go back to the HTML code all the time to assign such classes.
For example:
.orangeText{
color:orange
}
.strongtext{
font-weight:900
}
a regular scenario would use those clases like this:
<div class="orangeText strongtext">My orange Strong Text</div>
I am looking for something like this:
.orangeText{
color:orange
}
.strongtext{
font-weight:900
}
.orangeStrongTitle{
#.orangeText; /* Import styles from .orangeText*/
#.strongtext; /* Import styles from .strongtext*/
text-decoration:underline;
}
ON HTML:
<div class="orangeStrongTitle">My orange Strong Text</div>
PLEASE NOTE THAT #.orangeText IS MY INVENTION TO GIVE AN EXAMPLE OF WHAT I WANT, I DON'T KNOW IF SUCH THING EXISTS, I WAS INSPIRED ON #import
Is this possible?
With traditional CSS it seem's that you can't.
See this topic : Is it possible to combine classes in CSS?
Unless you use LESS or SASS's 'mixin' features, there's no real way in
plain CSS to "inherit" other classes. The best you can do is apply
all three classes to your DOM element. If your problem is that you
have a ton of those classes already on a page and you want to add
properties to all of them, you can do:
.foo, .bar, .foobar {
color: red;
}
and that will apply color: red to any element that has .foo or .bar or
.foobar.
EDIT
Since you're asking for an exemple, here is how the mixin feature of SASS works :
#mixin mixin-name {
margin: 0;
padding: 0;
}
ul {
#include mixin-name;
}
Having this will compile into this :
ul {
margin: 0;
padding: 0;
}

Angular 2: How to style host element of the component?

I have component in Angular 2 called my-comp:
<my-comp></my-comp>
How does one style the host element of this component in Angular 2?
In Polymer, You would use ":host" selector. I tried it in Angular 2. But it doesn't work.
:host {
display: block;
width: 100%;
height: 100%;
}
I also tried using the component as selector:
my-comp {
display: block;
width: 100%;
height: 100%;
}
Both approaches don't seem to work.
Thanks.
There was a bug, but it was fixed in the meantime. :host { } works fine now.
Also supported are
:host(selector) { ... } for selector to match attributes, classes, ... on the host element
:host-context(selector) { ... } for selector to match elements, classes, ...on parent components
selector /deep/ selector (alias selector >>> selector doesn't work with SASS) for styles to match across element boundaries
UPDATE: SASS is deprecating /deep/.
Angular (TS and Dart) added ::ng-deep as a replacement that's also compatible with SASS.
UPDATE2: ::slotted
::slotted is now supported by all new browsers and can be used with `ViewEncapsulation.ShadowDom
https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted
See also Load external css style into Angular 2 Component
/deep/ and >>> are not affected by the same selector combinators that in Chrome which are deprecated.
Angular emulates (rewrites) them, and therefore doesn't depend on browsers supporting them.
This is also why /deep/ and >>> don't work with ViewEncapsulation.Native which enables native shadow DOM and depends on browser support.
I have found a solution how to style just the component element. I have not found any documentation how it works, but you can put attributes values into the component directive, under the 'host' property like this:
#Component({
...
styles: [`
:host {
'style': 'display: table; height: 100%',
'class': 'myClass'
}`
})
export class MyComponent
{
constructor() {}
// Also you can use #HostBinding decorator
#HostBinding('style.background-color') public color: string = 'lime';
#HostBinding('class.highlighted') public highlighted: boolean = true;
}
UPDATE:
As Günter Zöchbauer mentioned, there was a bug, and now you can style the host element even in css file, like this:
:host{ ... }
Check out this issue. I think the bug will be resolved when new template precompilation logic will be implemented. For now I think the best you can do is to wrap your template into <div class="root"> and style this div:
#Component({ ... })
#View({
template: `
<div class="root">
<h2>Hello Angular2!</h2>
<p>here is your template</p>
</div>
`,
styles: [`
.root {
background: blue;
}
`],
...
})
class SomeComponent {}
See this plunker
In your Component you can add .class to your host element if you would have some general styles that you want to apply.
export class MyComponent{
#HostBinding('class') classes = 'classA classB';
For anyone looking to style child elements of a :host here is an example of how to use ::ng-deep
:host::ng-deep <child element>
e.g :host::ng-deep span { color: red; }
As others said /deep/ is deprecated
Try the :host > /deep/ :
Add the following to the parent.component.less file
:host {
/deep/ app-child-component {
//your child style
}
}
Replace the app-child-component by your child selector

Resources