RyanClementsHax / tailwindcss-themer

An unopinionated, scalable, tailwindcss theming solution

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Idea for `defaultTheme` variant to exclude other themes, and some other thoughts

MichaelAllenWarner opened this issue · comments

Something like addVariant('defaultTheme', '&:not(.other-theme-1 *, .other-theme-2 *)') might do the trick. Complex selectors in :not() have pretty good browser support at this point: https://caniuse.com/css-not-sel-list

In general, you might like to allow for a layer or two of "nesting" for the variants, so that, for example, the innermost div in the following would have a black background, regardless of the declaration-order in the CSS:

<div class="theme-3">
  <div class="theme-2">
    <div class="theme-2:bg-black theme-3:bg-white"></div>
  </div>
</div>

Again, you can use a complex :not() selector:

addVariant('theme-3', '.theme-3 &:not(.theme-3 .theme-1 *, .theme-3 .theme-2 *)')

To allow for more "nesting," maybe something like:

addVariant('theme-3', `
  .theme-3 &:not(.theme-3 .theme-1 *, .theme-3 .theme-2 *),
  .theme-3 .theme-1 .theme-3 &,
  .theme-3 .theme-2 .theme-3 &
`)

(assuming addVariant even supports multiple selectors; if not, maybe a PostCSS plugin could do this). For defaultTheme, I guess you'd want something like:

addVariant('defaultTheme', `
  &:not(.theme-1 *, .theme-2 *),
  .theme-1 .defaultTheme &,
  .theme-2 .defaultTheme &
`)

If the themes were indicated by attributes instead of classes, you might not have to list the other individual theme-names at all (though there's no "not-equals" attribute-selector, and you can't nest :not()s):

addVariant('theme-3', `
  [data-tw-theme="3"] &:not([data-tw-theme="3"] [data-tw-theme] *),
  [data-tw-theme="3"] [data-tw-theme="3"] &
`)

Another layer of nesting might be possible but I doubt it would be useful.

This project seems really promising to me. I've been thinking along similar lines recently (as you can maybe tell).

One more unrelated idea (a little out there, admittedly): what if you could also make theme-group variants, that apply selectors for several themes at once? For example, maybe your theme-2, theme-3, and theme-4 all generally use white for the text-color—what if you could make a themes-white-text variant for all of them at once? Then themes-white-text:text-decoration-white/50 could be shorthand for theme-2:text-decoration-white/50 theme-3:text-decoration-white/50 theme-4:text-decoration-white/50. Heck, maybe it would even be possible to use matchUtilities to allow for some JIT magic there, like themes-[2,3,4|text-decoration-color-white/50] (inspiration: https://play.tailwindcss.com/SLtXEpJPbJ).

Hi! 👋

Thanks for opening an issue about those things. I like the direction of all the ideas.

'&:not(.other-theme-1 *, .other-theme-2 *)'

I created a branch that demonstrates what we are talking about named defaultTheme-variant-css-selector. The commit representing this branch at the time of writing this is 242f358246867bfc5ecda64616118ff8f78919b1.

Thanks for contributing a selector as I'm having such a hard time at creating a robust one. I think yours is close, but when testing this, I couldn't get it to work. Perhaps you could create a proof of concept here?

When massaging it to get something that sort of works, I came up with :not(.other-theme-1, .other-theme-2) & but this requires that the class of the theme you are enabling (e.g. other-theme-1 or other-theme-2) must exist on every parent of the element you are selecting

<!-- doesnt work -->
<div class="other-theme-1">
  <header> <!-- this triggers :not(.other-theme-1, .other-theme-2) -->
    <h1 class="defaultTheme:underline">
      Hello world!
    </h1>
  </header>
</div>
<!-- this works -->
<div class="other-theme-1">
  <header class="other-theme-1">
    <h1 class="defaultTheme:underline">
      Hello world!
    </h1>
  </header>
</div>

Do you have any suggestions on how to make this work? A POC would be greatly helpful.

Nesting

That's a good point. I do think nesting could be worked out a little thoroughly. If you want to create a separate issue detailing exactly what you want, feel free. (I would create it but I want to make sure the issue is written in your words so things don't get lost in translation). I will intentionally delay implementing any solution just yet so this plugin can get more adoption and whatever solution we go with can be use case driven.

Also good point on the attributes. That is something we would need to be careful about when adding additional ways to activate a theme.

theme groups

That certainly is a fun idea! Like my point in "Nesting", feel free to create a separate issue. I will delay implementing a solution for the same reason.

(I think I addressed all of your points?)

Hi @RyanClementsHax,

Thanks for the reply. Don't have a lot of time right now, but here is an attribute-driven approach that I've been fiddling with: https://play.tailwindcss.com/9ygk2ozUjG

This updated version includes a JIT matchUtilities thing that seems to be working (for applying utilities to multiple themes using a "shorthand"): https://play.tailwindcss.com/4cchHMtasV

@RyanClementsHax

I think you and I might have meant different things by "exclude other themes": by itself, addVariant('theme-default', '&:not(.theme-other-1 *, .theme-other-2 *)') would just allow the theme-default: variant to be used anywhere except inside another theme's scope.

If by "exclude other themes" you meant "disable other theme-variants within this theme's scope," then that logic needs to be handled in the selectors for those other theme-variants, just as the theme-default logic above disables the theme-default: variant when an ancestor-element has established another theme's scope. For non-default themes, you'd be looking at something like:

addVariant('theme-other-1', '.theme-other-1 &:not(.theme-default *, .theme-other-2 *)')

Here is a working demo with these selectors (class-based, and simpler than what I shared earlier): https://play.tailwindcss.com/OUCAeFK8Wy

(Theme-nesting requires more complex selectors.)

Excellent work on creating that demo! I think that will be a useful reference when considering adding better support for nesting.

I think there was a bit of confusion between us. When talking about a defaultTheme variant. My goal is to only enable the class it is attached to if not in the scope of any other theme, not disabling any other theme (i.e. the class defaultTheme is attached to is disabled when in the scope of any active theme).

Thus, I'd like to leave other classes unaffected.

(Hope this helps)

This issue has been automatically marked stale because it it received no activity for 60 days. If you wish to keep this open, please leave a comment. Thanks.

This issue has been automatically closed because it received no activity for 60 days. If you think this was closed by accident, please leave a comment. Thanks.