Wildcard nested selector - css

I have the following bit of Sass code :
.c-panel-menu-c {
&.grid_6 {
float: right;
}
}
i need to have float right applied to all grids that start with grid_ . The float should only be set when the grid class is applied to an element that also has c-panel-menu-c.
I wanted to use a wildcard selector like
div[class*='grid_'] {
float:right
}
But am not sure if it's possible the way i need it. Something like
.c-panel-menu-c {
&.div[class*='grid_'] {
vertical-align: top;
}
}
Which doesnt work.
Thank you for any tips/advice.

the problem is the tag name ... you can not concatenate a tag name, for example div, at the end of another selector, like you would with a class name. Let's look at your example:
.c-panel-menu-c {
&.grid_6 {
...
}
}
will return
.c-panel-menu-c.grid_6 { ... }
which is a valid selector. But attaching 'div' at the end (lets leave out the attribute selector part for now)
.c-panel-menu-c {
&div {
...
}
}
does not make sense (and it also isn't possible in Sass, hence you get an error) as the tag name should always be before the class or id selector. This does not change if we add an attribute selector statement, so that's why it does not work the way you tried &div[class*='grid_'].
What you can do, is add the attribute selector directly to the preceding class name, like so:
.c-panel-menu-c {
&[class*='grid_'] {
...
}
}
which would compile to:
.c-panel-menu-c[class=*'grid_'] { ... }
and select everything that has the class .c-panel-menu-c and a class containing with grid_.

Related

Using the "&" (ampersand) operator where the top-most parent is an element tag selector

I have this LESS:
LESS
custom-component {
.random-class {
.decorator-class& wrapper {
...
}
}
that yields
CSS
.decorator-classcustom-component .random-class wrapper {
...
}
which outputs gibberish because there is no such class .decorator-classcustom-component. This happens because the uppermost parent selector is an HTML element tag and not a class or other selector type.
Is there a way to get the characters before the ampersand (.decorator-class) to concatenate on the right side of the uppermost parent selector? Like this:
custom-component.decorator-class .random-class wrapper
?
Note that attempting,
custom-component {
.random-class {
&.decorator-class wrapper {
// ^ ampersand at the beginning of selector
...
}
}
would yield,
custom-component .random-class.decorator-class wrapper
I find it really useful to translate CSS selectors to English for debugging in situations like this. Your desired selector custom-component.decorator-class .random-class wrapper translates to:
Select wrapper elements inside an element with a class random-class, inside a custom-component element with the class .decorator-class.
If that's what you mean, then you need something like this:
custom-component {
&.decorator-class {
.random-class wrapper {
...
}
}

Create css class for whole document with angular

In my CSS file, I have a class:
.test{
background: red;
}
But at the beginning of my app, I'd like to redefine this class based on the server response such that the background becomes blue or green depending on a variable.
It is very important to attribute to this class (.test) the new color as many of my elements have already this class and I don't want to apply a new class to them.
Not sure it's very clear but to summarize, I want to create a class from javascript (using angular 2) that will apply to the whole document.
The code below will find any style rules (including those inside media rules) that are part of the document, and overwrite any styles that are matched by the selector.
You can call modifyStyles('.test', { 'background': 'blue' }) on an instance of the service to make all styles with the .test class to have a blue background. You probably want to play with the way the selector functions, because in its current implementation any rule that has .test anywhere within it will have its background changed. You might prefer changing the regex to ^.test$ so that it matches .test and only .test.
#Injectable()
export class CssUpdateService {
constructor( #Inject(DOCUMENT) private document: Document) {
}
modifyStyles(selector: string, styles: any) {
const rulesToUpdate = this.findRules(new RegExp(`\b${selector}\b`, 'g'));
for (let rule of rulesToUpdate) {
for (let key in styles) {
rule.style[key] = styles[key];
}
}
}
/**
* Finds all style rules that match the regular expression
*/
private findRules(re: RegExp) {
let foundRules: CSSStyleRule[] = [];
let ruleListToCheck = Array.prototype.slice.call(this.document.styleSheets);
for (let sheet of ruleListToCheck) {
for (let rule of (<any[]>(sheet.cssRules || sheet.rules || []))) {
if (rule instanceof CSSStyleRule) {
if (re.test(rule.selectorText)) {
foundRules.push(rule);
}
}
else if (rule instanceof CSSMediaRule) {
ruleListToCheck.push(rule);
}
}
}
return foundRules;
}
}
EDIT (bc I was confused on your requirements initially) -
I don't think there's a good way to modify the global styles file after the application loads, but if I am wrong on that someone please correct me.
The shadow DOM makes this tricky. I would provide a runtime configuration variable to your module and then conditionally add a class to your application's root component.
<div class="outer-app-wrapper" [ngClass]="someValue">
Then in your global styles.css file, you can just define all the different variations of .test there could be.
.someValue1 .test {
background: red;
}
.someValue2 .test {
background: green;
}
.someValue3 .test {
background: yellow;
}
I think if you define all the variations in the styles.css file, you should be able to avoid having to use the 'host-context:' selector in the descendant components. There's no need to add any class to an element outside of Angular's scope like the 'body' element, just add it to the top-most element of your app, and as long as descendant components don't redefine the test class as it is defined in the global stylesheet, it should work fine.
Note - you could also use #HostBinding to add the classes to your root component if you don't want to add a wrapper element or modify an existing one

CSS class precedence

I have two classes, one is used specifically fro certain tags, the other can be used on any tag:
a.action_link_2 {
display:inline-block;
}
.display_none {
display:none;
}
In some circumstances I want to apply both these styles and have tried this:
<a class="action_link display_none">content</a>
However, when rendered in the browser, the 'action_link' class take precedence. I understand that this might be to do with CSS class priority, i.e. tag-specific classes taking precedence. My question is how do I make this tag hidden using these classes and still allow the 'display_none' class to be used on any element to hide it?
you could just remove the a from before the class, and also add body before the display none class to give it a higher priority.
.action_link_2 {
display:inline-block;
}
body .display_none {
display:none;
}
You are right, it because specificity read this
To overcome the problem, you need to increase the specificity for
.display_none class when it is present on action_link_2 .
Just add one more rule, just below all of it
a.display_none {
display:none;
}
This will work , but there will be a problem when you try to add class
.display_none to an anchor, but there is no .action_link_2 class
present.
So the final and best solution would be to use:
.action_link_2.display_none {
display:none;
}
You could try this:
.display_none { display:none !important; }

Parent Selector nested inside &:hover

I am using the Less parent selector to shorten my selectors:
.module-name {
// styles go here
&__sub-module-1 {
//styles go here
}
}
Now my problem is how to continue to use the parent selector for adding the module-name inside a nested :hover statement. E.g. on hover of sub-module-1 I want to change something in sub-module-2. Inside the &:hover statement, what does the parent selector refer to?
.module-name {
&__sub-module1 {
&:hover {
&__sub-module2 {
// on hover of .module-name__sub-module1 change something in .module-name__sub-module2
}
}
}
}
If i write it like this it works, but it defeats the purpose of using the parent selector to automatically fill in the name of the module:
.module-name {
&__sub-module1 {
&:hover {
.module-name__sub-module2 {
// on hover of .module-name__sub-module1 change something in .module-name__sub-module2
}
}
}
}
I hope I could adequately express my problem; any help is appreciated.
The parent selector (&) will always refer to the full parent based on the level at which you are. For example at the first level of nesting, & refers to .module-name. In the second level, it refers to .module-name__sub-module1 and in the third level, it refers to .module-name__sub-module1:hover.
Extract from Less Website: Note that & represents all parent selectors (not just the nearest ancestor or the overall root ancestor)
The emphasised part in the above statement is my inclusion based on the context
For this particular case, you could assign the module-name to a variable and use selector interpolation like below to form the selectors.
The variable value would never change unlike the parent selector (&) irrespective of how many levels of nesting you have and at which level of nesting you are using it.
#module-name: mod-name;
.#{module-name} {
&__sub-module1 {
&:hover {
& .#{module-name}__sub-module2 {
// on hover of .module-name__sub-module1 change something in .module-name__sub-module2
color: blue;
}
}
}
}

How to write "any span that is a child of label is extended by x, whether it has a class or not" using SASS?

I'd like that every span that is a child of a label receive an extension, and at the same time simplify the SASS and remove repetition.
e.g. I'd like to rewrite this:
label > span {
extend #informational;
}
label > span.info {
extend #informational;
/* other info stuff */
}
label > span.error {
extend #informational;
/* other error stuff */
}
Into something resembling this:
label {
span {
extend #informational;
}
span.info {
/* other info stuff */
/* this also gets extended, but no explicit extension statement here */
}
span.error {
/* other error stuff */
/* this also gets extended, but no explicit extension statement here */
}
}
because just extending the span does not also extend a span.xxx (though I'd expect it to).
Is there a way? All the combinations I've attempted (including the one above) have failed.
I've also tried this without using #extend but through repeating the CSS in informational within each relevant block, to the same effect. This makes it a CSS problem (afaics) but if it can be solved using SASS that's fine too.
You may have other style rules that are overriding whatever is in your #informational extension for your span elements with classes. That's as far as I can see as well...
If that is the case you'll have to keep your class selectors so they make up for the less specific span selector, but you can group them together like this so you don't have to repeat the extend #informational; statement (you'll also want to use > if you're specifically only looking for children, and not just descendants at any nesting level):
label {
> span, > span.info, > span.error {
extend #informational;
}
> span.info {
/* other info stuff */
}
> span.error {
/* other error stuff */
}
}
If you're not interested in which specific classes your span elements have, as long as they either have or don't have a class attribute, you could use an attribute selector instead as a cheap hack:
label {
> span, > span[class] {
extend #informational;
}
/* ... */
}

Resources