Specifying `@media` at rules and pseudo selectors `:last-child` together
Describe the issue
So I have the following styles specified:
const styles = stylex.create({
container: {
marginBottom: {
default: '0.5rem',
'@media screen and (min-width: 800px)': '0.75rem',
':last-child': 0
}
}
})
Expected behavior
I want my margin-bottom to always be 0 when as a last child element, otherwise 0.5rem and 0.5rem on larger screens. However ':last-child' never works on larger screen. I'd like to know the proper way to do so.
Steps to reproduce
import * as stylex from '@stylexjs/stylex';
const styles = stylex.create({
container: {
marginBottom: {
default: '0.5rem',
'@media screen and (min-width: 800px)': '0.75rem',
':last-child': 0
}
}
});
<div>
<div {...stylex.props(styles.container)}>
Hello!
</div>
<div {...stylex.props(styles.container)}>
Hello!
</div>
<div {...stylex.props(styles.container)}>
Hello!
</div>
</div>
Test case
No response
Additional comments
No response
Well, the right way to do this:
const styles = stylex.create({
container: {
marginBottom: {
default: '0.5rem',
':last-child': 0,
'@media screen and (min-width: 800px)': {
default: '0.75rem',
':last-child': 0,
},
}
}
});
But this is not so obvious, and there are way too many nested things here.
I am using stylexswc-nextjs-plugin and this has been confirmed in my environment.
.marginBottom-xhbfen4:not(#\#):not(#\#):not(#\#){margin-bottom:.5rem}
.marginBottom-xzboxd6:last-child:not(#\#):not(#\#):not(#\#){margin-bottom:0}
@media screen and (min-width: 800px){.marginBottom-x1x6kbue.marginBottom-x1x6kbue:not(#\#):not(#\#):not(#\#){margin-bottom:.75rem}}
media query: .marginBottom-x1x6kbue.marginBottom-x1x6kbue
You can see that the specificity of the media query class has been increased by one. I don't know if this is intentional, but
We double class within media queries for reliable style composition, so the specificity bump is expected. Generally we advise for you to make changes within JS to style conditionally instead of using :last-child altogether
You can nest differently to reduce the duplication:
const styles = stylex.create({
container: {
marginBottom: {
default: {
default: '0.5rem',
'@media screen and (min-width: 800px)': '0.75rem',
},
':last-child': 0,
}
}
});
This should do exactly what you intended without the duplication needed in the solution @SukkaW gave (which is also valid FWIW).
I also want to re-emphasize what @mellyeliu already said:
Generally we advise for you to make changes within JS to style conditionally instead of using
:last-childaltogether
One of the goals of StyleX is to use atomic CSS to minimize the total amount of CSS. It's preferable to avoid pseudo elements and pseudo-classes like :last-child as much as possible.
I also want to re-emphasize what @mellyeliu already said:
Generally we advise for you to make changes within JS to style conditionally instead of using
:last-childaltogetherOne of the goals of StyleX is to use atomic CSS to minimize the total amount of CSS. It's preferable to avoid pseudo elements and pseudo-classes like
:last-childas much as possible.
@mellyeliu @nmn
And that means I will have to write more obscure and potentially inefficient code:
const styles = stylex.create({
item: {
marginBottom: {
default: {
default: '0.5rem',
'@media screen and (min-width: 800px)': '0.75rem',
},
- ':last-child': 0,
}
},
+ item_last_child: {
+ marginBottom: 0
+ }
});
- array.map(item => <div {...stylex.props(styles.item)} />)
+ array.map((item, index, arr) => <div {...stylex.props(styles.item, index === arr.length - 1 && styles.item_last_child)} />)
And the stylex compiler can't optimize this; If I use :last-child, it is all CSS, and the stylex compiler can strip/eliminate all runtime codes and calls.