Family
A library of mixins that enable manipulation of a selector based on it’s relationship with parent or sibling selectors.
Install
npm install seed-family --save
About
Because of how CSS works, it’s (currently) not possible to create rules based on the target’s parent selectors or previous sibling classes in any way. This pack aims to help resolve these issues.
This use-case doesn’t occur very often, but it’s not too uncommon either.
Problem
Take the following CSS for a menu component (structured with the BEM convention).
_menu.scss
.menu {
&__item {
display: none;
}
}
Workarounds
The .menu__item
is hidden with display: none
by default. We want it to be display: block
when the .menu.is-active
.
We currently have to write it like this:
_menu.scss
.menu {
&__item {
display: none;
}
&.is-active {
.menu__item {
display: block;
}
}
}
The class of .menu__item
is repeated. Not to mention that .menu__item
no longer manages its own styles, but instead, they are abstracted and dicated by .menu
. What’s worse is that &
no longer references .menu
in the context of &.is-active
(but that’s a separate issue).
Ideal
Wouldn’t it be nice if we could write something like this?
_menu.scss
.menu {
&__item {
display: none;
:parent.is-active {
display: block;
}
}
}
When the parent, in this case .menu
, has the .is-active
class, change the display of .menu__item
.
Mixins
parent
parent($selector)
Argument | Type | Description |
---|---|---|
$selector |
String | The desired parent selector to target. |
We can write our menu styles similarly to our “ideal” example.
_menu.scss
.menu {
&__item {
display: none;
@include parent("#{&}.is-active") {
display: block;
}
}
}
menu.css
.menu__item {
display: none;
}
.menu.is-active .menu__item {
display: block;
}
The parent
mixin intelligently injects the provided $selector
directly before the current selector.
The magic happens with this line:
@include parent("{&}.is-active") {
Let’s break it down:
@include parent
- Activating theparent
mixin."{&}.is-active"
- The parent selector + the.is-active
class.
In this example, &
still references .menu
. That is because it’s direct parent selector is .menu
, unlike our workaround example.
.menu {
// & = .menu
}
.menu {
&.is-active {
// & = .menu.is-active
}
}
sibling
sibling($selector)
Argument | Type | Description |
---|---|---|
$selector |
String | The desired sibling selector to target. |
In CSS, we can target the sibling selector that comes after, but not before. The sibling
mixin allows you to reference the sibling selector the comes before your current selector.
_menu.scss
.menu {
&__item {
margin-top: 0;
@include sibling("#{&}__sub-menu") {
margin-top: 10px;
}
}
}
menu.css
.menu__item {
margin-top: 0;
}
.menu__sub-menu + .menu__item {
margin-top: 10px;
}