htmlcsstailwind-css

preline ui offcanvas not working when tailwind prefix is applied


im trying to use preline ui offcanvas, when copy pasting the code given on their website, it works as expected, but since im also using bootstrap i need to use a prefix to avoid conflicts

<button type="button" class="tw-m-1 tw-ms-0 tw-py-3 tw-px-4 tw-inline-flex tw-items-center tw-gap-x-2 tw-text-sm tw-font-medium tw-rounded-lg tw-border tw-border-transparent tw-bg-blue-600 tw-text-white hover:tw-bg-blue-700 focus:tw-outline-none focus:tw-bg-blue-700 disabled:tw-opacity-50 disabled:tw-pointer-events-none" aria-haspopup="dialog" aria-expanded="false" aria-controls="hs-offcanvas-right" data-hs-overlay="#hs-offcanvas-right">
  Toggle right offcanvas
</button>


<div id="hs-offcanvas-right" class="tw-hs-overlay hs-overlay-open:tw-translate-x-0 tw-hidden tw-translate-x-full tw-fixed tw-top-0 tw-end-0 tw-transition-all tw-duration-300 tw-transform tw-h-full tw-max-w-xs tw-w-full tw-z-[80] tw-bg-white tw-border-s dark:tw-bg-neutral-800 dark:tw-border-neutral-700" role="dialog" tabindex="-1" aria-labelledby="hs-offcanvas-right-label">
  <div class="tw-flex tw-justify-between tw-items-center tw-py-3 tw-px-4 tw-border-b dark:tw-border-neutral-700">
    <h3 id="hs-offcanvas-right-label" class="tw-font-bold tw-text-gray-800 dark:tw-text-white">
      Offcanvas title
    </h3>
    <button type="button" class="tw-size-8 tw-inline-flex tw-justify-center tw-items-center tw-gap-x-2 tw-rounded-full tw-border tw-border-transparent tw-bg-gray-100 tw-text-gray-800 hover:tw-bg-gray-200 focus:tw-outline-none focus:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-pointer-events-none dark:tw-bg-neutral-700 dark:tw-hover:bg-neutral-600 dark:tw-text-neutral-400 dark:tw-focus:bg-neutral-600" aria-label="Close" data-hs-overlay="#hs-offcanvas-right">
      <span class="tw-sr-only">Close</span>
      <svg class="tw-shrink-0 tw-size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M18 6 6 18"></path>
        <path d="m6 6 12 12"></path>
      </svg>
    </button>
  </div>
  <div class="tw-p-4">
    <p class="tw-text-gray-800 dark:tw-text-neutral-400">
      Some text as placeholder. In real life you can have the elements you have chosen. Like, text, images, lists, etc.
    </p>
  </div>
</div>

I've replaced every class with the prefix, I have also made sure that the prefix exists in tailwind.config.js and important is set to true.


Solution

  • The problem is multifaceted. Every solution below must be applied to fix your issue.

    Fix target class

    In the JavaScript initialization for off-canvases, it searches for the hs-overlay class:

    document
        .querySelectorAll('.hs-overlay:not(.--prevent-on-load-init)')
        .forEach((el: HTMLElement) => {
            if (
                !window.$hsOverlayCollection.find(
                    (elC) => (elC?.element?.el as HTMLElement) === el,
                )
            )
                new HSOverlay(el);
        });
    

    Solution

    Hence the class on the off-canvas should be hs-overlay, not tw-hs-overlay:

    <div id="hs-offcanvas-right" class="hs-overlay …">
    

    Fix hidden class toggling

    The JavaScript assumes we're using unprefixed Tailwind classes:

    this.hiddenClass = concatOptions?.hiddenClass || 'hidden';
    

    But the lines before this allow us to override this:

    const data = el.getAttribute('data-hs-overlay-options');
    const dataOptions: IOverlayOptions = data ? JSON.parse(data) : {};
    const concatOptions = {
        ...dataOptions,
        ...toggleDataOptions,
        ...options,
    };
    

    Solution

    So we can configure the overlay as follows using the data-hs-overlay-options for our prefixed tw-hidden class instead:

    <div
      id="hs-offcanvas-right"
      class="…"
      data-hs-overlay-options='{"hiddenClass":"tw-hidden"}'
    >
    

    Fix hs-overlay-open usage

    When the off-canvas is opened, the open and opened classes are added to the element:

    this.el.classList.add('open', 'opened');
    

    The hs-overlay-open variant corresponds to the open class:

    addVariant('hs-overlay-open', [
        ({ modifySelectors, separator }) => {
            modifySelectors(({ className }) => {
                return `.open.${e(`hs-overlay-open${separator}${className}`)}`;
            });
        },
    

    However, with the Tailwind prefix, Tailwind adds the prefix string to open:

    .tw-open.hs-overlay-open\:tw-translate-x-0 {
    

    Solution

    This means these classes won't apply even when the open class is present. There is no API in Tailwind or Preline to modify this. So we would need to change the hs-overlay-open: variant usage for perhaps an arbitrary variant that targets the open class:

    <div id="hs-offcanvas-right" class="hs-overlay [&.open]:tw-translate-x-0 …">
    

    Here's a working StackBlitz project with all the above solutions applied.