Nested `:has()` alternative

I'm hitting a bit of a roadblock. I'm building an obsidian theme, and I want to make a little bit of an out-of-spec "markdown feature". The problem I'm running into is conditionals with css selectors. I'm a huge fan of the :has pseudo-selector but it doesn't support nesting, which makes that I'm running into the following. Let: - An "empty line" is a line (.cm-line) with only one child of type br - An "overline" is a blockquote (.HyperMD-quote) surrounded by empty line siblings, followed by any heading (.HyperMD-header) - A "subtitle" is a blockquote (.HyperMD-quote) surrounding by empty line siblings, preceeded by any heading (.HyperMD-header) I want to select the empty line in between the headings and the subtitle. The problem lies in the nested conditions. -# Also see comments inside scss attachment
5 Replies
Mia
MiaOP3mo ago
so the blocker is the fact that I don't see a way to select that empty line with a sibling that has a sibling that is an empty child without using nested :has() i'm kind of worried it's just straight up not possible which would suck very much ok i found a solution but it's not pretty cuz it just doesn't work with the way sass expands placeholders inside of the :has() selector for whatever reason
.cm-line {
> br:only-child {
@extend %empty-line-br !optional;
}

&:has(> br:only-child) {
@extend %empty-line !optional;
}

&:where(.HyperMD-header) {
@extend %heading !optional;

+ :where(%empty-line):has(+ :where(%blockquote) + %empty-line-br) {
/* empty line preceeding a subtitle */
background-color: cyan;
}
}

&:where(.HyperMD-quote.HyperMD-quote-1) {
@extend %blockquote !optional;
}
}
.cm-line {
> br:only-child {
@extend %empty-line-br !optional;
}

&:has(> br:only-child) {
@extend %empty-line !optional;
}

&:where(.HyperMD-header) {
@extend %heading !optional;

+ :where(%empty-line):has(+ :where(%blockquote) + %empty-line-br) {
/* empty line preceeding a subtitle */
background-color: cyan;
}
}

&:where(.HyperMD-quote.HyperMD-quote-1) {
@extend %blockquote !optional;
}
}
compiles to
.cm-line:where(.HyperMD-header) + :where(.cm-line:has(> br:only-child)):has(+ .cm-line > :where(.cm-line:where(.HyperMD-quote.HyperMD-quote-1)) + br:only-child) {
/* empty line preceeding a subtitle */
background-color: cyan;
}
.cm-line:where(.HyperMD-header) + :where(.cm-line:has(> br:only-child)):has(+ .cm-line > :where(.cm-line:where(.HyperMD-quote.HyperMD-quote-1)) + br:only-child) {
/* empty line preceeding a subtitle */
background-color: cyan;
}
which...why does it split the %empty-line-br placeholder up to surround the %blockquote placeholder??? I don't really understand why it would expand what should clearly be
:has(+ :where(.cm-line:where(.HyperMD-quote.HyperMD-quote-1)) + .cm-line > br:only-child) {}
:has(+ :where(.cm-line:where(.HyperMD-quote.HyperMD-quote-1)) + .cm-line > br:only-child) {}
to
:has(+ .cm-line > :where(.cm-line:where(.HyperMD-quote.HyperMD-quote-1)) + br:only-child) {}
:has(+ .cm-line > :where(.cm-line:where(.HyperMD-quote.HyperMD-quote-1)) + br:only-child) {}
is it something to do with the fact that the placeholders are all extended as part of the same .cm-line rule? or is this straight up a sass bug?
Mia
MiaOP3mo ago
GitHub
@extend / placeholder doesn't follow sibling / child hierarchy insi...
I may be misunderstanding the @extend placeholder spec but this feels wrong on many levels. I dont know if this is intended behaviour but it is definitely unexpected behaviour to me. .cm-line { &gt...
Mia
MiaOP3mo ago
Ok I figured out that the order shuffling happens specifically when chaining sibling operators where a placeholder has a direct child operator. scss:
.parent > .child {
/* parent > child = %placeholder */
@extend %placeholder !optional;
}

.two:has(+ * + %placeholder) {
/* expected output: .two:has(+ * + .parent > .child) */
}
.parent > .child {
/* parent > child = %placeholder */
@extend %placeholder !optional;
}

.two:has(+ * + %placeholder) {
/* expected output: .two:has(+ * + .parent > .child) */
}
output:
.parent > .child {
/* parent > child = %placeholder */
}

.two:has(+ .parent > * + .child) {
/* expected output: .two:has(+ * + .parent > .child) */
}
.parent > .child {
/* parent > child = %placeholder */
}

.two:has(+ .parent > * + .child) {
/* expected output: .two:has(+ * + .parent > .child) */
}
https://github.com/sass/dart-sass/issues/2645#issuecomment-3264963222 The plot thickens figured out an alternative route to a solution
Mia
MiaOP3mo ago
Mia
MiaOP3mo ago
Solved it myself my issue reopened an old issue from 2018 so that's fun :D

Did you find this page helpful?