timolins / react-hot-toast

Smoking Hot React Notifications 🔥
https://react-hot-toast.com
MIT License
9.61k stars 314 forks source link

Is it possible to change where the style sheets are injected? #189

Open vedantroy opened 2 years ago

vedantroy commented 2 years ago

I am using this library inside of a shadow DOM. I was wondering if there was way to change where the stylesheets are injected. I know goober has options to do this (you can bind the goober functions), but I don't think I can change the goober functions that are used inside the library.

Fabrotich commented 2 years ago

Facing the same issue here. I'm building a chrome extension that renders on web pages via the shadow dom. I explicitly import my custom stylesheets to make css work in that environment. I'm unable to do the same with React hot toast.

vedantroy commented 2 years ago

Facing the same issue here. I'm building a chrome extension that renders on web pages via the shadow dom. I explicitly import the stylesheets to make things work. Curious about how I could change where the stylesheets are injected.

Hm, how have you managed to explicitly import the style sheets? I don't see a way to change where goober injects them at runtime.

Fabrotich commented 2 years ago

Facing the same issue here. I'm building a chrome extension that renders on web pages via the shadow dom. I explicitly import the stylesheets to make things work. Curious about how I could change where the stylesheets are injected.

Hm, how have you managed to explicitly import the style sheets? I don't see a way to change where goober injects them at runtime.

Updated my reply with more accurate information

kitsunekyo commented 2 years ago

we ran into a similar issue when using the library with tailwind. react-hot-toast will generate a css class .go2072408551 that will overwrite the tailwind utility styles on build due to the import order. on local dev it works ok.

on build, all css will move into a chunk.css file, that is imported in the html head first. react-hot-toast will inject a style tag at the end of the head, and thus overwriting all tailwind styles due to import order specificity.

we customize our <Toaster> globally once like this

export const Toaster = () => (
  <ReactHotToaster
    position="bottom-left"
    reverseOrder
    toastOptions={{
      icon: <FontAwesomeIcon icon={['far', 'info-circle']} fixedWidth />,
      className: defaultStyles,
      success: {
        icon: <FontAwesomeIcon icon={['far', 'check-circle']} fixedWidth />,
        className: `${defaultStyles} bg-green-600`,
      },
      error: {
        icon: (
          <FontAwesomeIcon icon={['far', 'exclamation-circle']} fixedWidth />
        ),
        className: `${defaultStyles} bg-red-600`,
      },
    }}
  />
);

edit:

we're specifically importing the <Toaster> module BEFORE our tailwind css, so that the css of the library SHOULD be loaded before tailwind, but it doesnt.

// ...
import { Toaster } from 'src/common/toast';
import './index.css';
// ...

image

TAGC commented 2 years ago

I'm being bitten by the same issue. I'm trying to use custom styles for toast notifications and the styling is broken when building a production version of the app compared to when serving it locally.

TAGC commented 2 years ago

Working around this issue by copying all the properties off of this external CSS file and directly setting them as the styles on the toast notifications:

// Necessary due to issue with react-hot-toast (https://github.com/timolins/react-hot-toast/issues/189)
function styleAlertToast(alertType: string): React.CSSProperties {
  const baseStyle: React.CSSProperties = {
    // .ch-alert
    border: '1px solid #e5e5e5',
    borderRadius: '4px',
    color: '#2d3737',
    display: 'block',
    padding: '12px 14px',
    width: '100',
    // .ch-alert--icon
    backgroundPosition: 'left 14px center',
    backgroundRepeat: 'no-repeat',
    backgroundSize: '28px auto',
    paddingLeft: '56px',
  };

  switch (alertType) {
    case 'success':
      return {
        ...baseStyle,
        // .ch-alert--success
        backgroundColor: '#e7f7ec',
        borderColor: '#12b447',
        // .ch-alert--success.ch-alert--icon
        backgroundImage: `url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.06 8.06c.32.3.33.8.02 1.13l-6.41 6.73a.8.8 0 0 1-.57.24h-.01a.8.8 0 0 1-.57-.23l-2.59-2.6a.8.8 0 1 1 1.13-1.12l2.01 2 5.85-6.13a.8.8 0 0 1 1.14-.02M12 22.4C6.27 22.4 1.6 17.73 1.6 12a10.33 10.33 0 0 1 3.05-7.35A10.33 10.33 0 0 1 12 1.6c2.78 0 5.39 1.08 7.36 3.05A10.33 10.33 0 0 1 22.4 12c0 5.73-4.67 10.4-10.4 10.4M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24' fill='%2312B347'/%3E%3C/svg%3E" )`,
      };

    case 'error':
      return {
        ...baseStyle,
        // .ch-alert--danger
        backgroundColor: '#f8eaea',
        borderColor: '#c23131',
        // .ch-alert--danger.ch-alert--icon
        backgroundImage: `url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23C23131' d='M22.4 12a10.33 10.33 0 0 0-3.05-7.35A10.33 10.33 0 0 0 12 1.6a10.33 10.33 0 0 0-7.36 3.05A10.33 10.33 0 0 0 1.6 12c0 5.73 4.67 10.4 10.4 10.4S22.4 17.73 22.4 12m1.6 0a12 12 0 1 1-24 0 12 12 0 0 1 24 0m-12 2.05a1.26 1.26 0 1 0 0 2.53 1.26 1.26 0 0 0 0-2.53m-.8-2.63v-4a.8.8 0 0 1 1.6 0v4a.8.8 0 0 1-1.6 0'/%3E%3C/svg%3E")`,
      };

    default:
      return baseStyle;
  }
}

