Skip to content

kfdf/tailwind-modifier-aliasing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

tailwind-modifier-aliasing

A cli tool for TailwindCSS that allows you to apply one stack of modifiers to several utility classes. It's a postprocessor, so you write normal and valid tailwind classes, intellisense works as intended, warnings are helpful and the type of the frontend framework is irrelevant.

Installation and usage

npm install tailwind-modifier-aliasing

tailwind-ma input.css output.css [--watch]

To run in parallel with tailwind in watch mode in bash:

tailwind -i input.css -o output.css -w & tailwind-ma output.css final.css -w

How it works

First, some of the tailwind classes have to be written in a particular way. Then, this cli tool takes the tailwind generated and unminified output file and rewrites some of the rules to take advantage of native css nesting.

A utility class that defines a property named alias can have its modifiers applied to utility classes that prepend their selectors with the the value of this property. Such classes can be said to depend on the presence of the aliased modifier class that they reference. Take this markup:

<div class="hover:md:[alias:hm] [hm&]:bg-red-500">
  Red on hover when wider than 768px
</div>

Here hm is an alias for hover:md, and [hm&] is a reference that can be used to apply the modifiers to bg-red-500. Given this markup tailwind generates these two rules:

@media (min-width: 768px) {
  .hover\:md\:\[alias\:hm\]:hover {
    alias: hm;
  }
}

hm.\[hm\&\]\:bg-red-500 {
  //...
}

which are then rewritten by this tool into this:

@media (min-width: 768px) {
  :is(.hover\:md\:\[alias\:hm\]:hover) {
    &.\[hm\&\]\:bg-red-500 {
      //...
    }
  }
}

which has the same effect as hover:md:bg-red-500.

An element can have several aliased classes, each must have a different alias to avoid conflicts. Also, dependent classes can have additional modifiers of their own.

<div class="hover:peer-[:not(:placeholder-shown)]:[alias:a] [a&]:bg-blue-300 [a&]:text-sm [a&]:font-bold group-focus/item:last-of-type:[alias:b] [b&]:bg-red-500 [b&]:hover:text-lg [b&]:hover:italic">

The syntax is quite verbose and only suitable for taming some of the more egregious modifiers, but with native support it could look like this hover:md::h h:bg-red-500 h:text-white h:font-bold which is almost as concise as other grouping proposals.

Deduplication

Aliases are global. This tool doesn't examine source files and has no idea which classes actually depend on which. So if the same alias is used by different modifier classes that are dependent on by many other utilities then all their permutations must be supported. Given this markup:

<div class="hover:[alias:a] [a&]:bg-red-500">
  Red
</div>
<div class="focus:[alias:a] [a&]:bg-blue-500" tabindex="0">
  Blue
</div>

tailwind generates this css:

.hover\:\[alias\:a\]:hover {
  alias: a;
}
.focus\:\[alias\:a\]:focus {
  alias: a;
}
// ...two more rules for red and blue

and now both bg-red-500 and bg-blue-500 rules have to be copied into each of those aliased rules. This tool tries to tackle the problem by deduplicating aliased rules, so the final css is actually this:

:is(.focus\:\[alias\:a\]:focus,
.hover\:\[alias\:a\]:hover) {
  &.\[a\&\]\:bg-blue-500 {
    //...
  }
  &.\[a\&\]\:bg-red-500 {
    //...
  }
}

Wrapping the selector list in the :is pseudo-class function ensures that one unsupported selector doesn't invalidate the entire list.

Deduping is only possible for aliased rules that belong to the same at-rule. For example, given these classes: hover:md:[alias:a], hover:sm:[alias:a], hover:md:dark:[alias:a], hover:[alias:a] tailwind generates this css:

.hover\:\[alias\:a\]:hover {
  alias: a;
}
@media (min-width: 640px) {
  .hover\:sm\:\[alias\:a\]:hover {
    alias: a;
  }
}
@media (min-width: 768px) {
  .hover\:md\:\[alias\:a\]:hover {
    alias: a;
  }
  @media (prefers-color-scheme: dark) {
    .hover\:md\:dark\:\[alias\:a\]:hover {
      alias: a;
    }
  }
}

None of the above rules will be deduped. The tool reports the amount of dependent rules it initally finds and the amount of copies it makes to help see if this becomes a problem.

Considerations

Modifiers that target anything but their own element, like marker or *, can't be aliased. Modifiers that target pseudo-elements are moved to the dependent rule's selector, so this limitation doesn't apply to them. However, it does negatively affect deduping.

<div class="before:md:hover:[alias:bmh] [bmh&]:dark:peer-invalid:font-bold ..."></div>

The tailwind generated css for the above markup is:

@media (min-width: 768px) {
  .before\:md\:hover\:\[alias\:bmh\]:hover::before {
    content: var(--tw-content);
    alias: bmh;
  }
}

@media (prefers-color-scheme: dark) {
  bmh.peer:invalid ~ .\[bmh\&\]\:dark\:peer-invalid\:font-bold {
    font-weight: 700;
  }
}

Then, ::before is cut from the outer and appended to the inner selector. The content property is also moved:

@media (min-width: 768px) {
  :is(.before\:md\:hover\:\[alias\:bmh\]:hover) {
    @media (prefers-color-scheme: dark) {
      .peer:invalid ~ &.\[bmh\&\]\:dark\:peer-invalid\:font-bold::before {
        content: var(--tw-content);
        font-weight: 700;
      }
    }
  }
}

Modifiers that target children are allowed in dependent classes, but they make other modifiers apply to the target element and not to the children. For example, hover:[alias:a] [a&]:marker:text-sky-500 works the same as marker:hover:text-sky-500 and not as hover:marker:text-sky-500.

About

A tool for Tailwind CSS to enable modifier reuse

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published