I am not sure if this is possible, but is there a way for sass to identify which li a particular class is attached to?
If I had:
<ul>
<li></li>
<li></li>
<li class="Selected"></li>
<li></li>
<li></li>
</ul>
And I'm trying to get a mixin to switch a CSS property based on where the "Selected" class is located. So something like:
ul li { background: url(#include(mixin)); }
$Names:("../Images/One.png","../Images/Two.png","../Images/Three.png","../Images/Four.png","../Images/Five.png",)
#include mixin($Names){
#each $liindex in $Names{
$i: index($Names, $liindex );
if(/* see if this li has class Selected */){
/* change property here?
}
}
}
This should be doable with nth-of-type. Something like this:
#mixin bg-mixin($Names){
#each $bg-url in $Names{
$i: index($Names, $bg-url );
&:nth-of-type(#{$i}).Selected { background: url($bg-url); }
}
}
and used like this:
ul li {
#include bg-mixin($Names);
}
resulting in
ul li:nth-of-type(1).Selected {
background: url("../Images/One.png");
}
ul li:nth-of-type(2).Selected {
background: url("../Images/Two.png");
}
ul li:nth-of-type(3).Selected {
background: url("../Images/Three.png");
}
ul li:nth-of-type(4).Selected {
background: url("../Images/Four.png");
}
ul li:nth-of-type(5).Selected {
background: url("../Images/Five.png");
}
Related
Lets say in my global.css file of a Next.js project I have:
.flex {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
I also have a Layout.js component and a Layout.module.css file. The component looks like this:
import styles from "../styles/Layout.module.css";
const Layout = ({ children }) => {
return (
<div>
<div className={styles.navbar}>
<div className="flex">
<h1>Structured Safety</h1>
<nav>
<ul>
<li> Home </li>
<li> Demo </li>
</ul>
</nav>
</div>
</div>
</div>
);
};
export default Layout;
and the Layout.module.css is:
/* Navbar */
.navbar {
background-color: var(--primary-color);
color: #fff;
height: 70px;
}
.navbar ul {
display: flex;
}
.navbar .flex {
justify-content: space-between;
}
Structured like this, my .navbar .flex does not overwrite the global .flex class and split the h1 from the nav. How can I accomplish overwriting my global style from this component style?
Since .flex refers to a global class you'll need to use the :global selector to target it in your CSS module.
/* Layout.module.css */
.navbar :global(.flex) {
justify-content: space-between;
}
Or using an alternate syntax.
.navbar {
:global {
.flex {
justify-content: space-between;
}
}
}
/** CSS MODULE FILE **/
.classname :global(.globalClass) { css properties }
.classname {
:global {
.globalClass { css properties }
}
}
In NextJS and React when you
import styles from "__.css" the styles becomes a variable so you have to use it in your HTML for it to take effect.
Currently you're not using any styles from your Layout.module.css, if you want to use that css you would change your html to: <div className={styles.navbar}> and such..
I have these lines as part of my navigation sass, simplified here:
// These lines (66-72) should generate what I want.
.top-nav .nav {
& > ul {
a {
#extend %section-button;
}
}
}
// These lines (76-82) prevent the previous code from generating what I want.
// Delete it to see things work.
.nav {
& > ul {
a {
#extend %section-button;
}
}
}
When lines 76-82 are deleted, lines 66-72 produce .top-nav .nav > ul a:hover, applying the dark grey color to the text on hover. However, when lines 76-82 are allowed to remain, the .top-nav .nav > ul a:hover selector is not generated. Why is this?
Changing the order of the selector blocks doesn't help.
I need both blocks because in the .nav selector block I override things in %section-button that I don't want to be overridden in .top-nav .nav.
Here's the code in a pen, simplified as much as possible while still demonstrating the problem.
Full code:
HTML:
<div class="top-nav">
<nav id="nav" class="nav">
<ul>
<li class="page-link"> Live</li>
</ul>
</nav>
</div>
SCSS:
// Colors
$grey-color-dark: #3f3f3d;
$grey-color-light: lighten($grey-color-dark, 65%);
$blue: #23527c;
$green: #2d5a3a;
// Section Colors
%section-background-color {
background: $grey-color-dark;
}
%section-text-color {
color: $grey-color-dark;
}
// Buttons
%button {
cursor:pointer;
padding:0 1rem;
line-height: 2rem;
height: 2rem;
display:inline-block;
}
%section-button {
#extend %button;
#extend %section-background-color;
color:$grey-color-light;
&:hover, &:focus {
background: $grey-color-light;
#extend %section-text-color;
}
}
// navigation
// These lines (66-72) should generate what I want.
.top-nav .nav {
& > ul {
a {
#extend %section-button;
}
}
}
// These lines (76-82) prevent the previous code from generating what I want. Delete it to see things work.
.nav {
& > ul {
a {
#extend %section-button;
}
}
}
You're using #extends when you would want to use a mixin. #extends is designed to create groups of classes (and it will compile into one instance), so calling it on both .top-nav .nav and .nav will create a CSS grouping combining both classes and leading to other unwieldiness. Whereas using a mixin will define two different instances of the section-button in the nav and top-nav.
If you change %section-button to #mixin section-button and the #extend %section-button calls to #include section-button, everything should work.
If you are set on using placeholders, you'll need to create several placeholders. But I think that kind of defeats the point of using a placeholder. Or if you're not worried about duplicating code, you could just define the CSS you want inside the .top-nav class with a nested :hover, &:focus pseudoselector then whatever color you want.
I've rewritten the question to make this easier to understand.
I am using a class in my theme to control accent styles. I have a multisite using one theme so the theme is being style using my theme classes...
.theme-portal
.theme-motogp
.theme-mxgp
.theme-rally
But please note, that my theme styles change within themes.
For example, my theme class is applied to the <BODY> tag on every single page.
So like <BODY class="theme-portal"> or <BODY class="theme-motogp"> etc. But even though the body has this class <BODY class="theme-portal">, further down the same page the mxgp class could be applied to a table or an article, for example <TABLE class="theme-portal"> or <ARTICLE class="theme-motogp">
Now I am trying to be clever and save code, I am this mixin below and control access class stylings...
#mixin theme-change($theme) {
.theme-#{$theme} & {
#content;
}
}
And I use like this...
.btn {
text-transform: uppercase;
font-size: 16px;
border: none;
&.btn-accent {
color: #ffffff;
/* portal site */
#include theme-change('portal') {
background-color: $color-portal-red;
}
/* motogp site */
#include theme-change('motogp') {
background-color: $color-motogp-orange;
}
/* mxgp site */
#include theme-change('mxgp') {
background-color: $color-mxgp-red;
}
/* rally site */
#include theme-change('rally') {
background-color: $color-rally-red;
}
}
}
the above sass block outputs...
.theme-portal .btn.btn-accent { ... }
.theme-motogp .btn.btn-accent { ... }
.theme-mxgp .btn.btn-accent { ... }
.theme-rally .btn.btn-accent { ... }
and another nice example of my mixin working to control my theme accents
.header-bar-secondary {
UL.nav {
> LI {
> A {
/* portal site */
#include theme-change('portal') {
color: #000000;
&:hover {
background-color: #ffffff;
text-decoration: none;
}
}
/* motogp site */
#include theme-change('motogp') {
...
}
/* mxgp site */
#include theme-change('mxgp') {
...
}
/* rally site */
#include theme-change('rally') {
...
}
}
}
}
/* portal site */
#include theme-change('portal') {
border-color: #e0e0e0;
#include background-image(linear-gradient(#ffffff, #f2f2f2));
...
}
/* motogp site */
#include theme-change('motogp') {
...
}
/* mxgp site */
#include theme-change('mxgp') {
...
}
/* rally site */
#include theme-change('rally') {
...
}
}
the above sass block outputs...
.theme-portal .header-bar-secondary UL.nav > LI > A { ... }
.theme-portal .header-bar-secondary UL.nav > LI > A:hover { ... }
.theme-motogp .header-bar-secondary UL.nav > LI > A { ... }
.theme-motogp .header-bar-secondary UL.nav > LI > A:hover { ... }
.theme-mxgp .header-bar-secondary UL.nav > LI > A { ... }
.theme-mxgp .header-bar-secondary UL.nav > LI > A:hover { ... }
.theme-rally .header-bar-secondary UL.nav > LI > A { ... }
.theme-rally .header-bar-secondary UL.nav > LI > A:hover { ... }
.theme-portal .header-bar-secondary { ... }
.theme-motogp .header-bar-secondary { ... }
.theme-mxgp .header-bar-secondary { ... }
.theme-rally .header-bar-secondary { ... }
OK here is the problem. Using the mixing below in this block. As I described in my previous question I am trying to move the theme class to just before the element name. But my current mixin is moving the class right to the beginning of the sass block.
See sass example below where mixin fails.
#primary_carousel {
.item {
.inner {
}
}
.overlay {
H1 {
/* portal site */
#include theme-change('portal') {
...
}
/* motogp site */
#include theme-change('motogp') {
...
}
/* mxgp site */
#include theme-change('mxgp') {
...
}
/* rally site */
#include theme-change('rally') {
...
}
}
}
}
The reason it is failing is because my theme class is applied to the <BODY class="theme-portal"> like this. But the theme class is also applied to <DIV class="item"> which is a child of <BODY>
See below HTML structure...
<BODY class="theme-portal">
<DIV id="primary_carousel">
<DIV class="item theme-motogp">
<DIV class="inner">
<DIV class="overlay">
See below the above failing SASS block output...
.theme-portal #primary_carousel .overlay { ... }
.theme-motogp #primary_carousel .overlay { ... }
.theme-mxgp #primary_carousel .overlay { ... }
.theme-rally #primary_carousel .overlay { ... }
But because the my body tag has the <BODY class="theme-portal"> applied to it, it overrides my theme class which is applied to the <DIV class="item">
So I need my sass to output like this somehow using a simple mixin or clever new & operator...
#primary_carousel .theme-portal .overlay { ... }
#primary_carousel .theme-motogp .overlay { ... }
#primary_carousel .theme-mxgp .overlay { ... }
#primary_carousel .theme-rally .overlay { ... }
If you can help left me know a operator that moves the class to just before the selector, rather than the beginning of the sass block that would be awesome.
Thanks
#primary_carousel {
.item {
H1 {
}
& .theme-one H1{
background: red;
}
& .theme-two H1{
background: blue;
}
}
}
The ampersand in SASS/SCSS takes the place of all the parent selectors that it is nested in, not just the last one. In this case that is #primary_carousel .item where as in the original code provided it is nested inside the H1 so it is #primary_carousel .item H1. You can't put the theme classes inbetween the other classes using the ampersand, so you have to take it out of the H1 and then put your class and then the H1 after your class that is changing.
EDIT: Looking over the code again, I've actually just noticed that you don't even need the ampersands in this case, but I've left them in since the question is about them.
CSS OUTPUT:
#primary_carousel .item .theme-one H1 {
background: red;
}
#primary_carousel .item .theme-two H1 {
background: blue;
}
I'd like to make a rounded button group in a button bar in Foundation using their custom SCSS mixins.
So my mark up looks like this:
<ul class="contextualButtons">
<li><a class="buttonUp">Up a Level</a></li>
<li><a class="buttonEdit">Edit</a></li>
<li><a class="buttonDelete">Delete File</a></li>
<li><a class="buttonAddProtocol">Add Protocol</a></li>
<li><a class="buttonAddFolder">Add Folder</a></li>
<li><a class="buttonAddFile" data-dropdown="dropFile" >Add a File</a></li>
<ul id="dropFile" data-dropdown-content class="f-dropdown">
<li><a href="#"class="add_content" id="add_idcrp_file">Add File</a</li>
<li>Add Modifiable File</li>
</ul>
<li><a class="buttonPermissions">Permissions</a></li>
<li><a class="buttonSearch">Search</a></li>
</ul>
And my scss looks like this: (assume I can load foundation elements correctly)
.contextualButtons {
#include button-group-container();
.buttonUp,
.buttonEdit,
.buttonDelete,
.buttonAddProtocol,
.buttonAddFolder,
.buttonAddFile,
.buttonPermissions,
.buttonSearch {
#include button();
}
& > li {
#include button-group-style($button-round, 8, left);
}
}
It gets pretty close to the example, but I'm not getting any border between the buttons. If I add .button to each, it works. I'd rather just have one class on each element, keeping things nice and semantic an all.
The individual buttons are going to have icons, so they need to be differentiated from their peers.
Thanks.
Update
Digging deeper into what compiles, my custom button group class .contextualButtons is not replacing the default .button with my custom button classes.
.contextualButtons > * > .button {
border-right: 1px solid;
border-color: rgba (255, 255, 255, 0.5);
}
.contextualButtons > li:last-child button, .contextualButtons > li:last-child .button {
border-right: 0;
}
.contextualButtons > li:first-child {
margin-left: 0;
}
Looks like I would be better off extending .button rather than using the button mixin. This isn't what the documentation would seem to indicate, but I understand now thats not how the mixins are written. Might be better to combine button-group container and button-group button class into a single mixin into which two class names (or strings) are defined.
Looks ok-ish.
.contextualButtons {
#extend .button-group;
.buttonUp,
.buttonEdit,
.buttonDelete,
.buttonAddProtocol,
.buttonAddFolder,
.buttonAddFile,
.buttonPermissions,
.buttonSearch {
#extend .button;
padding-left: 0.1em;
padding-right: 0.1em;
}
& > li {
#include button-group-style($button-round, 8, left);
margin: .2em;
& > a {
word-wrap: break-word;
}
}
}
I have set up a mixin for a button using display:inline-block. I am trying to get to the parent of whatever class that will eventually end up using the mixim, so I can add the font-size: 0px line there to make sure that I don't need to make adjustments to my HTML to avoid unwanted space between each button.
Here's an example... I want the. parent class to receive the font-size: 0px line.
#mixin button() {
display:inline-block;
font-size: 1em;
//other stuff to make a pretty button
&& { font-size: 0px; }
}
.parent{
.child {
#include button();
}
}
As of Sass 3.4 this is now possible.
#mixin parent {
#each $selector in & {
$l: length($selector);
#if ($l == 1) {
#error "Used parent mixin on a top-level selector";
} #else {
$parent: nth($selector,1);
#for $i from 2 to $l {
$parent: append($parent,nth($selector,$i));
}
#at-root #{$parent} {
#content;
}
}
}
}
// Use
.grandparent {
.parent{
.child {
font-size: 1em;
#include parent {
font-size: 0px;
}
}
}
}
// Result
.grandparent .parent .child {
font-size: 1em;
}
.grandparent .parent {
font-size: 0px;
}
// Errors:
.root {
#include parent {
content: "Won't work";
}
}
.grandparent .parent, .root {
#include parent {
content: "Also won't work";
}
}
No, this is not possible. You could do something like this, though:
#mixin button($child: '.child') {
font-size: 0px;
//other stuff to make a pretty button
#{$child} {
display:inline-block;
font-size: 1em;
}
}
.parent{
#include button();
}
Output:
.parent {
font-size: 0px;
}
.parent .child {
display: inline-block;
font-size: 1em;
}
There is a XXX! selector in the draft for the CSS 4 spec, which will act as the way you like. It announces the subject of the CSS style declarations, if the selectors match
So if you have this selector
.a > .b! > .c
It will match e.g. for this
<div class="a">
<div class="b">
<div class="c">
</div>
</div>
</div>
but the style declarations will not take effect on .c, but on .b, because I announced by the exclamation mark, that this element should be the subject of the style
http://dev.w3.org/csswg/selectors4/#subject
You cannot use it right now out of the box. But there is one jQuery plugin, that is a polyfill for that. http://dev.w3.org/csswg/selectors4/
See also this stack: Is there a CSS parent selector?
How to apply?
Well, I don't know exactly in SASS, but in LESS it would be
*! > & {
/* ... */
}
While Karol's answer is near perfect, it doesn't take into account pseudo-elements or pseudo-selectors. Furthermore, code is duplicated if using more than one complex selector. I came up with a simplified version:
#mixin parent {
$parents: ();
$parent: '';
#each $selector in & {
$length: length($selector);
$index: 0;
$last-selector: nth($selector, $length);
#if ($length == 1) {
#error "Used parent mixin on a top-level selector";
} #else {
$index: str-index($last-selector, '::');
#if ($index) {
$last-selector: str-slice($last-selector, 1, $index - 1);
} #else {
$last-selector: null;
}
// Inspect allows us to combine two selectors in one block.
$parent: inspect(set-nth($selector, $length, #{$last-selector}));
$parents: join($parents, $parent, comma);
}
}
#at-root #{$parents} {
#content;
}
}
There's a first loop to iterate over the selector list (selectors with commas at the end). Because complex selectors are also treated as a list, we just need to remove the last element of the list. There's no loop to iterate over the compound or simple selectors since we only need to discard the last one.
There's no function in Sass to remove an element of a list, but we can set the value of an element with set-nth. By making the last element as an empty string and unquoting it, we can remove the last element from the printed representation (string) of the list. Since selectors can be strings, we simply use the new string as a selector.
When using the following:
.grandmother,
.grandfather {
.parent {
.child {
font-size: 10em;
#include parent {
font-size: 5em;
}
&::after {
font-size: 1px;
#include parent {
font-weight: bold;
}
}
}
}
}
We get the following:
.grandmother .parent .child,
.grandfather .parent .child {
font-size: 10em;
}
.grandmother .parent,
.grandfather .parent {
font-size: 5em;
}
.grandmother .parent .child::after,
.grandfather .parent .child::after {
font-size: 1px;
}
.grandmother .parent .child,
.grandfather .parent .child {
font-weight: bold;
}
Note: pseudo-elements and pseudo-selectors are not children of an element but are attached to it and have therefore no parents in themselves. I assumed parents would mean the parent in the sense of Sass nesting.