export default function AdminClient() {
  return (
    <AdminClientContainer>
      <Toaster
        position="bottom-right"
        containerStyle={{
          top: 20,
          left: 20,
          bottom: 20,
          right: 20,
        }}
        toastOptions={{
          icon: null,
          style: {
            fontSize: '1.2rem',
            width: 'auto',
          },
          success: {
            style: styleAlertToast('success'),
          },
          error: {
            style: styleAlertToast('error'),
          },
        }}
      />
      <Header />
      <AdminClientContent>
        {renderSidebar()}
        {renderRouter()}
      </AdminClientContent>
    </AdminClientContainer>
  );
}

But it would be a lot more convenient to just have been able to do:

export default function AdminClient() {
  return (
    <AdminClientContainer>
      <Toaster
        position="bottom-right"
        containerStyle={{
          top: 20,
          left: 20,
          bottom: 20,
          right: 20,
        }}
        toastOptions={{
          icon: null,
          style: {
            fontSize: '1.2rem',
            width: 'auto',
          },
          success: {
            className: 'ch-alert ch-alert--success ch-alert--icon',
          },
          error: {
            className: 'ch-alert ch-alert--danger ch-alert--icon',
          },
        }}
      />
      <Header />
      <AdminClientContent>
        {renderSidebar()}
        {renderRouter()}
      </AdminClientContent>
    </AdminClientContainer>
  );
}
luixo commented 1 year ago

The same issue. I'm using stitches which injected itself on SSR before react-hot-toast kicked in.

I managed to bypass that though by manually adding a goober-extracted CSS before the prioritized one in a _document.tsx (I'm using next.js):

  import { getCssText } from "@stitches/react";
+ import { extractCss } from "goober";

  // ...
  <Head>
+  <style id="_goober">{extractCss()}</style>
   <style
     id="stitches"
     dangerouslySetInnerHTML={{ __html: getCssText() }}
   />
  </Head>
  // ...

UPD: You may have to add anything inside the style tag (like a comment: /* ! */) because empty style tag may crash the app.

livwvil commented 1 year ago

we ran into a similar issue when using the library with tailwind. react-hot-toast will generate a css class .go2072408551 that will overwrite the tailwind utility styles on build due to the import order. on local dev it works ok.

I have the same issue... Do you have a workaround?

naveen22208 commented 1 year ago

I'm also facing the same issue @livwvil. did you get the solution?

livwvil commented 1 year ago

I'm also facing the same issue @livwvil. did you get the solution?

import type { FC } from 'react';
import { Toaster as LibToaster, toast, ToastBar } from 'react-hot-toast';

import { XIcon } from '@heroicons/react/solid';

export const Toaster: FC = () => (
  <LibToaster
    reverseOrder={false}
    toastOptions={{
      position: 'bottom-right',
      success: {
        duration: 4000,
      },
      error: {
        duration: Infinity,
      },
      className:
        'flex items-start max-w-none w-[400px] h-[85px] rounded-lg overflow-hidden bg-background-primary border dark:border-d-gray-100 dark:bg-d-background-secondary',
    }}
  >
    {t => (
      <ToastBar toast={t}>
        {({ icon, message }) => (
          <>
            <div className='shrink-0'>{icon}</div>
            <div className='my-[-4px] grow'>{message}</div>
            {t.type !== 'loading' && (
              <button
                className='h-5 w-5 shrink-0'
                onClick={() => toast.dismiss(t.id)}
              >
                <XIcon />
              </button>
            )}
          </>
        )}
      </ToastBar>
    )}
  </LibToaster>
);

I cant remember. We have this Toaster component and a couple of message components, so we're toasting the component, not the string.

toast.promise(somePromise, {
  loading: <ToastMessage title={t('static:uploading')} />,
  success: (
    <ToastMessage title={t('static:successfullyUploaded')} />
  ),
  error: err => (
    <ToastMessage
      title={t('static:notUploaded')}
      text={err.message}
    />
  ),
});
livwvil commented 1 year ago

I found that piece of code, we just moved styles from classname to style and left TODO...

    <LibToaster
      reverseOrder={false}
      toastOptions={{
        position: 'bottom-right',
        success: {
          duration: 4000,
        },
        error: {
          duration: 8000,
        },
        // TODO find out how to fix production build order [tailwind, goober] -> [goober, tailwind] then replace it with commented code
        // https://github.com/timolins/react-hot-toast/issues/189
        // or make custom Toaster component
        // https://codesandbox.io/s/react-hot-toast-tailwindcss-headless-ui-qvjri?file=/src/App.js
        style: {
          display: 'flex',
          height: '85px',
          width: '400px',
          maxWidth: 'none',
          alignItems: 'flex-start',
          overflow: 'hidden',
          borderRadius: '8px',
          borderWidth: '1px',
          backgroundColor: '#fff',
        },
        className: 'dark:!border-d-gray-100 dark:!bg-d-background-secondary',
        // className:
        //   'flex items-start max-w-none w-[400px] h-[85px] rounded-lg overflow-hidden bg-background-primary border dark:border-d-gray-100 dark:bg-d-background-secondary',
      }}
    >
...