Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with applying outline utility classes in Tailwind CSS #387

Closed
Squiffles opened this issue Mar 3, 2024 · 5 comments
Closed

Problem with applying outline utility classes in Tailwind CSS #387

Squiffles opened this issue Mar 3, 2024 · 5 comments
Labels
context-v2 Related to tailwind-merge v2

Comments

@Squiffles
Copy link

Description

I'm encountering an issue with applying outline utility classes in Tailwind CSS. When using the twMerge utility function, I'm unable to consistently apply outline-focusedWidth, outline-focusedColor and outline-offset-focusedOffset classes to an element.

Steps To Reproduce

tailwind.config.js:

module.exports = {
  theme: {
    extend: {
      outlineColor: {
        focus: '#00A491',
      },
      outlineWidth: {
        focus: '2.5px',
      },
      outlineOffset: {
        focus: '1.7px',
      },
    },
  },
};

Button.jsx:

function Button () {
  return (
    <button className={twMerge("outline outline-focusedWidth outline-focusedColor outline-offset-focusedOffset")}>
      Click
    </button>
  )
}

Expected Behaviour

On the browser, I expect the bundler to apply the following classes to the element:

  1. outline:
    .outline {
        outline-style: solid;
    }
  2. outline-focusedWidth:
    .outline-focusedWidth {
        outline-width: 2.5px;
    }
  3. outline-focusedColor:
    .outline-focusedColor {
        outline-color: #00A491;
    }
  4. outline-offset-focusedOffset:
    .outline-offset-focusedOffset {
        outline-offset: 1.7px;
    }

And render the following HTML:

<button class="outline outline-offset-focusedOffset outline-focusedColor outline-focusedWidth">Click</button>

Actual behaviour

Only the last class is applied. For instance, if I have this:

<button className={twMerge("outline outline-focusedWidth outline-focusedColor outline-offset-focusedOffset")}>

The outline-offset-focusedOffset class is correctly applied, but the outline-focusedWidth and outline-focusedColor are not and this is rendered:

<button class="outline outline-offset-focusedOffset">Click</button>

However, if I change the order:

<button className={twMerge("outline outline-focusedWidth outline-offset-focusedOffset outline-focusedColor")}>

This is rendered:

<button class="outline outline-focusedColor">Click</button>

The outline class is always added, no matter the order.

Using the regular className prop:

<button className="outline outline-focusedWidth outline-focusedColor outline-offset-focusedOffset">Click</button>

works well and adds the four classes to the element in the final bundle. So, I suppose the issue is related to how twMerge manages the outline classes: outline-<customClass> and outline-offset-<customClass>.

Workaround

Using arbitrary values for the outline when using twMerge:

<button className={twMerge("outline outline-[2.5px] outline-[#00A491] outline-offset-[1.7px]")}>Click</button>

Correctly applies the 4 classes:

<button class="outline outline-[2.5px] outline-[#00A491] outline-offset-[1.7px]">Click</button>

Environment

  • TypeScript version: 5.3.3
  • React version: 18.2.0
  • Vite version: 5.1.3
  • Tailwind CSS version: 2.2.1

Additional information

The same behaviour occurs when the classes are defined with the same name:

tailwind.config.js:

module.exports = {
  theme: {
    extend: {
      outlineColor: {
        focus: '#00A491'
      },
      outlineWidth: {
        focus: '2.5px'
      },
    },
  },
};

Then, using the regular className prop:

<button className="outline outline-focus outline-offset-focus">Click</button>

adds the 3 classes:

  1. outline:
.outline {
    outline-style: solid;
}
  1. outline-focus:
.outline-focus {
    outline-width: 2.5px;
}
.outline-focus {
    outline-color: #00A491;
}
  1. outline-offset-focus:
.outline-offset-focus {
    outline-offset: 1.7px;
}

But, using twMerge doesn't work:

<button className={twMerge("outline outline-focus outline-offset-focus")}>Click</button>

And you have to choose which one to write last, whether outline-focus or outline-offset-focus.

Note that in this case if you choose to write "outline-focus" as the last:

<button className={twMerge("outline outline-offset-focus outline-focus ")}>Click</button>

It will apply colour and width:

outline-focus:

.outline-focus {
    outline-width: 2.5px;
}
.outline-focus {
    outline-color: #00A491;
}

However, this behaviour only occurs if the names for these custom properties are the same:

@dcastil dcastil added the context-v2 Related to tailwind-merge v2 label Mar 5, 2024
@dcastil
Copy link
Owner

dcastil commented Mar 5, 2024

Hey @Squiffles! 👋

tailwind-merge doesn't have access to the tailwind.config.js file and you need to configure it separately so it knows about your outline classes (you only need to configure outline width, outline color work without any config).

Here is an example on how to configure tailwind-merge: https://github.com/dcastil/tailwind-merge/blob/v2.2.1/docs/recipes.md#adding-custom-scale-from-tailwind-config-to-tailwind-merge-config.

And here is the documentation on how the tailwind-merge configuration works: https://github.com/dcastil/tailwind-merge/blob/v2.2.1/docs/configuration.md#usage-with-custom-tailwind-config.


(For myself)

Related: #368, #322, #321, #315, #302, #276, #275, #274, #250, #207

@Squiffles
Copy link
Author

Hey @dcastil. Thanks for your answer.

Actually, the issue comes with "outline-focus" (colour and width) and "outline-offset-focus" (just offset). Only one of these classes would be accepted.

Using just a string adds both classes:

className="outline-focus outline-offset-focus" --> outline-focus outline-offset-focus (colour, width and offset)

But using twMerge only adds the last one when it should be both:

className={twMerge("outline-focus outline-offset-focus")} --> outline-offset-focus (offset)
className={twMerge("outline-offset-focus outline-focus")} --> outline-focus (colour and width)

This is my tailwind config again and why I think it should accept both classes:

...
theme: {
  extend: {
      outlineColor: {
        focus: '#00A491'
      },
      outlineWidth: {
        focus: '2.5px'
      },
      outlineOffset: {
        focus: '1.7px'
      },
...

I've tried to use a custom tailwind merge as suggested. Ended up with this function:

import { extendTailwindMerge } from 'tailwind-merge';

export default extendTailwindMerge({
    extend: {
        classGroups: {
            "outline-color": ["focus"],
            "outline-offset": ["focus"],
            "outline-w": ["focus"]
        }
    }
});

Still doesn't accept both classes.

@dcastil
Copy link
Owner

dcastil commented Mar 14, 2024

Ah you need to use the full class name as value. Also no need to configure color classes, tailwind-merge detects any unknown classes that could be color classes as such. This would be the correct config for that:

const twMerge = extendTailwindMerge({
    extend: {
        classGroups: {
            "outline-offset": ["outline-offset-focus"],
            "outline-w": ["outline-focus"]
        }
    }
});

@Squiffles
Copy link
Author

Working like a fine wine ! Thanks.

@dcastil
Copy link
Owner

dcastil commented Apr 8, 2024

Awesome! Let me close this issue then.

@dcastil dcastil closed this as completed Apr 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
context-v2 Related to tailwind-merge v2
Projects
None yet
Development

No branches or pull requests

2 participants