tailwind-merge: Bug report: Shadow color overriding extended shadow plugin

I am using a shadow color along with a custom shadow plugin utility I’ve added:

Usage

shadow-border-r-2 shadow-gray-900/10

twMerge Config

import clsx, { type ClassValue } from 'clsx';
import { extendTailwindMerge } from 'tailwind-merge';

const twMerge = extendTailwindMerge({
  classGroups: {
    shadow: [
      'shadow-t',
      'shadow-r',
      'shadow-b',
      'shadow-l',
      'shadow-border',
      'shadow-border-l',
      'shadow-border-r',
      'shadow-border-t',
      'shadow-border-b',
    ],
  },
});

export const cls = (...inputs: ClassValue[]) => twMerge(clsx(...inputs));

And I’m pulling all those utilities in from my package, tailwindcss-directional-shadows (GitHub source | NPM):

const plugin = require('tailwindcss/plugin');

module.exports = plugin(function({ addUtilities, matchUtilities, theme }) {
  // Directional shadows
  ['sm', '', 'md', 'lg', 'xl', '2xl'].forEach((suffix) => {
    ['b', 'r', 't', 'l'].forEach((dir, i) => {
      const isEven = i % 2 === 0
      const baseStyleString = theme(`boxShadow.${suffix || 'DEFAULT'}`)
      const styleString = baseStyleString.split(', ').map((basePart) =>
        basePart
          .split(' ')
          .slice(0, 2)
          .map((l, j) => (j === 1 && i > 1 ? `-${l}` : l))
          [isEven ? 'slice' : 'reverse']()
          .concat(basePart.split(' ').slice(2, 4))
          .join(' ')
      ).map((p) => `${p} var(--tw-shadow-color, rgb(0 0 0 / 0.1))`).join(', ');
      addUtilities({
        [`.shadow-${dir}${suffix ? `-${suffix}` : ''}`]: {
          '--tw-shadow': styleString,
          '--tw-shadow-colored': styleString,
          'box-shadow':
            'var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)',
        },
      })
    })
  });
  // Shadows as borders (useful with `position: sticky` on tables)
  matchUtilities(
    {
      'shadow-border': (value) => ({
        boxShadow: `inset 0 0 0 ${value} var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
      'shadow-border-x': (value) => ({
        boxShadow: `inset ${value} 0 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )}), inset -${value} 0 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
      'shadow-border-y': (value) => ({
        boxShadow: `inset 0 ${value} 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )}), inset 0 -${value} 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
      'shadow-border-t': (value) => ({
        boxShadow: `inset 0 ${value} 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
      'shadow-border-b': (value) => ({
        boxShadow: `inset 0 -${value} 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
      'shadow-border-l': (value) => ({
        boxShadow: `inset ${value} 0 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
      'shadow-border-r': (value) => ({
        boxShadow: `inset -${value} 0 0 0 var(--tw-shadow-color, ${theme(
          'colors.gray.200'
        )})`,
      }),
    },
    { values: theme('borderWidth') }
  );
});

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 17 (7 by maintainers)

Most upvoted comments

~It still appears that doing this shadow-border shadow-black/5 compiles to shadow-black/5, and shadow-border is lost altogether. Is that because of the opacity values in shadow-black/5? Any way around that?~

Disregard — it appears I never saved the last change you suggested. Sorry, and thanks again!

@dcastil Just an idea— it might be useful to introduce a helper function to shorthand some of those repetitive lines. For example, this…

const twMerge = extendTailwindMerge({
  classGroups: {
    shadow: [
      {
        shadow: [
          {
            t: ['', 'inner', validators.isTshirtSize],
            r: ['', 'inner', validators.isTshirtSize],
            b: ['', 'inner', validators.isTshirtSize],
            l: ['', 'inner', validators.isTshirtSize],
            border: [
              validators.isNumber,
              validators.isArbitraryLength,
              {
                x: [validators.isNumber, validators.isArbitraryLength],
                y: [validators.isNumber, validators.isArbitraryLength],
                t: [validators.isNumber, validators.isArbitraryLength],
                r: [validators.isNumber, validators.isArbitraryLength],
                b: [validators.isNumber, validators.isArbitraryLength],
                l: [validators.isNumber, validators.isArbitraryLength],
              },
            ],
          },
        ],
      },
    ],
  },
});

could become this…

function spreadKeys<T>(keys: string[], value: T): Record<string, T> {
  return keys.reduce((accu: Record<string, T>, key) => {
    accu[key] = value;
    return accu;
  }, {});
};

const twMerge = extendTailwindMerge({
  classGroups: {
    shadow: [
      {
        shadow: [
          {
            ...spreadKeys(['t','r','b','l'], ['', 'inner', validators.isTshirtSize]),
            border: [
              validators.isNumber,
              validators.isArbitraryLength,
              spreadKeys(['x','y','t','r','b','l'], [validators.isNumber, validators.isArbitraryLength]),
            ],
          },
        ],
      },
    ],
  },
});

I assume that your problem is resolved, so I’m going to close this issue. Let me know if I should re-open it.