aphrodite
aphrodite copied to clipboard
how to write Nesting ?
.jump-btn {
width: 32px;
height: 32px;
background: url('jump_btn.png') no-repeat 0 0;
&.disabled {
background-position: -32px 0;
}
&.back {
background-position: -64px 0;
}
}
This is somewhat not supported. The only real nesting that is supported is pseudo selectors (:hover, :active, etc) and media queries. The way you would do it in Aphrodite would be to do something like
const styles = StyleSheet.create({
jumpBtn: {
width: 32,
height: 32,
background: 'background: url('jump_btn.png') no-repeat 0 0',
},
disabled: {
backgroundPosition: '-32px 0',
},
back: {
backgroundPosition: '-64px 0',
},
});
const classes = css(styles.jumpBtn, isDisabled && styles.disabled, isBack && styles.back);
There is an escape hatch by doing something like
const styles = StyleSheet.create({
jumpBtn: {
...styles,
':not(foobar).disabled': { ....disabledStyles }
},
});
const classes = 'disabled' + css(styles.jumpBtn)
I would try hard to not use this though.
thank you verymuch @dmiller9911
You can do that pretty easily by adding a custom selectorHandler with StyleSheet.extend
import { StyleSheet as Aphrodite } from 'aphrodite';
const { StyleSheet, css } = Aphrodite.extend([{
selectorHandler: (selector, baseSelector, generateSubtreeStyles) => {
if (selector[0] === '>') {
const tag = selector.slice(1);
const nestedTag = generateSubtreeStyles(`${baseSelector} ${tag}`);
return nestedTag;
}
return null;
}
}]);
Then just write your style as :
{
color: "red",
"> p": {
textTransform: 'uppercase',
fontSize: '0.8em',
textDecoration: 'none'
}
}
Even better, just wrap all your styles into a function like this :
import _ from 'lodash';
function mapNestedStyle(stylesheet) {
_.each(stylesheet, (item, selectorKey) => {
const selectors = item._definition;
_.each(selectors, (selector, key) => {
if (_.isPlainObject(selector) && key[0] !== '>' && key[0] !== ':' && key[0] !== '@') {
delete selectors[key];
selectors[`> ${key}`] = selector;
}
});
});
return stylesheet;
}
So that you can just write :
{
color: "red",
p: {
textTransform: 'uppercase',
fontSize: '0.8em',
textDecoration: 'none'
}
}
This can be automated using a React HOC.
I hope nesting will become part of the core functionalities at some point :)
@nakwa Perfect!
I do think selectors for ' ' (space - all descendants) and >(direct child descendants) should be included - it seems wrong to have to add styles to every component that needs styling. For example, every label in a form... I want to just put the styles on the form to apply to all the labels.
I modified as follows
const { StyleSheet, css } = Aphrodite.extend([{
selectorHandler: (selector, baseSelector, generateSubtreeStyles) => {
if (selector[0] === ' ') {
const tag = selector.slice(1);
const nestedTag = generateSubtreeStyles(`${baseSelector} ${tag}`);
return nestedTag;
} else if (selector[0] === '>') {
const tag = selector.slice(1);
const nestedTag = generateSubtreeStyles(`${baseSelector} > ${tag}`);
return nestedTag;
}
return null;
}
}]);
so now this works:
Form: {
borderWidth: 1,
borderStyle: "solid",
borderColor: "black",
margin: 5,
padding: 5,
borderRadius: 7,
" label" :{
backgroundColor: "pink",
}
}
and produces:
.Form_1pi38cr {
border-width: 1px !important;
border-style: solid !important;
border-color: rgb(66, 139, 202) !important;
margin: 5px !important;
padding: 5px !important;
border-radius: 7px !important;
}
.Form_1pi38cr label {
background-color: rgb(91, 192, 222) !important;
}
Would be great for it to be core functionality. Might look at the code base. I use typescript so had to modify the typings file slightly for linting to pass.
Wasn't so hard to implement in base library: https://github.com/Khan/aphrodite/compare/master...Sibz:nesting
I'll ponder over the code a bit longer and then I might make it a PR.
Yea, @Sibz , actually I think using a
(space) or >
ends up being quite unreadable.
I've updated my script to only enable nesting when the first char of the selector is &
, which I believe is the current proposal for nesting in CSS: https://tabatkins.github.io/specs/css-nesting/
Also, in order to support the ,
selector (applying style to multiple selectors), and ensure it is still within the aphrodite scope :
import _ from 'lodash';
import { StyleSheet as Aphrodite } from 'aphrodite/no-important';
const { StyleSheet, css } = Aphrodite.extend([{
selectorHandler: (selector, baseSelector, generateSubtreeStyles) => {
const nestedTags = [];
const selectors = selector.split(',');
_.each(selectors, (subselector, key) => {
if (selector[0] === '&') {
const tag = key === 0 ? subselector.slice(1) : subselector;
const nestedTag = generateSubtreeStyles(`${baseSelector} ${tag}`.replace(/ +(?= )/g, ''));
nestedTags.push(nestedTag);
}
});
return _.isEmpty(nestedTags) ? null : _.flattenDeep(nestedTags);
}
}]);
export { StyleSheet, css };
so that :
'& .container-fluid, .container-fluid > .row': {
height: '100%',
'& .woop': {
backgroundColor: 'red'
}
},
generates :
[
".component_s3hzln .container-fluid{height:100%;}",
".component_s3hzln .container-fluid .woop{background-color:red;}",
".component_s3hzln .container-fluid > .row{height:100%;}",
".component_s3hzln .container-fluid > .row .woop{background-color:red;}"
]
This is really neat. I think it would be awesome if we could support nesting like this. If someone has time to put together a PR for this it would be greatly appreciated.
I can, I used the actual CSS selectors [space] and > to support multi selection. It's been a long time since that project but it worked a dream!