Summary
lexxy-content.css wraps the outer container in :where(.lexxy-content), which suggests styles are intentionally low-specificity and easy to override. But CSS nesting only zeroes out the outer wrapper – nested selectors keep their full specificity, so any rule that mentions a class still resolves at (0,1,1) or higher. The result is that simple consumer selectors like article img { ... } lose to lexxy's defaults.
Repro
Consumer custom CSS:
article img {
border-radius: 0;
}
This has no effect on images inside posts, because of the Lexxy rule:
:where(.lexxy-content) {
.attachment--preview {
img, video {
border-radius: var(--lexxy-radius);
}
}
}
This Lexxy rule compiles to :where(.lexxy-content) .attachment--preview img with specificity (0,1,1), which beats article img (0,0,2).
The same problem affects every nested .attachment*, code, pre, etc. – anything where consumers might reasonably want to write a low-specificity override.
The outer :where(.lexxy-content) wrapping reads as a deliberate signal: "we don't want our specificity to fight you." In practice, the moment any nested selector mentions a class, that signal evaporates. Consumers writing simple element selectors against article (the natural way to scope blog post styles) will silently lose every battle without realising why.
Suggested fixes (either would work)
Option 1. Wrap nested selectors in :where() too
Inside :where(.lexxy-content), change patterns like:
.attachment--preview {
img, video { ... }
}
to:
:where(.attachment--preview) {
:where(img, video) { ... }
}
This pushes practical specificity down to the bare element selectors, so article img wins.
Option 2. Put content styles in @layer (preferred)
Wrap the content stylesheet in a layer:
@layer lexxy {
/* contents of lexxy-content.css */
}
Unlayered styles always win over layered ones, regardless of specificity, so consumer CSS overrides "just work" without any specificity gymnastics.
Pro: modern, idiomatic for CSS libraries (Tailwind, Open Props, Pico, etc. all do this), self-documenting, future-proof.
Con: shifts the library's cascade model - anyone currently relying on lexxy's specificity to override their own unlayered styles would be affected.
Option 2 is what :where() seems to imply but doesn't actually deliver for nested rules. Worth considering an opt-in flag (e.g. @layer lexxy.content { ... }) if you want to introduce it without breaking existing consumers.
Thoughts?
Summary
lexxy-content.csswraps the outer container in:where(.lexxy-content), which suggests styles are intentionally low-specificity and easy to override. But CSS nesting only zeroes out the outer wrapper – nested selectors keep their full specificity, so any rule that mentions a class still resolves at (0,1,1) or higher. The result is that simple consumer selectors likearticle img { ... }lose to lexxy's defaults.Repro
Consumer custom CSS:
This has no effect on images inside posts, because of the Lexxy rule:
This Lexxy rule compiles to
:where(.lexxy-content) .attachment--preview imgwith specificity (0,1,1), which beatsarticle img(0,0,2).The same problem affects every nested
.attachment*,code,pre, etc. – anything where consumers might reasonably want to write a low-specificity override.The outer
:where(.lexxy-content)wrapping reads as a deliberate signal: "we don't want our specificity to fight you." In practice, the moment any nested selector mentions a class, that signal evaporates. Consumers writing simple element selectors against article (the natural way to scope blog post styles) will silently lose every battle without realising why.Suggested fixes (either would work)
Option 1. Wrap nested selectors in
:where()tooInside
:where(.lexxy-content), change patterns like:to:
This pushes practical specificity down to the bare element selectors, so
article imgwins.Option 2. Put content styles in
@layer(preferred)Wrap the content stylesheet in a layer:
Unlayered styles always win over layered ones, regardless of specificity, so consumer CSS overrides "just work" without any specificity gymnastics.
Pro: modern, idiomatic for CSS libraries (Tailwind, Open Props, Pico, etc. all do this), self-documenting, future-proof.
Con: shifts the library's cascade model - anyone currently relying on lexxy's specificity to override their own unlayered styles would be affected.
Option 2 is what
:where()seems to imply but doesn't actually deliver for nested rules. Worth considering an opt-in flag (e.g.@layer lexxy.content { ... }) if you want to introduce it without breaking existing consumers.Thoughts?