tailwind-csstailwind-css-4

Readable cross-browser (webkit, moz) input slider styling using custom-variants


In order to use Tailwind CSS for styling the input slider's track and thumb, I have to rely on two different pseudo elements (WebKit and Mozilla), which results in code duplication.

However, since these selectors are not standardized, it may be necessary to apply different styles for each system separately.

It is possible to implement this with arbitrary values, but the code becomes very long and unreadable.

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<input
  type="range"
  class="
    m-5 appearance-none w-[20em] h-[2em] outline-none overflow-hidden rounded
    
    /* Track for webkit */
    [&::-webkit-slider-runnable-track]:bg-slate-300
    [&::-webkit-slider-runnable-track]:h-full
    [&::-webkit-slider-runnable-track]:rounded
    
    /* Track for moz */
    [&::-moz-range-track]:bg-slate-300
    [&::-moz-range-track]:h-full
    [&::-moz-range-track]:rounded
    
    /* Thumb for webkit */
    [&::-webkit-slider-thumb]:h-full
    [&::-webkit-slider-thumb]:w-[8px]
    [&::-webkit-slider-thumb]:bg-blue-500
    [&::-webkit-slider-thumb]:[box-shadow:_-20em_0_0_20em_black]
    /* Custom for webkit */
    [&::-webkit-slider-thumb]:appearance-none
    
    /* Thumb for moz */
    [&::-moz-range-thumb]:h-full
    [&::-moz-range-thumb]:w-[8px]
    [&::-moz-range-thumb]:bg-blue-500
    [&::-moz-range-thumb]:[box-shadow:_-20em_0_0_20em_black]
    /* Custom for moz */
    [&::-moz-range-thumb]:border-0
    [&::-moz-range-thumb]:rounded-none
  "
/>

I tried creating a custom variant, but it seems it doesn't work:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@custom-variant slider-track (&::-webkit-slider-runnable-track, &::-moz-range-track));
</style>

<input
  type="range"
  class="
    m-5 appearance-none w-[20em] h-[2em] outline-none overflow-hidden rounded
    
    /* Track for webkit & moz */
    slider-track:bg-slate-300
    slider-track:h-full
    slider-track:rounded
    
    /* Thumb for webkit */
    [&::-webkit-slider-thumb]:h-full
    [&::-webkit-slider-thumb]:w-[8px]
    [&::-webkit-slider-thumb]:bg-blue-500
    [&::-webkit-slider-thumb]:[box-shadow:_-20em_0_0_20em_black]
    /* Custom for webkit */
    [&::-webkit-slider-thumb]:appearance-none
    
    /* Thumb for moz */
    [&::-moz-range-thumb]:h-full
    [&::-moz-range-thumb]:w-[8px]
    [&::-moz-range-thumb]:bg-blue-500
    [&::-moz-range-thumb]:[box-shadow:_-20em_0_0_20em_black]
    /* Custom for moz */
    [&::-moz-range-thumb]:border-0
    [&::-moz-range-thumb]:rounded-none
  "
/>

How can I make the variants in class names more readable, and how can I eliminate code duplication this way?


Solution

  • You're on the right track - using custom variants, you can make the values written as arbitrary values more readable. Not only single-line custom variants can be created, but much more complex ones as well. In this case, the styles passed to the variant need to be declared twice: once for WebKit and once for Mozilla, and these cannot be merged.

    <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
    <style type="text/tailwindcss">
    @custom-variant slider-track {
      &::-webkit-slider-runnable-track {
        @slot;
      }
      &::-moz-range-track {
        @slot;
      }
    }
    
    @custom-variant slider-thumb {
      &::-webkit-slider-thumb {
        @slot;
      }
      &::-moz-range-thumb {
        @slot;
      }
    }
    </style>
    
    <input
      type="range"
      class="
        m-5 appearance-none w-[20em] h-[2em] outline-none overflow-hidden rounded
    
        slider-track:h-full
        slider-track:bg-slate-300 slider-track:rounded
    
        slider-thumb:h-full slider-thumb:w-[8px]
        slider-thumb:bg-sky-500 slider-thumb:[box-shadow:_-20em_0_0_20em_black]
      "
    />

    Note: The example is not perfect yet, as it lacks Mozilla-specific and WebKit-specific styling.

    After that, we can create additional custom variant - with consistent naming - that apply styles specifically for WebKit or Mozilla only.

    <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
    <style type="text/tailwindcss">
    @custom-variant slider-track {
      &::-webkit-slider-runnable-track {
        @slot;
      }
      &::-moz-range-track {
        @slot;
      }
    }
    @custom-variant slider-track--webkit (&::-webkit-slider-runnable-track);
    @custom-variant slider-track--moz (&::-moz-range-track);
    
    @custom-variant slider-thumb {
      &::-webkit-slider-thumb {
        @slot;
      }
      &::-moz-range-thumb {
        @slot;
      }
    }
    @custom-variant slider-thumb--webkit (&::-webkit-slider-thumb);
    @custom-variant slider-thumb--moz (&::-moz-range-thumb);
    </style>
    
    <input
      type="range"
      class="
        m-5 appearance-none w-[20em] h-[2em] outline-none overflow-hidden rounded
    
        slider-track:h-full
        slider-track:bg-slate-300 slider-track:rounded
    
        slider-thumb:h-full slider-thumb:w-[8px]
        slider-thumb:bg-sky-500 slider-thumb:[box-shadow:_-20em_0_0_20em_black]
    
        slider-thumb--webkit:appearance-none
        slider-thumb--moz:border-0 slider-thumb--moz:rounded-none
      "
    />