skeleton icon indicating copy to clipboard operation
skeleton copied to clipboard

Proposal: Tailwind Merge pattern for native Tailwind v4

Open endigo9740 opened this issue 9 months ago • 5 comments

@Hugos68 came across this new technique that could potentially allow us to rethink the way you apply classes to components via what we currently call style props.

https://x.com/diegohaz/status/1897776774206853269

We'll review this as a potential component API change for the next major release of Skeleton (v4).

What This Does

Currently we have the concept of style props in separate "channels" like: base | {property} | classes, but this would allow us to streamline this down to ONLY classes because base and property classes could be applied inline in the HTML template, but overwritten with classes that always take precedence without the need of third-party tools like Tailwind Merge or using !.

<div class="base:rounded-full base:bg-red-500 {classes}">...</div>

There's several clear benefits here:

  • No need to abstract Tailwind classes to props - with a few potential exceptions (ex: state-based classes)
  • No need to generate multiple canned style props - every property can be passed and just works
  • Removed a Skeleton-specific concept you have to learn - just add classes and you're done

Real World Example

So here's a real world component example today:

<Avatar background="bg-green-500" classes="border border-white" />

Versus what it could be after the change:

<Avatar classes="bg-green-500 border border-white" />

What May Not Change

We would still need the prefix approach to target multiple elements within the component template. Like:

classes
imgClasses
fallbackClasses

We may also want to keep the term classes (plural) to avoid conflict with class. Though this can be discussed further.

Downsides / Pitfalls

Unfortunately this does have a slight cost to the final bundle size. Every time we use a native Tailwind utility like base:p-4, this does not resolve down to the same instance as p-4. So every base: class we add in our components would be a "dupe". But this is likely a worthwhile trade-off for the DX improvement added.

He's a trivial example using bg-red-500: https://play.tailwindcss.com/5DRJbdc3HW

Image

See Also:

  • https://github.com/skeletonlabs/skeleton/issues/3320

endigo9740 avatar Mar 08 '25 02:03 endigo9740

I think we should move away from classes if we were to use this because classes isn't picked up by Tailwind Intellissen, if we did class and <element>Class (or imo, just use components and always use class) we could instruct users to only add *Class to their tailwind extension config.

Hugos68 avatar Mar 08 '25 10:03 Hugos68

My biggest concern with that was that class typically requires some kind of work around because it's a reserved name. We would work around that for the root element, but the each child would implemented differently. Where as if we just keep them custom props it's consistent across the board, across all frameworks. But I do think folks would respond better to the singular version.

endigo9740 avatar Mar 08 '25 18:03 endigo9740

No workarounds needed:

<script>
	const props = $props();
</script>

<div class="base:bg-red-500 {props.class}">
	<div class={props.labelClass}>test</div>
</div>
export default function(props) {
  return (
    <div className='base:bg-red-500 {props.class}'>
      <div className={props.labelClass}>test</div>
    </div>
  );
}

This would work even better if we went with a component based approach (my favorite):

<script>
	const props = $props();
</script>

<div {...props} class="base:bg-red-500 {props.class}">
	{@render props.children?.()}
</div>
export default function(props) {
  return (
    <div {...props} className='base:bg-red-500 {props.class}'>
      {props.children}
    </div>
  );
}

Hugos68 avatar Mar 08 '25 18:03 Hugos68

That's 1/4 frameworks. Now we need React, Vue, and Solid too.

endigo9740 avatar Mar 08 '25 19:03 endigo9740

@endigo9740 I showed you react and solid already, look again. Vue is the same aswell. Nothing changes.

Hugos68 avatar Mar 09 '25 12:03 Hugos68

I'm going to go ahead and close this out, as we have a definitive answer to this in the upcoming Skeleton v4 release. I'd recommend checking out the updated Fundamentals docs and/or Contributor > Component docs for details when v4 launches for more information.

endigo9740 avatar Sep 23 '25 16:09 endigo9740