[
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\non: [push]\njobs:\n  build:\n    name: Build & test\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: 'pnpm'\n      - name: Install dependencies\n        run: pnpm install\n      - name: Build package\n        run: pnpm build\n      - name: Test\n        run: pnpm run test --ci --coverage\n"
  },
  {
    "path": ".github/workflows/size.yml",
    "content": "name: size\non: [pull_request]\njobs:\n  size:\n    runs-on: ubuntu-latest\n    env:\n      CI_JOB_NUMBER: 1\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: 20.x\n          cache: 'pnpm'\n      - run: pnpm install\n      - uses: andresz1/size-limit-action@v1\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.log\n.DS_Store\nnode_modules\n.cache\ncoverage\ndist\n/headless\n.vscode\n.vercel\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Timo Lins\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<a href=\"https://react-hot-toast.com/\"><img alt=\"react-hot-toast - Try it out\" src=\"https://github.com/timolins/react-hot-toast/raw/main/assets/header.svg\"/></a>\n\n<div align=\"center\">\n    <img src=\"https://badgen.net/npm/v/react-hot-toast\" alt=\"NPM Version\" />\n  <img src=\"https://badgen.net/bundlephobia/minzip/react-hot-toast\" alt=\"minzipped size\"/>\n    <img src=\"https://github.com/timolins/react-hot-toast/workflows/CI/badge.svg\" alt=\"Build Status\" />\n</a>\n</div>\n<br />\n<div align=\"center\"><strong>Smoking hot  Notifications for React.</strong></div>\n<div align=\"center\"> Lightweight, customizable and beautiful by default.</div>\n<br />\n<div align=\"center\">\n<a href=\"https://react-hot-toast.com/\">Website</a> \n<span> · </span>\n<a href=\"https://react-hot-toast.com/docs\">Documentation</a> \n<span> · </span>\n<a href=\"https://twitter.com/timolins\">Twitter</a>\n</div>\n\n<br />\n<div align=\"center\">\n  <sub>Cooked by <a href=\"https://twitter.com/timolins\">Timo Lins</a> 👨‍🍳</sub>\n</div>\n\n<br />\n\n## Features\n\n- 🔥 **Hot by default**\n- 🔩 **Easily Customizable**\n- ⏳ **Promise API** - _Automatic loader from a promise_\n- 🕊 **Lightweight** - _less than 5kb including styles_\n- ✅ **Accessible**\n- 🤯 **Headless Hooks** - _Create your own with [`useToaster()`](https://react-hot-toast.com/docs/use-toaster)_\n\n## Installation\n\n#### With pnpm\n\n```sh\npnpm add react-hot-toast\n```\n\n#### With NPM\n\n```sh\nnpm install react-hot-toast\n```\n\n## Getting Started\n\nAdd the Toaster to your app first. It will take care of rendering all notifications emitted. Now you can trigger `toast()` from anywhere!\n\n```jsx\nimport toast, { Toaster } from 'react-hot-toast';\n\nconst notify = () => toast('Here is your toast.');\n\nconst App = () => {\n  return (\n    <div>\n      <button onClick={notify}>Make me a toast</button>\n      <Toaster />\n    </div>\n  );\n};\n```\n\n## Documentation\n\nFind the full API reference on [official documentation](https://react-hot-toast.com/docs).\n"
  },
  {
    "path": "jest.config.js",
    "content": "/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */\nmodule.exports = {\n  preset: 'ts-jest',\n  testEnvironment: 'jsdom',\n  setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-hot-toast\",\n  \"description\": \"Smoking hot React Notifications. Lightweight, customizable and beautiful by default.\",\n  \"version\": \"2.6.0\",\n  \"author\": \"Timo Lins\",\n  \"license\": \"MIT\",\n  \"repository\": \"timolins/react-hot-toast\",\n  \"keywords\": [\n    \"react\",\n    \"notifications\",\n    \"toast\",\n    \"snackbar\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \"./package.json\": \"./package.json\",\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.js\"\n    },\n    \"./headless\": {\n      \"types\": \"./headless/index.d.ts\",\n      \"import\": \"./headless/index.mjs\",\n      \"require\": \"./headless/index.js\"\n    }\n  },\n  \"files\": [\n    \"headless\",\n    \"dist\",\n    \"src\"\n  ],\n  \"engines\": {\n    \"node\": \">=10\"\n  },\n  \"scripts\": {\n    \"start\": \"tsup --watch\",\n    \"build\": \"tsup\",\n    \"test\": \"jest --runInBand\",\n    \"setup\": \"pnpm i && cd site && pnpm i && cd .. && pnpm run link\",\n    \"link\": \"pnpm link ./site/node_modules/react && pnpm link ./site/node_modules/react-dom\",\n    \"size\": \"size-limit\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"prettier src --ignore-unknown --write\"\n    }\n  },\n  \"prettier\": {\n    \"printWidth\": 80,\n    \"semi\": true,\n    \"singleQuote\": true,\n    \"arrowParens\": \"always\",\n    \"trailingComma\": \"es5\"\n  },\n  \"size-limit\": [\n    {\n      \"path\": \"dist/index.js\",\n      \"limit\": \"5.5 KB\"\n    },\n    {\n      \"path\": \"dist/index.mjs\",\n      \"limit\": \"5.5 KB\"\n    },\n    {\n      \"path\": \"headless/index.js\",\n      \"limit\": \"2.5 KB\"\n    },\n    {\n      \"path\": \"headless/index.mjs\",\n      \"limit\": \"2.5 KB\"\n    }\n  ],\n  \"devDependencies\": {\n    \"@jest/types\": \"^29.6.3\",\n    \"@size-limit/preset-small-lib\": \"^7.0.8\",\n    \"@testing-library/jest-dom\": \"^6.6.3\",\n    \"@testing-library/react\": \"^16.1.0\",\n    \"@types/jest\": \"^29.5.14\",\n    \"@types/react\": \"^18.3.18\",\n    \"@types/react-dom\": \"^18.3.5\",\n    \"esbuild-minify-templates\": \"^0.13.1\",\n    \"jest\": \"^29.7.0\",\n    \"jest-environment-jsdom\": \"^29.7.0\",\n    \"prettier\": \"^2.8.8\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"size-limit\": \"^7.0.8\",\n    \"ts-jest\": \"^29.2.5\",\n    \"tslib\": \"^2.8.1\",\n    \"tsup\": \"^6.7.0\",\n    \"typescript\": \"^5.7.2\"\n  },\n  \"dependencies\": {\n    \"csstype\": \"^3.1.3\",\n    \"goober\": \"^2.1.16\"\n  },\n  \"peerDependencies\": {\n    \"react\": \">=16\",\n    \"react-dom\": \">=16\"\n  },\n  \"packageManager\": \"pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0\"\n}\n"
  },
  {
    "path": "site/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n"
  },
  {
    "path": "site/README.md",
    "content": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "site/components/code.tsx",
    "content": "import clsx from 'clsx';\nimport Highlight, {\n  defaultProps,\n  Language,\n  PrismTheme,\n} from 'prism-react-renderer';\n\nconst theme: PrismTheme = {\n  plain: {\n    backgroundColor: '#351e11',\n    color: '#d6ceff',\n  },\n  styles: [\n    {\n      types: ['comment', 'prolog', 'doctype', 'cdata', 'punctuation'],\n      style: {\n        color: '#6c6783',\n      },\n    },\n    {\n      types: ['namespace'],\n      style: {\n        opacity: 0.7,\n      },\n    },\n    {\n      types: ['tag', 'operator', 'number', 'module'],\n      style: {\n        color: '#e09142',\n      },\n    },\n    {\n      types: ['property', 'function'],\n      style: {\n        color: '#9a86fd',\n      },\n    },\n    {\n      types: ['tag-id', 'selector', 'atrule-id'],\n      style: {\n        color: '#eeebff',\n      },\n    },\n    {\n      types: ['attr-name'],\n      style: {\n        color: '#c4b9fe',\n      },\n    },\n    {\n      types: [\n        'boolean',\n        'string',\n        'entity',\n        'url',\n        'attr-value',\n        'keyword',\n        'control',\n        'directive',\n        'unit',\n        'statement',\n        'regex',\n        'at-rule',\n        'placeholder',\n        'variable',\n      ],\n      style: {\n        color: '#ffcc99',\n      },\n    },\n    {\n      types: ['deleted'],\n      style: {\n        textDecorationLine: 'line-through',\n      },\n    },\n    {\n      types: ['inserted'],\n      style: {\n        textDecorationLine: 'underline',\n      },\n    },\n    {\n      types: ['italic'],\n      style: {\n        fontStyle: 'italic',\n      },\n    },\n    {\n      types: ['important', 'bold'],\n      style: {\n        fontWeight: 'bold',\n      },\n    },\n    {\n      types: ['important'],\n      style: {\n        color: '#c4b9fe',\n      },\n    },\n  ],\n};\n\nexport const Code: React.FC<{\n  snippet: string;\n  language?: Language;\n  className?: string;\n}> = (props) => {\n  const language = props.language || 'jsx';\n\n  return (\n    <Highlight\n      {...defaultProps}\n      code={props.snippet}\n      theme={theme}\n      language={language}\n    >\n      {({ className, style, tokens, getLineProps, getTokenProps }) => (\n        <pre\n          className={clsx(\n            props.className,\n            className,\n            'h-full w-full rounded-lg p-4 overflow-x-auto flex flex-col items justify-center'\n          )}\n          style={style}\n        >\n          {tokens.map((line, i) => {\n            if (tokens.length - 1 === i && line[0].empty) {\n              return null;\n            }\n\n            return (\n              <div {...getLineProps({ line, key: i })} key={i}>\n                {line.map((token, key) => (\n                  <span {...getTokenProps({ token, key })} key={key} />\n                ))}\n              </div>\n            );\n          })}\n        </pre>\n      )}\n    </Highlight>\n  );\n};\n"
  },
  {
    "path": "site/components/docs-layout.tsx",
    "content": "import * as React from 'react';\nimport { Toaster } from 'react-hot-toast';\nimport { NextSeo } from 'next-seo';\nimport Link from 'next/link';\nimport { Footer } from './sections/footer';\nimport Logo from '../assets/logo-small.svg';\n\nconst TableItem: React.FC<{\n  href: string;\n  children?: React.ReactNode;\n}> = ({ children, href }) => (\n  <Link href={href}>\n    <a className=\"rounded px-3 py-1.5 transition-colors duration-200 relative block hover:text-toast-500 text-toast-700\">\n      {children}\n    </a>\n  </Link>\n);\n\nconst TableHeader: React.FC<{\n  children?: React.ReactNode;\n}> = ({ children }) => (\n  <span className=\"px-3 mt-3 mb-1 text-sm font-semibold tracking-wide text-toast-900 uppercase\">\n    {children}\n  </span>\n);\n\nexport default function DocsLayout({ meta, children }) {\n  return (\n    <div className=\"bg-toast-50 bg-opacity-50 min-h-screen flex flex-col\">\n      <NextSeo\n        titleTemplate=\"%s - react-hot-toast\"\n        title={meta.title}\n        openGraph={{\n          images: [\n            {\n              url: `https://react-hot-toast.com/social-image.png`,\n              width: 1200,\n              height: 630,\n            },\n          ],\n        }}\n      />\n\n      <div className=\"flex-1 mx-auto px-2 max-w-4xl w-full\">\n        <header className=\" col-start-1 col-end-6 mt-12 mb-16 px-2 flex justify-between items-center\">\n          <Link href=\"/\">\n            <Logo\n              className=\"cursor-pointer\"\n              aria-label=\"react-hot-toast Logo\"\n            />\n          </Link>\n          <a\n            className=\"flex text-toast-600 underline\"\n            href=\"https://github.com/timolins/react-hot-toast\"\n          >\n            GitHub\n          </a>\n        </header>\n\n        <div className=\"md:flex md:space-x-4\">\n          <nav className=\"font-medium rounded-lg \">\n            <div className=\"flex flex-col mb-8 sticky top-0\">\n              <TableHeader>Overview</TableHeader>\n\n              <TableItem href=\"/docs\">Get Started</TableItem>\n\n              <TableHeader>API</TableHeader>\n\n              <TableItem href=\"/docs/toast\">toast()</TableItem>\n              <TableItem href=\"/docs/toaster\">{`Toaster`}</TableItem>\n              <TableItem href=\"/docs/toast-bar\">{`ToastBar`}</TableItem>\n              <TableItem href=\"/docs/use-toaster\">useToaster()</TableItem>\n              <TableItem href=\"/docs/use-toaster-store\">\n                useToasterStore()\n              </TableItem>\n              <TableHeader>Guides</TableHeader>\n              <TableItem href=\"/docs/styling\">Styling</TableItem>\n              <TableItem href=\"/docs/multi-toaster\">Multi Toaster</TableItem>\n\n              <TableHeader>Releases</TableHeader>\n              <TableItem href=\"/docs/version-2\">New in 2.0</TableItem>\n            </div>\n          </nav>\n\n          <main className=\"col-span-4 w-full prose prose-toast text-toast-900 flex-1\">\n            {children}\n          </main>\n        </div>\n      </div>\n      <Footer />\n      <Toaster />\n    </div>\n  );\n}\n"
  },
  {
    "path": "site/components/emoji-button.tsx",
    "content": "export const EmojiButton: React.FC<{\n  onClick: () => void;\n  emoji: string | React.ReactElement;\n  children?: React.ReactNode;\n}> = ({ onClick, children, emoji }) => (\n  <button\n    className=\"rounded bg-white text-sm font-semibold py-2 px-2 shadow-small-button flex items-center\"\n    onClick={onClick}\n  >\n    <span\n      style={{\n        fontFamily:\n          '\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji',\n      }}\n    >\n      {emoji}\n    </span>\n    <div className=\"flex-1 px-3\">{children}</div>\n  </button>\n);\n"
  },
  {
    "path": "site/components/sections/footer.tsx",
    "content": "import React from 'react';\nimport Link from 'next/link';\n\nexport function Footer({ noBadge }: { noBadge?: boolean }) {\n  return (\n    <footer className=\"container relative justify-center my-8 flex flex-col items-center space-y-4\">\n      <div className=\"flex space-x-4\">\n        <a\n          className=\"underline\"\n          href=\"https://github.com/timolins/react-hot-toast\"\n        >\n          GitHub\n        </a>\n        <Link href=\"/docs\">\n          <a className=\"underline\">Docs</a>\n        </Link>\n        <a className=\"underline\" href=\"https://twitter.com/timolins\">\n          Twitter\n        </a>\n      </div>\n      <div className=\"text-toast-600\">\n        <span>© {new Date().getFullYear()} react-hot-toast</span>\n        {' · '}\n        <span>\n          <span>Built by </span>\n          <a className=\"underline\" href=\"https://timo.sh\">\n            Timo Lins\n          </a>\n        </span>\n      </div>\n      {!noBadge && (\n        <div>\n          <a\n            href=\"https://splitbee.io/?ref=rht\"\n            data-splitbee-event=\"Click Splitbee Analytics\"\n            data-splitbee-event-location=\"Footer\"\n          >\n            <img\n              src=\"https://splitbee-cdn.fra1.cdn.digitaloceanspaces.com/static/badge/splitbee-badge.svg\"\n              alt=\"Analytics by Splitbee.io\"\n            />\n          </a>\n        </div>\n      )}\n    </footer>\n  );\n}\n"
  },
  {
    "path": "site/components/sections/splitbee-counter.tsx",
    "content": "import React from 'react';\nimport clsx from 'clsx';\n\nexport const useSplitbeeCount = <T extends string>(\n  event: T,\n  token: string\n): number => {\n  const [data, setData] = React.useState<number>(0);\n  const socket = React.useRef(null);\n  React.useEffect(() => {\n    if (typeof window !== undefined) {\n      socket.current = new WebSocket('wss://realtime.react-hot-toast.com/');\n      socket.current.onopen = (e) => {\n        socket.current.send(\n          JSON.stringify({\n            type: 'subscribe',\n            data: {\n              token: token,\n              events: [event],\n            },\n          })\n        );\n      };\n      socket.current.onmessage = (e) => {\n        const d = JSON.parse(e.data);\n        setData(d.count);\n      };\n\n      return () => {};\n    }\n  }, []);\n\n  return data;\n};\n\nexport const SplitbeeCounter = () => {\n  const count = useSplitbeeCount('Trigger Toast', 'NTV7AYBLEXW3');\n\n  const letters = count.toString().split('');\n\n  return (\n    <div className=\"flex items-center justify-center p-4 flex-col gap-3 mt-4\">\n      <div className=\"font-semibold text-toast-900 rounded text-lg\">\n        Toasts made on this website so far\n      </div>\n      <div\n        className={clsx('grid gap-2 grid-flow-col', count === 0 && 'opacity-0')}\n      >\n        {letters.map((l, i) => (\n          <div\n            className={clsx(\n              'animate-custom-enter',\n              'bg-toast-100 rounded p-4 text-lg font-bold font-mono'\n            )}\n            key={i + '-' + l}\n          >\n            {l}\n          </div>\n        ))}\n      </div>\n      <div className=\"text-toast-600\">\n        ⚡️ Real-time analytics by{' '}\n        <a\n          className=\"underline\"\n          data-splitbee-event=\"Click Splitbee Analytics\"\n          data-splitbee-event-location=\"Counter\"\n          href=\"https://splitbee.io/?ref=rht-realtime\"\n        >\n          Splitbee\n        </a>\n      </div>\n    </div>\n  );\n};\n"
  },
  {
    "path": "site/components/sections/toast-example.tsx",
    "content": "import React, { useState } from 'react';\nimport toast from 'react-hot-toast';\n\nimport { EmojiButton } from '../emoji-button';\nimport { Code } from '../code';\n\nconst examples: Array<{\n  title: string;\n  action: () => void;\n  emoji: string;\n  snippet: string;\n}> = [\n  {\n    title: 'Success',\n    emoji: '✅',\n    snippet: \"toast.success('Successfully toasted!')\",\n    action: () => {\n      toast.success('Successfully toasted!');\n    },\n  },\n  {\n    title: 'Error',\n    emoji: '❌',\n    snippet: `toast.error(\"This didn't work.\")`,\n\n    action: () => {\n      toast.error(\"This didn't work.\");\n    },\n  },\n  {\n    title: 'Promise',\n    emoji: '⏳',\n    snippet: `toast.promise(\n  saveSettings(settings),\n   {\n     loading: 'Saving...',\n     success: <b>Settings saved!</b>,\n     error: <b>Could not save.</b>,\n   }\n );`,\n    action: () => {\n      const promise = new Promise((res, rej) => {\n        setTimeout(Math.random() > 0.5 ? res : rej, 1000);\n      });\n\n      toast.promise(\n        promise,\n        {\n          loading: 'Saving...',\n          success: <b>Settings saved!</b>,\n          error: <b>Could not save.</b>,\n        },\n        {\n          style: {\n            width: '200px',\n            paddingRight: '10px',\n          },\n        }\n      );\n    },\n  },\n  {\n    title: 'Multi Line',\n    emoji: '↕️',\n    snippet: `toast(\n  \"This toast is super big. I don't think anyone could eat it in one bite.\\\\n\\\\nIt's larger than you expected. You eat it but it does not seem to get smaller.\",\n  {\n    duration: 6000,\n  }\n);`,\n    action: () => {\n      toast(\n        \"This toast is super big. I don't think anyone could eat it in one bite.\\n\\n It's larger than you expected. You eat it but it does not seem to get smaller.\",\n        {\n          duration: 6000,\n        }\n      );\n    },\n  },\n  {\n    title: 'Emoji',\n    emoji: '👏',\n    snippet: `toast('Good Job!', {\n  icon: '👏',\n});`,\n    action: () => {\n      toast('Good Job!', {\n        icon: '👏',\n      });\n    },\n  },\n  {\n    title: 'Dark Mode',\n    emoji: '🌚',\n    snippet: `toast('Hello Darkness!',\n  {\n    icon: '👏',\n    style: {\n      borderRadius: '10px',\n      background: '#333',\n      color: '#fff',\n    },\n  }\n);`,\n    action: () => {\n      toast('Hello Darkness!', {\n        icon: '👏',\n\n        style: {\n          borderRadius: '200px',\n          background: '#333',\n          color: '#fff',\n        },\n      });\n    },\n  },\n  {\n    title: 'JSX Content',\n    emoji: '🔩',\n    snippet: `toast((t) => (\n  <span>\n    Custom and <b>bold</b>\n    <button onClick={() => toast.dismiss(t.id)}>\n      Dismiss\n    </button>\n  </span>\n));`,\n\n    action: () => {\n      toast((t) => (\n        <span>\n          Custom and <b>bold</b>\n          <button\n            className=\"ml-2 py-1 rounded px-2 border bg-gray-100 text-gray-900\"\n            onClick={() => toast.dismiss(t.id)}\n          >\n            Dismiss\n          </button>\n        </span>\n      ));\n    },\n  },\n  {\n    title: 'Themed',\n    emoji: '🎨',\n    snippet: `toast.success('Look at my styles.', {\n  style: {\n    border: '1px solid #713200',\n    padding: '16px',\n    color: '#713200',\n  },\n  iconTheme: {\n    primary: '#713200',\n    secondary: '#FFFAEE',\n  },\n});`,\n\n    action: () => {\n      toast.success('Look at my styles.', {\n        style: {\n          border: '1px solid #713200',\n          padding: '16px',\n          color: '#713200',\n        },\n        iconTheme: {\n          primary: '#713200',\n          secondary: '#FFFAEE',\n        },\n      });\n    },\n  },\n  {\n    title: 'Custom Position',\n    emoji: '⬆️',\n    snippet: `toast.success('Always at the bottom.', {\n  position: \"bottom-center\"\n})`,\n    action: () => {\n      toast.success('Always at the bottom.', {\n        position: 'bottom-center',\n        duration: 10000,\n      });\n    },\n  },\n  {\n    title: 'TailwindCSS',\n    emoji: '️💨',\n    snippet: `toast.custom((t) => (\n  <div\n    className={\\`\\${\n      t.visible ? 'animate-custom-enter' : 'animate-custom-leave'\n    } max-w-md w-full bg-white shadow-lg rounded-lg pointer-events-auto flex ring-1 ring-black ring-opacity-5\\`}\n  >\n    <div className=\"flex-1 w-0 p-4\">\n      <div className=\"flex items-start\">\n        <div className=\"flex-shrink-0 pt-0.5\">\n          <img\n            className=\"h-10 w-10 rounded-full\"\n            src=\"https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixqx=6GHAjsWpt9&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.2&w=160&h=160&q=80\"\n            alt=\"\"\n          />\n        </div>\n        <div className=\"ml-3 flex-1\">\n          <p className=\"text-sm font-medium text-gray-900\">\n            Emilia Gates\n          </p>\n          <p className=\"mt-1 text-sm text-gray-500\">\n            Sure! 8:30pm works great!\n          </p>\n        </div>\n      </div>\n    </div>\n    <div className=\"flex border-l border-gray-200\">\n      <button\n        onClick={() => toast.dismiss(t.id)}\n        className=\"w-full border border-transparent rounded-none rounded-r-lg p-4 flex items-center justify-center text-sm font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500\"\n      >\n        Close\n      </button>\n    </div>\n  </div>\n))`,\n    action: () => {\n      // toast.custom(<TestApp />);\n\n      toast.custom(\n        (t) => (\n          <div\n            className={`${\n              t.visible ? 'animate-custom-enter' : 'animate-custom-leave'\n            } max-w-md w-full bg-white shadow-lg rounded-lg pointer-events-auto flex ring-1 ring-black ring-opacity-5`}\n          >\n            <div className=\"flex-1 w-0 p-4\">\n              <div className=\"flex items-start\">\n                <div className=\"flex-shrink-0 pt-0.5\">\n                  <img\n                    className=\"h-10 w-10 rounded-full\"\n                    src=\"https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixqx=6GHAjsWpt9&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.2&w=160&h=160&q=80\"\n                    alt=\"\"\n                  />\n                </div>\n                <div className=\"ml-3 flex-1\">\n                  <p className=\"text-sm font-medium text-gray-900\">\n                    Emilia Gates\n                  </p>\n                  <p className=\"mt-1 text-sm text-gray-500\">\n                    Sure! 8:30pm works great!\n                  </p>\n                </div>\n              </div>\n            </div>\n            <div className=\"flex border-l border-gray-200\">\n              <button\n                onClick={() => toast.dismiss(t.id)}\n                className=\"w-full border border-transparent rounded-none rounded-r-lg p-4 flex items-center justify-center text-sm font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500\"\n              >\n                Close\n              </button>\n            </div>\n          </div>\n        ),\n        {\n          duration: 10000,\n        }\n      );\n    },\n  },\n];\n\nexport const ToastExample = () => {\n  const [snippet, setSnippet] = useState(examples[0].snippet);\n  return (\n    <section className=\"grid md:grid-cols-2 gap-4\">\n      <div className=\"flex items-center\">\n        <div className=\"w-full grid grid-cols-2 gap-2 bg-toast-100  rounded-xl p-4\">\n          {examples.map((e) => (\n            <EmojiButton\n              key={e.title}\n              emoji={e.emoji}\n              onClick={() => {\n                if (e.snippet) {\n                  setSnippet(e.snippet);\n                }\n                (window as any).splitbee?.track('Trigger Toast', {\n                  example: e.title,\n                });\n                e.action();\n              }}\n            >\n              {e.title}\n            </EmojiButton>\n          ))}\n        </div>\n      </div>\n      <div className=\"md:h-72 w-full overflow-auto rounded-lg\">\n        <Code snippet={snippet} className=\"!h-auto min-h-full\" />\n      </div>\n    </section>\n  );\n};\n"
  },
  {
    "path": "site/components/sections/toaster-example.tsx",
    "content": "import clsx from 'clsx';\nimport toast, { ToastPosition } from 'react-hot-toast';\nimport Arrow from '../../assets/arrow.svg';\nimport { Code } from '../code';\n\nimport { EmojiButton } from '../emoji-button';\n\nexport const positions: Array<ToastPosition> = [\n  'top-left',\n  'top-center',\n  'top-right',\n  'bottom-left',\n  'bottom-center',\n  'bottom-right',\n];\n\nexport const ToasterExample: React.FC<{\n  position: ToastPosition;\n  onPosition: (pos: ToastPosition) => void;\n  reverse: boolean;\n  onReverse: (rev: boolean) => void;\n}> = ({ position, onPosition, reverse, onReverse }) => {\n  const reverseIt = () => {\n    setTimeout(() => {\n      toast('Notification 1', {\n        icon: '1️⃣',\n        id: 'reverse-1',\n      });\n    }, 10);\n\n    setTimeout(\n      () =>\n        toast('Notification 2', {\n          icon: '2️⃣',\n          id: 'reverse-2',\n        }),\n      250\n    );\n    setTimeout(\n      () =>\n        toast('Notification 3', {\n          icon: '3️⃣',\n          id: 'reverse-3',\n        }),\n      500\n    );\n    setTimeout(\n      () =>\n        toast('Notification 4', {\n          icon: '4️⃣',\n          id: 'reverse-4',\n        }),\n      750\n    );\n    (window as any).splitbee?.track('Change Order', {\n      reverseOrder: !reverse,\n    });\n    onReverse(!reverse);\n  };\n\n  const renderPosition = (p: ToastPosition) => (\n    <button\n      id=\"p\"\n      className={clsx(\n        'rounded-xl text-center text-xs md:text-sm py-2 px- flex items-center justify-center cursor-pointer flex-col md:flex-row',\n        position === p\n          ? 'bg-toast-900 text-toast-100 '\n          : 'bg-white shadow-small-button'\n      )}\n      key={p}\n      onClick={() => {\n        toast.success(\n          <span>\n            Position set to <b>{p}</b>\n          </span>,\n          {\n            id: 'position',\n          }\n        );\n\n        (window as any).splitbee?.track('Change Position', {\n          position: p,\n        });\n\n        (window as any).splitbee?.track('Trigger Toast', {\n          example: 'position',\n        });\n\n        onPosition(p);\n      }}\n    >\n      <span className=\"mr-2\">{p}</span>\n    </button>\n  );\n\n  return (\n    <section className=\"flex flex-col md:grid grid-cols-1 md:grid-cols-3 gap-2\">\n      <Code\n        snippet={`<Toaster\n  position=\"${position}\"\n  reverseOrder={${reverse}}\n/>`}\n      />\n      <div className=\"order-first md:order-none col-span-2 grid grid-cols-3 justify-between bg-toast-100 rounded-xl gap-x-2 gap-y-4 p-2 md:p-4\">\n        {positions.map((p) => renderPosition(p))}\n      </div>\n      <div className=\"col-start-2 col-span-2 flex justify-center my-4\">\n        <EmojiButton\n          emoji={\n            <Arrow\n              className={clsx(\n                'transform transition-transform',\n                ((position.includes('bottom') && !reverse) ||\n                  (position.includes('top') && reverse)) &&\n                  'rotate-180'\n              )}\n            />\n          }\n          onClick={reverseIt}\n        >\n          Toggle Direction\n        </EmojiButton>\n      </div>\n    </section>\n  );\n};\n"
  },
  {
    "path": "site/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": "site/next.config.mjs",
    "content": "import rehypeSlug from 'rehype-slug';\nimport remarkGfm from 'remark-gfm';\nimport nextMdx from '@next/mdx';\n\nconst withMDX = nextMdx({\n  extension: /\\.mdx?$/,\n  options: {\n    rehypePlugins: [rehypeSlug],\n    remarkPlugins: [remarkGfm],\n    providerImportSource: '@mdx-js/react',\n  },\n});\n\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  pageExtensions: ['ts', 'tsx', 'md', 'mdx'],\n  webpack(config) {\n    config.module.rules.push({\n      test: /\\.svg$/,\n      use: ['@svgr/webpack'],\n    });\n    return config;\n  },\n  async rewrites() {\n    return [\n      {\n        source: '/bee.js',\n        destination: 'https://cdn.splitbee.io/sb.js',\n      },\n      {\n        source: '/_hive/:slug',\n        destination: 'https://hive.splitbee.io/:slug',\n      },\n    ];\n  },\n};\n\nexport default withMDX(nextConfig);\n"
  },
  {
    "path": "site/package.json",
    "content": "{\n  \"name\": \"site\",\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\"\n  },\n  \"dependencies\": {\n    \"@mdx-js/loader\": \"^2.3.0\",\n    \"@mdx-js/react\": \"^2.3.0\",\n    \"@next/mdx\": \"^12.3.4\",\n    \"@svgr/webpack\": \"^6.5.1\",\n    \"@types/prismjs\": \"^1.26.5\",\n    \"@vercel/analytics\": \"^0.1.11\",\n    \"clsx\": \"^1.1.1\",\n    \"next\": \"^12.3.4\",\n    \"next-seo\": \"^5.15.0\",\n    \"postcss\": \"^8.4.49\",\n    \"prism-react-renderer\": \"^1.3.5\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"react-hot-toast\": \"link:../\",\n    \"rehype-slug\": \"^5.1.0\"\n  },\n  \"devDependencies\": {\n    \"@tailwindcss/typography\": \"^0.5.15\",\n    \"@types/node\": \"^18.19.68\",\n    \"@types/react\": \"^18.3.18\",\n    \"@types/react-dom\": \"^18.3.5\",\n    \"autoprefixer\": \"^10.4.20\",\n    \"remark-gfm\": \"^3.0.1\",\n    \"tailwindcss\": \"^3.4.17\",\n    \"typescript\": \"^4.9.5\"\n  }\n}\n"
  },
  {
    "path": "site/pages/_app.tsx",
    "content": "import '../styles/tailwind-utils.css';\nimport '../styles/main.css';\nimport * as React from 'react';\nimport Link from 'next/link';\nimport Head from 'next/head';\nimport { Analytics } from '@vercel/analytics/react';\n\n\nimport { MDXProvider } from '@mdx-js/react';\nimport { Code } from '../components/code';\n\nconst components = {\n  a: (props) => (\n    <Link href={props.href}>\n      <a {...props} />\n    </Link>\n  ),\n  h1: (props) => {\n    const id = props.id || '';\n    return (\n      <h1 {...props}>\n        <Link href={`#${id}`}>\n          <a\n            className={`!no-underline !font-extrabold !text-toast-900 *:!text-toast-900`}\n          >\n            {props.children}\n          </a>\n        </Link>\n      </h1>\n    );\n  },\n  h2: (props) => {\n    const id = props.id || '';\n    return (\n      <h2 {...props}>\n        <Link href={`#${id}`}>\n          <a\n            className={`!no-underline !font-semibold !text-toast-800 *:!text-toast-800`}\n          >\n            {props.children}\n          </a>\n        </Link>\n      </h2>\n    );\n  },\n  h3: (props) => {\n    const id = props.id || '';\n    return (\n      <h3 {...props}>\n        <Link href={`#${id}`}>\n          <a\n            className={`!no-underline !font-semibold !text-toast-800 *:!text-toast-800`}\n          >\n            {props.children}\n          </a>\n        </Link>\n      </h3>\n    );\n  },\n  code: (props) =>\n    props.className ? (\n      <Code className={props.className} snippet={props.children} />\n    ) : (\n      <code\n        className=\"bg-toast-300 py-1 my-0.5 px-1 rounded bg-opacity-40\"\n        {...props}\n      />\n    ),\n};\n\nfunction MyApp({ Component, pageProps }) {\n  return (\n    <>\n      <Head>\n        {process.browser && (\n          <script async data-no-cookie data-api=\"/_hive\" src=\"/bee.js\" />\n        )}\n        <link rel=\"shortcut icon\" href=\"/favicon.png\" type=\"image/x-icon\" />\n      </Head>\n      <MDXProvider components={components}>\n        <Component {...pageProps} />\n        <Analytics />\n      </MDXProvider>\n    </>\n  );\n}\n\nexport default MyApp;\n"
  },
  {
    "path": "site/pages/docs/index.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: 'Documentation',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# Getting Started\n\nAdd beautiful notifications to your React app with [react-hot-toast](https://github.com/timolins/react-hot-toast).\n\n### Install with pnpm\n\n```sh\npnpm add react-hot-toast\n```\n\n### Install with NPM\n\n```sh\nnpm install react-hot-toast\n```\n\n## Basic usage\n\n```jsx\nimport toast, { Toaster } from 'react-hot-toast';\n\nconst notify = () => toast('Here is your toast.');\n\nconst App = () => {\n  return (\n    <div>\n      <button onClick={notify}>Make me a toast</button>\n      <Toaster />\n    </div>\n  );\n};\n```\n"
  },
  {
    "path": "site/pages/docs/multi-toaster.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast, { Toaster } from 'react-hot-toast';\n\nexport const meta = {\n  title: 'Multiple Toasters',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# Multiple Toasters\n\nReact Hot Toast supports multiple toaster instances in your app, They can be used and configured independently of each other. This is useful for having notifications in different areas of your app.\n\nYou can use multiple toasters by creating a [`Toaster`](/docs/toaster) with a unique `toasterId`:\n\n```jsx\n<Toaster toasterId=\"sidebar\" />\n```\n\n## Example\n\nThis example shows two toasters, each maintaining their own state and configuration.\n\n<div className=\"not-prose flex gap-4 flex-col md:flex-row my-4\">\n  <div className=\"relative min-h-[200px] bg-toast-200 text-toast-800 rounded-lg p-4 overflow-hidden flex-1 flex flex-col gap-2\">\n    <p className=\"text-lg flex-1 text-center text-toast-300 flex items-center justify-center\">Area 1</p>\n    <Toaster\n      toasterId=\"area1\"\n      position=\"top-center\"\n      containerStyle={{ position: 'absolute' }}\n    />\n    <button\n      onClick={() => toast('Notification for Area 1', { toasterId: 'area1' })}\n      className=\"bg-toast-600 text-white px-4 py-2 rounded-lg hover:bg-toast-600 w-full\"\n    >\n      Show Toast in Area 1\n    </button>\n  </div>\n\n  <div className=\"relative min-h-[200px] rounded-lg p-4 overflow-hidden flex-1 flex flex-col gap-2\" style={{ backgroundColor: 'rgba(154, 134, 253, 0.15)' }}>\n    <p className=\"text-lg flex-1 text-center text-[#876fff84] flex items-center justify-center\">Area 2</p>\n    <Toaster\n      toasterId=\"area2\"\n      position=\"top-center\"\n      containerStyle={{ position: 'absolute' }}\n      toastOptions={{\n        className: '!text-white px-4 py-2 border !rounded-full',\n        style: {\n          backgroundColor: 'rgb(154, 134, 253)',\n          borderColor: 'rgba(154, 134, 253, 0.3)'\n        }\n      }}\n    />\n    <button\n      onClick={() => toast('Notification for Area 2', { toasterId: 'area2' })}\n      className=\"text-white px-4 py-2 rounded-lg bg-[#9a86fd] w-full\"\n    >\n      Show Toast in Area 2\n    </button>\n  </div>\n</div>\n\n## Basic Usage\n\nYou can create multiple toasters providing unique `toasterId` to each `<Toaster />` component:\n\n```jsx\n// Create a toaster with a unique id\n<Toaster toasterId=\"area1\" />\n\n// Create another toaster with a unique id\n<Toaster toasterId=\"area2\" toastOptions={{ ... }} />\n```\n\nTo create a toast in a specific toaster, you can pass the `toasterId` to the `toast` function.\n\n```jsx\n// Create a toast in area 1\ntoast('Notification for Area 1', {\n  toasterId: 'area1',\n});\n```\n\nWhen no `toasterId` is provided, it uses `\"default\"` as the `toasterId`.\n\n### Positioning the toaster\n\nWhen placing a toaster in a specific area of your app, set the position to `absolute` and the parent element to `relative`.\n\n```jsx\n<div style={{ position: 'relative' }}>\n  <Toaster\n    toasterId=\"area1\"\n    position=\"top-center\"\n    containerStyle={{ position: 'absolute' }}\n  />\n</div>\n```\n"
  },
  {
    "path": "site/pages/docs/styling.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: 'Styling',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# Styling\n\nYou can style your notifications globally with the `toastOptions` inside the Toaster component, or for each notification manually.\n\n### Set default for all toasts\n\n```jsx\n<Toaster\n  toastOptions={{\n    className: '',\n    style: {\n      border: '1px solid #713200',\n      padding: '16px',\n      color: '#713200',\n    },\n  }}\n/>\n```\n\n### Set default for specific types\n\n```jsx\n<Toaster\n  toastOptions={{\n    success: {\n      style: {\n        background: 'green',\n      },\n    },\n    error: {\n      style: {\n        background: 'red',\n      },\n    },\n  }}\n/>\n```\n\n### Style per toast\n\n```jsx\ntoast('I have a border.', {\n  style: {\n    border: '1px solid black',\n  },\n});\n```\n\n## Change the offset\n\nIf you want to change the offset of your notifications, you can adapt the absolute position in `containerStyle`.\n\n```jsx\n<Toaster\n  containerStyle={{\n    top: 20,\n    left: 20,\n    bottom: 20,\n    right: 20,\n  }}\n/>\n```\n\n## Change position of the toaster\n\nBy default, the toaster is position fixed in the window. If you want to place it somewhere else, you can overwrite the position with `containerStyle`.\n\n```jsx\n<Toaster\n  containerStyle={{\n    position: 'relative',\n  }}\n/>\n```\n\n## Change offset between toasts\n\nIf you want to change the offset between notifications change the gutter.\n\n```jsx\n<Toaster gutter={24} />\n```\n\n## Change icon color\n\nAll icon colors can be changed by supplying a `iconTheme` with a `primary` & `secondary` color.\n\n```jsx\n<Toaster\n  toastOptions={{\n    success: {\n      iconTheme: {\n        primary: 'green',\n        secondary: 'black',\n      },\n    },\n  }}\n/>\n```\n\n## Change enter and exit animations\n\nIn this example, we provide a render function with the default `<ToastBar />`. We overwrite the animation style based on the current state.\n\n```jsx\nimport { Toaster, ToastBar } from 'react-hot-toast';\n\n<Toaster>\n  {(t) => (\n    <ToastBar\n      toast={t}\n      style={{\n        ...t.style,\n        animation: t.visible\n          ? 'custom-enter 1s ease'\n          : 'custom-exit 1s ease forwards',\n      }}\n    />\n  )}\n</Toaster>;\n```\n"
  },
  {
    "path": "site/pages/docs/toast-bar.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: '<ToastBar/> API',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# `<ToastBar />` API\n\nThis is the **default toast component** rendered by the [Toaster](/docs/toaster). You can use this component in a [Toaster](/docs/toaster) with a custom render function to overwrite its defaults.\n\n## Available options\n\n```jsx\n<ToastBar\n  toast={t}\n  style={{}} // Overwrite styles\n  position=\"top-center\" // Used to adapt the animation\n/>\n```\n\n## Add custom content\n\nYou can add a **render function to the ToastBar to modify its content**. An object containing The `icon` as well as the `message` are passed into the function.\n\n### Add a dismiss button\n\nIn this example we add a basic dismiss button to all toasts, except if the loading one.\n\n```jsx\nimport { toast, Toaster, ToastBar } from 'react-hot-toast';\n\n<Toaster>\n  {(t) => (\n    <ToastBar toast={t}>\n      {({ icon, message }) => (\n        <>\n          {icon}\n          {message}\n          {t.type !== 'loading' && (\n            <button onClick={() => toast.dismiss(t.id)}>X</button>\n          )}\n        </>\n      )}\n    </ToastBar>\n  )}\n</Toaster>;\n```\n"
  },
  {
    "path": "site/pages/docs/toast.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: 'toast() API',\n};\n\n# `toast()` API\n\nCall it to create a toast from anywhere, even outside React. Make sure you add the [`<Toaster/>`](/docs/toaster) component to your app first.\n\n## Available toast options\n\nYou can provide `ToastOptions` as the second argument. They will overwrite all options received from [`<Toaster/>`](/docs/toaster).\n\n```js\ntoast('Hello World', {\n  duration: 4000,\n  position: 'top-center',\n\n  // Styling\n  style: {},\n  className: '',\n\n  // Custom Icon\n  icon: '👏',\n\n  // Change colors of success/error/loading icon\n  iconTheme: {\n    primary: '#000',\n    secondary: '#fff',\n  },\n\n  // Aria\n  ariaProps: {\n    role: 'status',\n    'aria-live': 'polite',\n  },\n\n  // Additional Configuration\n  removeDelay: 1000,\n\n  // Toaster instance\n  toasterId: 'default',\n});\n```\n\n## Creating a toast\n\n### Blank\n\n```js\ntoast('Hello World');\n```\n\nThe most basic variant. It does not have an icon by default, but you can provide one via the options. If you don't want any default styles, use `toast.custom()` instead.\n\n### Success\n\n```js\ntoast.success('Successfully created!');\n```\n\nCreates a notification with an animated checkmark. It can be themed with the `iconTheme` option.\n\n### Error\n\n```js\ntoast.error('This is an error!');\n```\n\nCreates a notification with an animated error icon. It can be themed with the `iconTheme` option.\n\n### Custom (JSX)\n\n```js\ntoast.custom(<div>Hello World</div>);\n```\n\nCreates a custom notification with JSX without default styles.\n\n### Loading\n\n```js\ntoast.loading('Waiting...');\n```\n\nThis will create a loading notification. Most likely, you want to update it afterwards. For a friendly alternative, check out `toast.promise()`, which takes care of that automatically.\n\n### Promise\n\nThis shorthand is useful for mapping a promise to a toast. It will update automatically when the promise resolves or fails.\n\n#### Simple Usage\n\n```js\nconst myPromise = fetchData();\n\ntoast.promise(myPromise, {\n  loading: 'Loading',\n  success: 'Got the data',\n  error: 'Error when fetching',\n});\n```\n\nIt's recommend to add min-width to your `toast.promise()` calls to **prevent jumps** from different message lengths.\n\n#### Advanced\n\nYou can provide a function to the success/error messages to incorporate the result/error of the promise. The third argument are `toastOptions` similiar to [`<Toaster />`](/docs/toaster)\n\n```js\ntoast.promise(\n  myPromise,\n  {\n    loading: 'Loading',\n    success: (data) => `Successfully saved ${data.name}`,\n    error: (err) => `This just happened: ${err.toString()}`,\n  },\n  {\n    style: {\n      minWidth: '250px',\n    },\n    success: {\n      duration: 5000,\n      icon: '🔥',\n    },\n  }\n);\n```\n\n#### Using an Async Function\n\nYou can also provide a function that returns a promise, which will be called automatically.\n\n```js\ntoast.promise(\n  async () => {\n    const { id } = await fetchData1();\n    await fetchData2(id);\n  },\n  {\n    loading: 'Loading',\n    success: 'Got the data',\n    error: 'Error when fetching',\n  }\n);\n```\n\n## Default durations\n\nEvery type has its own duration. You can overwrite them `duration` with the toast options. This can be done per toast options or globally by the [`<Toaster/>`](/docs/toaster).\n\n| type      | duration |\n| --------- | -------- |\n| `blank`   | 4000     |\n| `error`   | 4000     |\n| `success` | 2000     |\n| `custom`  | 4000     |\n| `loading` | Infinity |\n\n### Dismiss toast programmatically\n\nYou can manually dismiss a notification with `toast.dismiss`. Be aware that it triggers the exit animation and does not remove the Toast instantly. Toasts will auto-remove after 1 second by default.\n\n#### Dismiss a single toast\n\n```js\nconst toastId = toast.loading('Loading...');\n\n// ...\n\ntoast.dismiss(toastId);\n```\n\nYou can dismiss all toasts at once, by leaving out the `toastId`.\n\n#### Dismiss all toasts at once\n\n```js\ntoast.dismiss();\n```\n\nTo remove toasts instantly without any animations, use `toast.remove`.\n\n#### Configure remove delay\n\n```js\ntoast.success('Successfully created!', { removeDelay: 500 });\n```\n\nBy default, the remove operation is delayed by 1000ms. This is how long a toast should be kept in the DOM after being dismissed. It is used to play the exit animation. This duration (number in milliseconds) can be configured when calling the toast.\n\nOr, for all toasts, using the Toaster like so:\n\n```js\n<Toaster\n  toastOptions={{\n    removeDelay: 500,\n  }}\n/>\n```\n\n#### Remove toasts instantly\n\n```js\ntoast.remove(toastId);\n\n// or\n\ntoast.remove();\n```\n\n### Update an existing toast\n\nEach toast call returns a unique id. Use in the toast options to update the existing toast.\n\n```js\nconst toastId = toast.loading('Loading...');\n\n// ...\n\ntoast.success('This worked', {\n  id: toastId,\n});\n```\n\n### Prevent duplicate toasts\n\nTo prevent duplicates of the same kind, you can provide a unique permanent id.\n\n```js\ntoast.success('Copied to clipboard!', {\n  id: 'clipboard',\n});\n```\n\n### Render JSX custom content\n\nYou can provide a React component instead of text. If you don't want any default styles use `toast.custom()` instead.\n\n```jsx\ntoast(\n  <span>\n    Custom and <b>bold</b>\n  </span>,\n  {\n    icon: <Icon />,\n  }\n);\n```\n\nYou can also supply a function that receives the `Toast` as an argument, giving you access to all properties. This allows you to access the toast id, which can be used to add a dismiss button.\n\n```jsx\ntoast(\n  (t) => (\n    <span>\n      Custom and <b>bold</b>\n      <button onClick={() => toast.dismiss(t.id)}>Dismiss</button>\n    </span>\n  ),\n  {\n    icon: <Icon />,\n  }\n);\n```\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n"
  },
  {
    "path": "site/pages/docs/toaster.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: '<Toaster/> API',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# `<Toaster />` API\n\nThis component will render all toasts. Alternatively you can create own renderer with the headless [`useToaster()`](/docs/use-toaster) hook.\n\n## Available options\n\n```jsx\n<Toaster\n  position=\"top-center\"\n  reverseOrder={false}\n  gutter={8}\n  containerClassName=\"\"\n  containerStyle={{}}\n  toasterId=\"default\"\n  toastOptions={{\n    // Define default options\n    className: '',\n    duration: 5000,\n    removeDelay: 1000,\n    style: {\n      background: '#363636',\n      color: '#fff',\n    },\n\n    // Default options for specific types\n    success: {\n      duration: 3000,\n      iconTheme: {\n        primary: 'green',\n        secondary: 'black',\n      },\n    },\n  }}\n/>\n```\n\n### `position` Prop\n\nYou can change the position of all toasts by modifying supplying `positon` prop.\n\n| Positions   |               |              |\n| ----------- | ------------- | ------------ |\n| top-left    | top-center    | top-right    |\n| bottom-left | bottom-center | bottom-right |\n\n### `reverseOrder` Prop\n\nToasts spawn at top by default. Set to `true` if you want new toasts at the end.\n\n### `containerClassName` Prop\n\nAdd a custom CSS class name to toaster div. Defaults to `undefined`.\n\n### `containerStyle` Prop\n\nCustomize the style of toaster div. This can be used to change the offset of all toasts\n\n### `gutter` Prop\n\nChanges the gap between each toast. Defaults to `8`.\n\n### `toasterId` Prop\n\nYou can change the toasterId to have a different toaster instance. Learn more about [multiple toasters](/docs/multi-toaster). Defaults to `\"default\"`.\n\n### `toastOptions` Prop\n\nThese will act as default options for all toasts. See [`toast()`](/docs/toast) for all available options.\n\n#### Type specific options\n\nYou can change the defaults for a specific type by adding, `success: {}`, `error: {}`, `loading: {}` or `custom: {}`.\n\n## Using a custom render function\n\nYou can provide your **own render function** to the Toaster by passing it as children. It will be called for each [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) allowing you to render any component based on the toast state.\n\n### Minimal example\n\n```jsx\nimport { Toaster, resolveValue } from 'react-hot-toast';\n\n// In your app\n<Toaster>\n  {(t) => (\n    <div\n      style={{ opacity: t.visible ? 1 : 0, background: 'white', padding: 8 }}\n    >\n      {resolveValue(t.message, t)}\n    </div>\n  )}\n</Toaster>;\n```\n\n`resolveValue()` is needed to resolve all message types: Text, JSX or a function that resolves to JSX.\n\n### Adapting the default [`<ToastBar/>`](/docs/toast-bar)\n\nYou can use this API to modify the default ToastBar as well. In this example we overwrite the animation style based on the current state.\n\n```jsx\nimport { Toaster, ToastBar } from 'react-hot-toast';\n\n<Toaster>\n  {(t) => (\n    <ToastBar\n      toast={t}\n      style={{\n        ...t.style,\n        animation: t.visible\n          ? 'custom-enter 1s ease'\n          : 'custom-exit 1s ease forwards',\n      }}\n    />\n  )}\n</Toaster>;\n```\n\nCheck out the [`<ToastBar/>`](/docs/toast-bar) docs for more options.\n"
  },
  {
    "path": "site/pages/docs/use-toaster-store.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: 'useToasterStore() API',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# `useToasterStore()` API\n\nThis hook gives you access to the internal toaster state. This is the right choice if you need access to the data without wanting to roll your own toaster.\n\nIn comparison to [`useToaster()`](/docs/use-toaster) it does not handle pausing or provide handlers for creating your own notification system.\n\n```jsx\nimport { useToasterStore } from 'react-hot-toast';\n\nconst { toasts, pausedAt } = useToasterStore();\n```\n"
  },
  {
    "path": "site/pages/docs/use-toaster.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: 'useToaster() API',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# `useToaster()` API\n\nThe `useToaster()` hook provides a **headless toast management system** for building custom notification UIs. It manages toast state and lifecycle without rendering any components.\n\nIt handles pausing on hover, auto-removal, and provides a 1-second removal delay with `visible` flag for smooth animations.\n\n**Alternative**: Use [`useToasterStore()`](/docs/use-toaster-store) if you already have a toaster instance and only need the state.\n\n### Importing\n\n```jsx\nimport { useToaster } from 'react-hot-toast';\n```\n\nYou can also import from the headless entry point to exclude UI components:\n\n```jsx\nimport { useToaster } from 'react-hot-toast/headless';\n```\n\n**Note**: [React Hot Toast 2.0](/docs/version-2) includes **custom render functions** for easier custom components.\n\n## API Reference\n\n### Parameters\n\n```tsx\nuseToaster(\n  toastOptions?: DefaultToastOptions,\n  toasterId?: string\n)\n```\n\n| Parameter      | Type                  | Default     | Description                                     |\n| -------------- | --------------------- | ----------- | ----------------------------------------------- |\n| `toastOptions` | `DefaultToastOptions` | `undefined` | Default options for all toasts in this instance |\n| `toasterId`    | `string`              | `'default'` | Unique identifier for this toaster instance     |\n\n### Returns\n\n```tsx\n{\n  toasts: Toast[];\n  handlers: {\n    startPause: () => void;\n    endPause: () => void;\n    updateHeight: (toastId: string, height: number) => void;\n    calculateOffset: (toast: Toast, options?: OffsetOptions) => number;\n  };\n}\n```\n\n#### `toasts`\n\nArray of all toasts in this toaster instance, including hidden ones for animation purposes.\n\n#### `handlers`\n\n- **`startPause()`**: Pause all toast timers (useful for hover states)\n- **`endPause()`**: Resume all toast timers\n- **`updateHeight(toastId, height)`**: Update toast height for offset calculations\n- **`calculateOffset(toast, options)`**: Calculate vertical offset for toast positioning\n\n## Multiple Toasters\n\nYou can create multiple independent toaster instances by providing a unique `toasterId`. See the [Multiple Toasters](/docs/multi-toaster) guide for detailed examples.\n\n```jsx\nconst sidebar = useToaster({ duration: 5000 }, 'sidebar');\ntoast('Sidebar notification', { toasterId: 'sidebar' });\n```\n\n## Examples\n\n### Basic Implementation\n\n```jsx\nimport toast, { useToaster } from 'react-hot-toast/headless';\n\nconst Notifications = () => {\n  const { toasts, handlers } = useToaster();\n  const { startPause, endPause } = handlers;\n\n  return (\n    <div onMouseEnter={startPause} onMouseLeave={endPause}>\n      {toasts\n        .filter((toast) => toast.visible)\n        .map((toast) => (\n          <div key={toast.id} {...toast.ariaProps}>\n            {toast.message}\n          </div>\n        ))}\n    </div>\n  );\n};\n\n// Create toasts from anywhere\ntoast('Hello World');\n```\n\n### Animated Implementation\n\nThis example uses all `toasts` (including hidden ones) to enable smooth animations. The `toast.visible` property controls opacity, while the 1-second removal delay provides time for exit animations.\n\n**Live Demo**: [CodeSandbox](https://codesandbox.io/s/react-hot-toast-usetoaster-headless-example-zw7op?file=/src/App.js)\n\n```jsx\nimport { useToaster } from 'react-hot-toast/headless';\n\nconst AnimatedNotifications = () => {\n  const { toasts, handlers } = useToaster();\n  const { startPause, endPause, calculateOffset, updateHeight } = handlers;\n\n  return (\n    <div\n      style={{\n        position: 'fixed',\n        top: 8,\n        left: 8,\n      }}\n      onMouseEnter={startPause}\n      onMouseLeave={endPause}\n    >\n      {toasts.map((toast) => {\n        const offset = calculateOffset(toast, {\n          reverseOrder: false,\n          gutter: 8,\n        });\n\n        const ref = (el) => {\n          if (el && typeof toast.height !== 'number') {\n            const height = el.getBoundingClientRect().height;\n            updateHeight(toast.id, height);\n          }\n        };\n\n        return (\n          <div\n            key={toast.id}\n            ref={ref}\n            style={{\n              position: 'absolute',\n              width: '200px',\n              background: 'papayawhip',\n              transition: 'all 0.5s ease-out',\n              opacity: toast.visible ? 1 : 0,\n              transform: `translateY(${offset}px)`,\n            }}\n            {...toast.ariaProps}\n          >\n            {toast.message}\n          </div>\n        );\n      })}\n    </div>\n  );\n};\n```\n\n\n## Usage with React Native\n\nThe headless API works perfectly with React Native. View the [React Native example](<https://snack.expo.io/@timo/react-hot-toast---usetoaster()---react-native>) for implementation details.\n"
  },
  {
    "path": "site/pages/docs/version-2.mdx",
    "content": "import Layout from '../../components/docs-layout';\nimport toast from 'react-hot-toast';\n\nexport const meta = {\n  title: 'react-hot-toast 2.0 changes',\n};\n\nexport default ({ children }) => <Layout meta={meta}>{children}</Layout>;\n\n# What's new in react-hot-toast 2.0\n\nThis release is all about **flexibility**. It allows you to create the notification system of your dreams, even simpler. Before we dig deeper into the new APIs, check out what's included in this release:\n\n<div className=\"\">\n<span style={{ margin: '10px' }}>✨</span> <a href=\"#introducing-toastcustom\" className=\"font-semibold\">toast.custom()</a> <span className=\"\">- Dispatch components as toast</span>\n<br />\n\n<span style={{ margin: '10px' }}>👀</span>\n<a href=\"#better-accessibility\" className=\"font-semibold\">Reduced-motion support</a> <span className=\"\">- Automatically adapts to system preference</span>\n<br />\n\n<span style={{ margin: '10px' }}>🔀</span>\n<a href=\"#per-toast-positioning\" className=\"font-semibold\">Individually position toasts</a> <span className=\"\"> </span>\n<br />\n\n<span style={{ margin: '10px' }}>🧈</span>\n<a href=\"#smoother-exit-animation\" className=\"font-semibold\">Smoother exit animations</a> <span className=\"\"></span>\n<br />\n\n<span style={{ margin: '10px' }}>⚙️</span> <a href=\"#custom-renderer-api\" className=\"font-semibold\">Custom Renderer API</a> <span className=\"\">- Supply your own render function</span>\n</div>\n\nAs well as a many [other improvements and fixes](#changelog).\n\n## Introducing `toast.custom()`\n\nThis new function allows you to **render any React component** on the fly. Pass in JSX, and it will add it to the notification stack. There are no default styles applied, giving you complete control.\n\nThis API makes it super easy to add [Tailwind UI Notifications](https://tailwindui.com/components/application-ui/overlays/notifications) to your React app.\n\n```jsx\n// Minimal Example\ntoast.custom(<div>Minimal Example</div>);\n\n// Tailwind Example\ntoast.custom((t) => (\n  <div\n    className={`bg-white px-6 py-4 shadow-md rounded-full ${\n      t.visible ? 'animate-custom-enter' : 'animate-custom-leave'\n    }`}\n  >\n    Hello TailwindCSS! 👋\n  </div>\n));\n```\n\n<div className=\"w-full relative\">\n  <button\n    onClick={() => \n      toast.custom((t) => (\n        <div\n          className={`bg-white px-6 py-4 shadow-md rounded-full ${\n            t.visible ? 'animate-custom-enter' : 'animate-custom-leave'\n          }`}\n        >Hello from TailwindCSS! 👋</div>\n      ))\n    }\n    className=\"bg-toast-800 text-toast-100 whitespace-nowrap py-1 px-3 shadow-md rounded-lg absolute mt-[-4.5rem] -ml-2 transform -translate-x-full left-full\">Run Example</button>\n</div>\n\nIn the example above, we pass in a **function that returns JSX**. This allows us to access the current toast state and toggle between the enter and exit animation.\n\nInstead of CSS keyframe animations, you can use TailwindCSS classes by wrapping it in the [Transition](https://headlessui.dev/react/transition) component from [@headlessui/react](https://headlessui.dev/).\n\n## Better accessibility\n\nThe prefers reduced motion is now respected by default. If react-hot-toast detects this setting, it will use fade transitions instead of sliding.\n\n## Smoother exit animation\n\nThe exit animation is now less hectic when you have multiple toasts stacked.\n\n## Per toast positioning\n\nFrom now on, it's possible to have toasts at multiple positions at once. Just add the `position` you want as option when dispatching a toast.\n\n```jsx\ntoast.success('Always at the bottom', {\n  position: 'bottom-center',\n});\n```\n\n<div className=\"w-full relative\">\n  <button\n    className=\"bg-toast-800 text-toast-100 whitespace-nowrap py-1 px-3 shadow-md rounded-lg absolute mt-[-4.5rem] -ml-2 transform -translate-x-full left-full\"\n    onClick={() => {\n      toast.success('Always at the bottom', {\n        position: 'bottom-center',\n      });\n    }}>Run Example</button>\n</div>\n\n## Relative positioning\n\nYou can now overwrite the default position of the toaster and place it anywhere you want.\n\n```jsx\n<Toaster containerStyle={{ position: 'absolute' }} />\n```\n\n## Simpler offset styling\n\nThere is now a `gutter` option to control the gap between toasts.\n\n```jsx\n<Toaster gutter={30} />\n```\n\nThe offset is now controlled by the Toaster and can be changed by overwriting the `top`, `right`, `bottom` and `left` styles.\n\n```jsx\n<Toaster containerStyle={{ top: '8px' }} />\n```\n\n## Custom Renderer API\n\nYou can now use the [`<Toaster/>`](/docs/toaster#using-a-custom-render-function) to render your own components. Pass in a function that receives a [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) as the first argument, allowing you to render whatever you please.\n\nThis is a great alternative if you are using [`useToaster()`](/docs/use-toaster) to render create custom notfications.\n\nThis API allows us to dynamically react to the current state of your toasts. This can be used to **change the default animations**, add **a custom dismiss button** or render a custom notification, like [TailwindUI Notifications](https://tailwindui.com/components/application-ui/overlays/notifications).\n\n```jsx\nimport { toast, Toaster, ToastBar } from 'react-hot-toast';\n\nconst CustomToaster = () => (\n  <Toaster>\n    {(t) => (\n      <ToastBar toast={t}>\n        {({ icon, message }) => (\n          <>\n            {icon}\n            {message}\n            {t.type !== 'loading' && (\n              <button onClick={() => toast.dismiss(t.id)}>X</button>\n            )}\n          </>\n        )}\n      </ToastBar>\n    )}\n  </Toaster>\n);\n```\n\nThis example adapts the [ToastBar](/docs/toast-bar) with its new render function API. You can read more about the APIs in the [Toaster](/docs/toaster) & [ToastBar](/docs/toast-bar) docs.\n\n## Available now\n\nGet react-hot-toast 2.0 while it's hot. Upgrading from 1.0.0 should be seamless for most users.\n\n```sh\npnpm add react-hot-toast\n```\n\n## The future and beyond\n\nReact Hot Toast got a lot more flexible with this version, laying the **foundation for future releases**. Thanks to everyone who helped out; much appreciated!\n\nIn the next releases, I plan to add the [most requested feature](https://github.com/timolins/react-hot-toast/issues/7): a dismiss button. As well as support for [custom toast types](https://github.com/timolins/react-hot-toast/issues/23).\n\n---\n\n## Changelog\n\n### New\n\n- Easier Customization\n  - Create your own toast renderer (without useToaster)\n    - Support for custom render function in Toaster\n    - Support for custom render function in ToastBar\n  - `toast.custom()` - Render custom one-off toasts. No default styling will be applied.\n- Per toast positioning\n- New exit animation\n- Change the gutter between toasts with `<Toaster gutter={20} />`\n- Support for relative positioning\n- Respect reduce motion OS setting\n- Create persistent toasts with `duration: Infinity`\n\n### Breaking Changes\n\n- Use the `top`, `right`, `bottom`, `left` to in `containerStyle` to change the offset, instead of margin\n- Loading toasts no longer disappear after 30 seconds\n- `role` & `ariaLive` got moved into `ariaProps`\n- `useToaster()` no longer exposes `visibleToasts`\n- No longer expose `dispatch`\n"
  },
  {
    "path": "site/pages/index.tsx",
    "content": "import { NextSeo } from 'next-seo';\nimport toast, {\n  Toaster,\n  useToasterStore,\n  ToastPosition,\n} from 'react-hot-toast';\nimport React, { useState } from 'react';\nimport clsx from 'clsx';\nimport Link from 'next/link';\n\nimport Logo from '../assets/logo.svg';\nimport Butter1 from '../assets/butter-1.svg';\nimport Butter2 from '../assets/butter-2.svg';\nimport GitHub from '../assets/github.svg';\nimport Checkmark from '../assets/checkmark.svg';\nimport { ToastExample } from '../components/sections/toast-example';\nimport { Footer } from '../components/sections/footer';\nimport { ToasterExample } from '../components/sections/toaster-example';\nimport { SplitbeeCounter } from '../components/sections/splitbee-counter';\n\nimport packageInfo from '../../package.json';\nconst version = packageInfo.version;\n\nconst Feature: React.FC<{ children?: React.ReactNode }> = ({ children }) => (\n  <div className=\"flex gap-1 items-center\">\n    <Checkmark />\n    <span className=\"font-bold\">{children}</span>\n  </div>\n);\n\nconst Step: React.FC<{\n  count: number;\n  title: string;\n  subTitle: string;\n  code: React.ReactElement;\n}> = (props) => (\n  <div className=\"flex flex-col gap-1 items-center\">\n    <div className=\"h-6 w-6 mb-2 text-sm rounded-full bg-toast-900 text-toast-50 flex items-center justify-center\">\n      {props.count}\n    </div>\n    <div className=\"font-bold\">{props.title}</div>\n    <div className=\"text-red-700 text-sm\">{props.subTitle}</div>\n    <code className=\"mt-2 border border-toast-200 py-2 px-4 rounded font-bold bg-white w-full text-center\">\n      {props.code}\n    </code>\n  </div>\n);\n\nconst Steps = () => (\n  <div className=\"grid  grid-cols-1 md:grid-cols-3 gap-x-4 gap-y-8 my-12\">\n    <Step\n      count={1}\n      title=\"Install package\"\n      subTitle=\"It weighs less than 5kb\"\n      code={\n        <code>\n          <span className=\"text-toast-600\">pnpm add</span>{' '}\n          <span className=\"text-toast-800\">react-hot-toast</span>\n        </code>\n      }\n    ></Step>\n    <Step\n      count={2}\n      title=\"Add Toaster to your app\"\n      subTitle=\"Make sure it's placed at the top\"\n      code={\n        <>\n          <span className=\"text-toast-600\">{'<div>'}</span>\n          <span className=\"text-toast-800\">{'<Toaster/>'}</span>\n          <span className=\"text-toast-600\">{'</div>'}</span>\n        </>\n      }\n    ></Step>\n    <Step\n      count={3}\n      title=\"Start toasting!\"\n      subTitle=\"Call it from anywhere\"\n      code={\n        <>\n          <span className=\"text-toast-600\">{'toast'}</span>\n          <span className=\"text-toast-800\">{'(\"Hello World\")'}</span>\n        </>\n      }\n    ></Step>\n  </div>\n);\n\nconst Features = () => (\n  <div className=\"my-12 grid gap-x-8 gap-y-5 grid-cols-2 md:grid-cols-3\">\n    <Feature>Hot by default</Feature>\n    <Feature>Easy to use</Feature>\n    <Feature>Accessible</Feature>\n    <Feature>Emoji Support</Feature>\n    <Feature>Customizable</Feature>\n    <Feature>Promise API</Feature>\n    <Feature>Lightweight</Feature>\n    <Feature>Pause on hover</Feature>\n    <Feature>Headless Hooks</Feature>\n  </div>\n);\n\nexport default function Home() {\n  const [position, setPosition] = useState<ToastPosition>('top-center');\n  const [reverse, setReverse] = useState(false);\n  const { toasts: allToasts } = useToasterStore();\n\n  const shouldFade =\n    allToasts.filter((t) => t.visible).length && position.includes('top');\n  return (\n    <div className=\"overflow-x-hidden\">\n      <NextSeo\n        title={'react-hot-toast - The Best React Notifications in Town'}\n        openGraph={{\n          images: [\n            {\n              url: `https://react-hot-toast.com/social-image.png`,\n              width: 1200,\n              height: 630,\n            },\n          ],\n        }}\n        description=\"Add beautiful notifications to your React app with react-hot-toast. Lightweight. Smoking hot by default.\"\n      />\n      <header className=\"bg-gradient-to-b from-toast-50 to-white bg-opacity-10\">\n        <div className=\"container  flex flex-col items-center relative\">\n          <Butter1\n            className=\"absolute -left-24 md:left-24 transition-opacity duration-200\"\n            style={{\n              opacity: shouldFade ? 0.5 : 1,\n            }}\n          />\n\n          <div>\n            <Logo\n              role=\"img\"\n              aria-label=\"react-hot-toast\"\n              className=\"relative animate-slide-in transition-all duration-200 -mt-8 md:-mt-4\"\n              style={{\n                willChange: 'filter',\n                opacity: shouldFade ? 0.2 : 1,\n                filter: `blur(${shouldFade ? 6 : 0}px)`,\n              }}\n            />\n          </div>\n          <div className=\"text-center my-12 relative duration-200\">\n            <h1 className=\"text-3xl md:text-4xl animate-custom-enter font-bold text-toast-900\">\n              The Best Toast in Town.\n            </h1>\n            <h2 className=\"text-xl md:text-2xl font-bold text-toast-600 mt-2\">\n              Smoking hot React notifications.\n            </h2>\n          </div>\n\n          <div className=\"grid md:grid-cols-2 gap-4 rounded-2xl bg-toast-200 p-4 w-full max-w-lg\">\n            <button\n              data-splitbee-event=\"Trigger Toast\"\n              data-splitbee-event-example=\"CTA\"\n              className={clsx(\n                'rounded-lg font-bold gap-4 flex bg-gradient-to-b from-white to-toast-200 shadow-button text-center',\n                'py-4 px-6',\n                'active:translate-y-0.5 active:shadow-button-active active:bg-gray-100 transform',\n                'focus:outline-none focus:ring-4'\n              )}\n              style={{\n                transitionProperty: 'box-shadow, transform',\n              }}\n              onClick={() => {\n                const promise = new Promise((res, rej) => {\n                  if (Math.random() < 0.85) {\n                    setTimeout(res, 1000);\n                  } else {\n                    setTimeout(rej, 3000);\n                  }\n                });\n\n                toast.promise(\n                  promise,\n                  {\n                    loading: 'Preparing toast',\n                    error: 'Whoops, it burnt',\n                    success: \"Here's your toast\",\n                  },\n                  {\n                    style: {\n                      width: '200px',\n                      paddingRight: '10px',\n                    },\n                  }\n                );\n              }}\n            >\n              <div>🛎 </div>\n              <span className=\"flex-1 mr-2\">Make me a toast</span>\n            </button>\n            <a\n              className={clsx(\n                'rounded-lg flex font-bold bg-white py-4 px-6 shadow-button  text-toast-800',\n                'active:translate-y-0.5 active:shadow-button-active transform'\n              )}\n              style={{\n                transitionProperty: 'box-shadow, transform',\n              }}\n              data-splitbee-event=\"Open Link\"\n              data-splitbee-event-target=\"GitHub\"\n              onClick={() => {}}\n              href=\"https://github.com/timolins/react-hot-toast\"\n            >\n              <GitHub className=\"opacity-100\" />\n              <span className=\"flex-1 text-toast-800 text-center\">GitHub</span>\n            </a>\n          </div>\n          <div className=\"text-toast-600 my-2\">\n            <Link href=\"/docs\">\n              <a className=\"underline\">Documentation</a>\n            </Link>\n            {' · '}\n            <span>v{version}</span>\n          </div>\n\n          <Features />\n          <Steps />\n          <div className=\"w-full max-w-4xl\">\n            <div className=\"my-14\">\n              <h2 className=\"ml-5 text-2xl font-bold\">Examples</h2>\n              <ToastExample />\n            </div>\n            <div className=\"my-14\">\n              <h2 className=\"ml-5 mr-5 mb-4 text-2xl font-bold\">\n                Change Position\n              </h2>\n\n              <ToasterExample\n                onReverse={setReverse}\n                reverse={reverse}\n                position={position}\n                onPosition={setPosition}\n              />\n            </div>\n          </div>\n        </div>\n      </header>\n      <SplitbeeCounter />\n      <Toaster position={position} reverseOrder={reverse} toastOptions={{}} />\n      <div className=\"container flex justify-end -mt-10 pointer-events-none\">\n        <Butter2 className=\"transform translate-x-20\" />\n      </div>\n      <Footer noBadge />\n    </div>\n  );\n}\n"
  },
  {
    "path": "site/postcss.config.js",
    "content": "module.exports = {\n  plugins: ['tailwindcss'],\n};\n"
  },
  {
    "path": "site/styles/main.css",
    "content": "@tailwind base;\n@tailwind components;\n\nhtml,\nbody,\nbody > div {\n  @apply flex flex-col justify-between flex-1 min-h-full text-toast-900;\n}\n\nbutton,\na {\n  @apply outline-none focus:outline-none ring-offset-green-900 ring-toast-900 ring-opacity-30 transition-shadow duration-100 focus:ring-4;\n}\n"
  },
  {
    "path": "site/styles/prism-theme.css",
    "content": "/* Generated with http://k88hudson.github.io/syntax-highlighting-theme-generator/www */\n/* http://k88hudson.github.io/react-markdocs */\n/**\n * @author k88hudson\n *\n * Based on prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou\n */\n/*********************************************************\n* General\n*/\npre[class*=\"language-\"],\ncode[class*=\"language-\"] {\n  color: #a1724e;\n  font-size: 13px;\n  text-shadow: none;\n  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;\n  direction: ltr;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  -o-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  -moz-hyphens: none;\n  -ms-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"]::selection,\ncode[class*=\"language-\"]::selection,\npre[class*=\"language-\"]::mozselection,\ncode[class*=\"language-\"]::mozselection {\n  text-shadow: none;\n  background: #ffff00;\n}\n@media print {\n  pre[class*=\"language-\"],\n  code[class*=\"language-\"] {\n    text-shadow: none;\n  }\n}\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: .5em 0;\n  overflow: auto;\n  background: #faf0de;\n}\n:not(pre) > code[class*=\"language-\"] {\n  padding: .1em .3em;\n  border-radius: .3em;\n  color: #db4c69;\n  background: #f9f2f4;\n}\n/*********************************************************\n* Tokens\n*/\n.namespace {\n  opacity: .7;\n}\n.token.comment,\n.token.prolog,\n.token.doctype,\n.token.cdata {\n  color: #dddddd;\n}\n.token.punctuation {\n  color: #999999;\n}\n.token.property,\n.token.tag,\n.token.boolean,\n.token.number,\n.token.constant,\n.token.symbol,\n.token.deleted {\n  color: #327015;\n}\n.token.selector,\n.token.attr-name,\n.token.string,\n.token.char,\n.token.builtin,\n.token.inserted {\n  color: #54b427;\n}\n.token.operator,\n.token.entity,\n.token.url,\n.language-css .token.string,\n.style .token.string {\n  color: #8c4913;\n  background: transparent;\n}\n.token.atrule,\n.token.attr-value,\n.token.keyword {\n  color: #482307;\n}\n.token.function {\n  color: #381b05;\n}\n.token.regex,\n.token.important,\n.token.variable {\n  color: #ffedc0;\n}\n.token.important,\n.token.bold {\n  font-weight: bold;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n/*********************************************************\n* Line highlighting\n*/\npre[data-line] {\n  position: relative;\n}\npre[class*=\"language-\"] > code[class*=\"language-\"] {\n  position: relative;\n  z-index: 1;\n}\n.line-highlight {\n  position: absolute;\n  left: 0;\n  right: 0;\n  padding: inherit 0;\n  margin-top: 1em;\n  background: #ffe092;\n  box-shadow: inset 5px 0 0 #482307;\n  z-index: 0;\n  pointer-events: none;\n  line-height: inherit;\n  white-space: pre;\n}\n"
  },
  {
    "path": "site/styles/tailwind-utils.css",
    "content": "@tailwind utilities;\n"
  },
  {
    "path": "site/tailwind.config.js",
    "content": "module.exports = {\n  mode: 'jit',\n  content: [\n    './pages/*.tsx',\n    './pages/**/*.tsx',\n    './pages/*.mdx',\n    './pages/**/*.mdx',\n    './components/*.tsx',\n    './components/**/*.tsx',\n  ],\n  theme: {\n    extend: {\n      boxShadow: {\n        'small-button': '0px 1px 2px rgba(126, 56, 0, 0.5)',\n        button:\n          '-6px 8px 10px rgba(81, 41, 10, 0.1), 0px 2px 2px rgba(81, 41, 10, 0.2)',\n        'button-active':\n          '-1px 2px 5px rgba(81, 41, 10, 0.15), 0px 1px 1px rgba(81, 41, 10, 0.15)',\n      },\n      animation: {\n        'custom-enter': 'custom-enter 200ms ease-out',\n        'custom-leave': 'custom-leave 150ms ease-in forwards',\n        'slide-in': 'slide-in 1.2s cubic-bezier(.41,.73,.51,1.02)',\n      },\n      keyframes: {\n        'custom-enter': {\n          '0%': { transform: 'scale(0.9)', opacity: 0 },\n          '100%': { transform: 'scale(1)', opacity: 1 },\n        },\n        'custom-leave': {\n          '0%': { transform: 'scale(1)', opacity: 1 },\n          '100%': { transform: 'scale(0.9)', opacity: 0 },\n        },\n        'slide-in': {\n          '0%': { transform: 'translateY(-100%)' },\n          '100%': { transform: 'translateY(0)' },\n        },\n      },\n\n      colors: {\n        toast: {\n          '50': '#FFF6DF',\n          '100': '#fdf7f1',\n          '200': '#F8EEDB',\n          '300': '#ebbf99',\n          '400': '#dea373',\n          '500': '#ce864f',\n          '600': '#A1724E',\n          '700': '#8c501c',\n          '800': '#5c340f',\n          '900': '#482307',\n        },\n      },\n      typography: (theme) => ({\n        DEFAULT: {\n          css: {\n            '--tw-prose-bullets': theme('colors.toast[400]'),\n            '--tw-prose-links': theme('colors.toast[600]'),\n            color: theme('colors.toast.900'),\n            h1: {\n              color: theme('colors.toast.900'),\n            },\n            h2: {\n              color: theme('colors.toast.900'),\n            },\n            h3: {\n              color: theme('colors.toast.800'),\n            },\n            h4: {\n              color: theme('colors.toast.900'),\n            },\n            a: {\n              color: theme('colors.toast.600'),\n            },\n            strong: {\n              color: theme('colors.toast.900'),\n            },\n            pre: {\n              color: null,\n              backgroundColor: null,\n              overflowX: 'auto',\n              fontSize: theme('fontSize.base'),\n              padding: 0,\n            },\n            'pre pre': {\n              padding: theme('spacing.4'),\n              margin: 0,\n            },\n            'pre code': {\n              backgroundColor: 'transparent',\n              borderWidth: '0',\n              borderRadius: '0',\n              fontWeight: '400',\n              color: 'inherit',\n              fontFamily: 'inherit',\n              lineHeight: 'inherit',\n            },\n            code: {\n              color: theme('colors.toast.900'),\n              fontWeight: '600',\n            },\n            'code::before': {\n              content: '\"\"',\n            },\n            'code::after': {\n              content: '\"\"',\n            },\n            thead: {\n              color: theme('colors.toast.900'),\n              fontWeight: '600',\n              borderBottomWidth: '1px',\n              borderBottomColor: theme('colors.toast.200'),\n            },\n            'tbody tr': {\n              borderBottomWidth: '1px',\n              borderBottomColor: theme('colors.toast.200'),\n            },\n            'ul > li::before': {\n              content: '\"\"',\n              position: 'absolute',\n              backgroundColor: theme('colors.toast.800'),\n              borderRadius: '50%',\n            },\n            // ...\n          },\n        },\n      }),\n    },\n    container: {\n      padding: '1rem',\n      center: true,\n    },\n  },\n  variants: {\n    extend: {\n      translate: ['active'],\n      gradientColorStops: ['active'],\n      boxShadow: ['active'],\n    },\n  },\n  plugins: [require('@tailwindcss/typography')],\n};\n"
  },
  {
    "path": "site/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"types/*.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \"**/**/*.ts\",\n    \"**/**/*.tsx\",\n    \"../**/*.ts\",\n    \"../**/*.tsx\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "site/types/mdx.d.ts",
    "content": "declare module '*.mdx' {\n  let MDXComponent: (props: any) => React.ReactElement;\n  export default MDXComponent;\n}\n"
  },
  {
    "path": "site/types/svg.d.ts",
    "content": "interface SvgrComponent\n  extends React.StatelessComponent<React.SVGAttributes<SVGElement>> {}\n\ndeclare module '*.svg' {\n  const value: SvgrComponent;\n  export default value;\n}\n"
  },
  {
    "path": "src/components/checkmark.tsx",
    "content": "import { styled, keyframes } from 'goober';\n\nconst circleAnimation = keyframes`\nfrom {\n  transform: scale(0) rotate(45deg);\n\topacity: 0;\n}\nto {\n  transform: scale(1) rotate(45deg);\n\topacity: 1;\n}`;\n\nconst checkmarkAnimation = keyframes`\n0% {\n\theight: 0;\n\twidth: 0;\n\topacity: 0;\n}\n40% {\n  height: 0;\n\twidth: 6px;\n\topacity: 1;\n}\n100% {\n  opacity: 1;\n  height: 10px;\n}`;\n\nexport interface CheckmarkTheme {\n  primary?: string;\n  secondary?: string;\n}\n\nexport const CheckmarkIcon = styled('div')<CheckmarkTheme>`\n  width: 20px;\n  opacity: 0;\n  height: 20px;\n  border-radius: 10px;\n  background: ${(p) => p.primary || '#61d345'};\n  position: relative;\n  transform: rotate(45deg);\n\n  animation: ${circleAnimation} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)\n    forwards;\n  animation-delay: 100ms;\n  &:after {\n    content: '';\n    box-sizing: border-box;\n    animation: ${checkmarkAnimation} 0.2s ease-out forwards;\n    opacity: 0;\n    animation-delay: 200ms;\n    position: absolute;\n    border-right: 2px solid;\n    border-bottom: 2px solid;\n    border-color: ${(p) => p.secondary || '#fff'};\n    bottom: 6px;\n    left: 6px;\n    height: 10px;\n    width: 6px;\n  }\n`;\n"
  },
  {
    "path": "src/components/error.tsx",
    "content": "import { styled, keyframes } from 'goober';\n\nconst circleAnimation = keyframes`\nfrom {\n  transform: scale(0) rotate(45deg);\n\topacity: 0;\n}\nto {\n transform: scale(1) rotate(45deg);\n  opacity: 1;\n}`;\n\nconst firstLineAnimation = keyframes`\nfrom {\n  transform: scale(0);\n  opacity: 0;\n}\nto {\n  transform: scale(1);\n  opacity: 1;\n}`;\n\nconst secondLineAnimation = keyframes`\nfrom {\n  transform: scale(0) rotate(90deg);\n\topacity: 0;\n}\nto {\n  transform: scale(1) rotate(90deg);\n\topacity: 1;\n}`;\n\nexport interface ErrorTheme {\n  primary?: string;\n  secondary?: string;\n}\n\nexport const ErrorIcon = styled('div')<ErrorTheme>`\n  width: 20px;\n  opacity: 0;\n  height: 20px;\n  border-radius: 10px;\n  background: ${(p) => p.primary || '#ff4b4b'};\n  position: relative;\n  transform: rotate(45deg);\n\n  animation: ${circleAnimation} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)\n    forwards;\n  animation-delay: 100ms;\n\n  &:after,\n  &:before {\n    content: '';\n    animation: ${firstLineAnimation} 0.15s ease-out forwards;\n    animation-delay: 150ms;\n    position: absolute;\n    border-radius: 3px;\n    opacity: 0;\n    background: ${(p) => p.secondary || '#fff'};\n    bottom: 9px;\n    left: 4px;\n    height: 2px;\n    width: 12px;\n  }\n\n  &:before {\n    animation: ${secondLineAnimation} 0.15s ease-out forwards;\n    animation-delay: 180ms;\n    transform: rotate(90deg);\n  }\n`;\n"
  },
  {
    "path": "src/components/loader.tsx",
    "content": "import { styled, keyframes } from 'goober';\n\nconst rotate = keyframes`\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n`;\n\nexport interface LoaderTheme {\n  primary?: string;\n  secondary?: string;\n}\n\nexport const LoaderIcon = styled('div')<LoaderTheme>`\n  width: 12px;\n  height: 12px;\n  box-sizing: border-box;\n  border: 2px solid;\n  border-radius: 100%;\n  border-color: ${(p) => p.secondary || '#e0e0e0'};\n  border-right-color: ${(p) => p.primary || '#616161'};\n  animation: ${rotate} 1s linear infinite;\n`;\n"
  },
  {
    "path": "src/components/toast-bar.tsx",
    "content": "import * as React from 'react';\nimport { styled, keyframes } from 'goober';\n\nimport { Toast, ToastPosition, resolveValue, Renderable } from '../core/types';\nimport { ToastIcon } from './toast-icon';\nimport { prefersReducedMotion } from '../core/utils';\n\nconst enterAnimation = (factor: number) => `\n0% {transform: translate3d(0,${factor * -200}%,0) scale(.6); opacity:.5;}\n100% {transform: translate3d(0,0,0) scale(1); opacity:1;}\n`;\n\nconst exitAnimation = (factor: number) => `\n0% {transform: translate3d(0,0,-1px) scale(1); opacity:1;}\n100% {transform: translate3d(0,${factor * -150}%,-1px) scale(.6); opacity:0;}\n`;\n\nconst fadeInAnimation = `0%{opacity:0;} 100%{opacity:1;}`;\nconst fadeOutAnimation = `0%{opacity:1;} 100%{opacity:0;}`;\n\n// Use :where() for zero specificity - allows Tailwind to override easily\nconst ToastBarBase = styled('div')`\n  :where(&) {\n    display: flex;\n    align-items: center;\n    background: #fff;\n    color: #363636;\n    line-height: 1.3;\n    will-change: transform;\n    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05);\n    max-width: 350px;\n    pointer-events: auto;\n    padding: 8px 10px;\n    border-radius: 8px;\n  }\n`;\n\nconst Message = styled('div')`\n  display: flex;\n  justify-content: center;\n  margin: 4px 10px;\n  color: inherit;\n  flex: 1 1 auto;\n  white-space: pre-line;\n`;\n\ninterface ToastBarProps {\n  toast: Toast;\n  position?: ToastPosition;\n  style?: React.CSSProperties;\n  children?: (components: {\n    icon: Renderable;\n    message: Renderable;\n  }) => Renderable;\n}\n\nconst getAnimationStyle = (\n  position: ToastPosition,\n  visible: boolean\n): React.CSSProperties => {\n  const top = position.includes('top');\n  const factor = top ? 1 : -1;\n\n  const [enter, exit] = prefersReducedMotion()\n    ? [fadeInAnimation, fadeOutAnimation]\n    : [enterAnimation(factor), exitAnimation(factor)];\n\n  return {\n    animation: visible\n      ? `${keyframes(enter)} 0.35s cubic-bezier(.21,1.02,.73,1) forwards`\n      : `${keyframes(exit)} 0.4s forwards cubic-bezier(.06,.71,.55,1)`,\n  };\n};\n\nexport const ToastBar: React.FC<ToastBarProps> = React.memo(\n  ({ toast, position, style, children }) => {\n    const animationStyle: React.CSSProperties = toast.height\n      ? getAnimationStyle(\n          toast.position || position || 'top-center',\n          toast.visible\n        )\n      : { opacity: 0 };\n\n    const icon = <ToastIcon toast={toast} />;\n    const message = (\n      <Message {...toast.ariaProps}>\n        {resolveValue(toast.message, toast)}\n      </Message>\n    );\n\n    return (\n      <ToastBarBase\n        className={toast.className}\n        style={{\n          ...animationStyle,\n          ...style,\n          ...toast.style,\n        }}\n      >\n        {typeof children === 'function' ? (\n          children({\n            icon,\n            message,\n          })\n        ) : (\n          <>\n            {icon}\n            {message}\n          </>\n        )}\n      </ToastBarBase>\n    );\n  }\n);\n"
  },
  {
    "path": "src/components/toast-icon.tsx",
    "content": "import * as React from 'react';\nimport { styled, keyframes } from 'goober';\n\nimport { Toast } from '../core/types';\nimport { ErrorIcon, ErrorTheme } from './error';\nimport { LoaderIcon, LoaderTheme } from './loader';\nimport { CheckmarkIcon, CheckmarkTheme } from './checkmark';\n\nconst StatusWrapper = styled('div')`\n  position: absolute;\n`;\n\nconst IndicatorWrapper = styled('div')`\n  position: relative;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  min-width: 20px;\n  min-height: 20px;\n`;\n\nconst enter = keyframes`\nfrom {\n  transform: scale(0.6);\n  opacity: 0.4;\n}\nto {\n  transform: scale(1);\n  opacity: 1;\n}`;\n\nexport const AnimatedIconWrapper = styled('div')`\n  position: relative;\n  transform: scale(0.6);\n  opacity: 0.4;\n  min-width: 20px;\n  animation: ${enter} 0.3s 0.12s cubic-bezier(0.175, 0.885, 0.32, 1.275)\n    forwards;\n`;\n\nexport type IconThemes = Partial<{\n  success: CheckmarkTheme;\n  error: ErrorTheme;\n  loading: LoaderTheme;\n}>;\n\nexport const ToastIcon: React.FC<{\n  toast: Toast;\n}> = ({ toast }) => {\n  const { icon, type, iconTheme } = toast;\n  if (icon !== undefined) {\n    if (typeof icon === 'string') {\n      return <AnimatedIconWrapper>{icon}</AnimatedIconWrapper>;\n    } else {\n      return icon;\n    }\n  }\n\n  if (type === 'blank') {\n    return null;\n  }\n\n  return (\n    <IndicatorWrapper>\n      <LoaderIcon {...iconTheme} />\n      {type !== 'loading' && (\n        <StatusWrapper>\n          {type === 'error' ? (\n            <ErrorIcon {...iconTheme} />\n          ) : (\n            <CheckmarkIcon {...iconTheme} />\n          )}\n        </StatusWrapper>\n      )}\n    </IndicatorWrapper>\n  );\n};\n"
  },
  {
    "path": "src/components/toaster.tsx",
    "content": "import { css, setup } from 'goober';\nimport * as React from 'react';\nimport {\n  resolveValue,\n  ToasterProps,\n  ToastPosition,\n  ToastWrapperProps,\n} from '../core/types';\nimport { useToaster } from '../core/use-toaster';\nimport { prefersReducedMotion } from '../core/utils';\nimport { ToastBar } from './toast-bar';\n\nsetup(React.createElement);\n\nconst ToastWrapper = ({\n  id,\n  className,\n  style,\n  onHeightUpdate,\n  children,\n}: ToastWrapperProps) => {\n  const ref = React.useCallback(\n    (el: HTMLElement | null) => {\n      if (el) {\n        const updateHeight = () => {\n          const height = el.getBoundingClientRect().height;\n          onHeightUpdate(id, height);\n        };\n        updateHeight();\n        new MutationObserver(updateHeight).observe(el, {\n          subtree: true,\n          childList: true,\n          characterData: true,\n        });\n      }\n    },\n    [id, onHeightUpdate]\n  );\n\n  return (\n    <div ref={ref} className={className} style={style}>\n      {children}\n    </div>\n  );\n};\n\nconst getPositionStyle = (\n  position: ToastPosition,\n  offset: number\n): React.CSSProperties => {\n  const top = position.includes('top');\n  const verticalStyle: React.CSSProperties = top ? { top: 0 } : { bottom: 0 };\n  const horizontalStyle: React.CSSProperties = position.includes('center')\n    ? {\n        justifyContent: 'center',\n      }\n    : position.includes('right')\n    ? {\n        justifyContent: 'flex-end',\n      }\n    : {};\n  return {\n    left: 0,\n    right: 0,\n    display: 'flex',\n    position: 'absolute',\n    transition: prefersReducedMotion()\n      ? undefined\n      : `all 230ms cubic-bezier(.21,1.02,.73,1)`,\n    transform: `translateY(${offset * (top ? 1 : -1)}px)`,\n    ...verticalStyle,\n    ...horizontalStyle,\n  };\n};\n\nconst activeClass = css`\n  z-index: 9999;\n  > * {\n    pointer-events: auto;\n  }\n`;\n\nconst DEFAULT_OFFSET = 16;\n\nexport const Toaster: React.FC<ToasterProps> = ({\n  reverseOrder,\n  position = 'top-center',\n  toastOptions,\n  gutter,\n  children,\n  toasterId,\n  containerStyle,\n  containerClassName,\n}) => {\n  const { toasts, handlers } = useToaster(toastOptions, toasterId);\n\n  return (\n    <div\n      data-rht-toaster={toasterId || ''}\n      style={{\n        position: 'fixed',\n        zIndex: 9999,\n        top: DEFAULT_OFFSET,\n        left: DEFAULT_OFFSET,\n        right: DEFAULT_OFFSET,\n        bottom: DEFAULT_OFFSET,\n        pointerEvents: 'none',\n        ...containerStyle,\n      }}\n      className={containerClassName}\n      onMouseEnter={handlers.startPause}\n      onMouseLeave={handlers.endPause}\n    >\n      {toasts.map((t) => {\n        const toastPosition = t.position || position;\n        const offset = handlers.calculateOffset(t, {\n          reverseOrder,\n          gutter,\n          defaultPosition: position,\n        });\n        const positionStyle = getPositionStyle(toastPosition, offset);\n\n        return (\n          <ToastWrapper\n            id={t.id}\n            key={t.id}\n            onHeightUpdate={handlers.updateHeight}\n            className={t.visible ? activeClass : ''}\n            style={positionStyle}\n          >\n            {t.type === 'custom' ? (\n              resolveValue(t.message, t)\n            ) : children ? (\n              children(t)\n            ) : (\n              <ToastBar toast={t} position={toastPosition} />\n            )}\n          </ToastWrapper>\n        );\n      })}\n    </div>\n  );\n};\n"
  },
  {
    "path": "src/core/store.ts",
    "content": "import { useEffect, useState, useRef } from 'react';\nimport { DefaultToastOptions, Toast, ToastType } from './types';\n\nexport const TOAST_EXPIRE_DISMISS_DELAY = 1000;\nexport const TOAST_LIMIT = 20;\nexport const DEFAULT_TOASTER_ID = 'default';\n\ninterface ToasterSettings {\n  toastLimit: number;\n}\n\nexport enum ActionType {\n  ADD_TOAST,\n  UPDATE_TOAST,\n  UPSERT_TOAST,\n  DISMISS_TOAST,\n  REMOVE_TOAST,\n  START_PAUSE,\n  END_PAUSE,\n}\n\nexport type Action =\n  | {\n      type: ActionType.ADD_TOAST;\n      toast: Toast;\n    }\n  | {\n      type: ActionType.UPSERT_TOAST;\n      toast: Toast;\n    }\n  | {\n      type: ActionType.UPDATE_TOAST;\n      toast: Partial<Toast>;\n    }\n  | {\n      type: ActionType.DISMISS_TOAST;\n      toastId?: string;\n    }\n  | {\n      type: ActionType.REMOVE_TOAST;\n      toastId?: string;\n    }\n  | {\n      type: ActionType.START_PAUSE;\n      time: number;\n    }\n  | {\n      type: ActionType.END_PAUSE;\n      time: number;\n    };\n\ninterface ToasterState {\n  toasts: Toast[];\n  settings: ToasterSettings;\n  pausedAt: number | undefined;\n}\n\ninterface State {\n  [toasterId: string]: ToasterState;\n}\n\nexport const reducer = (state: ToasterState, action: Action): ToasterState => {\n  const { toastLimit } = state.settings;\n\n  switch (action.type) {\n    case ActionType.ADD_TOAST:\n      return {\n        ...state,\n        toasts: [action.toast, ...state.toasts].slice(0, toastLimit),\n      };\n\n    case ActionType.UPDATE_TOAST:\n      return {\n        ...state,\n        toasts: state.toasts.map((t) =>\n          t.id === action.toast.id ? { ...t, ...action.toast } : t\n        ),\n      };\n\n    case ActionType.UPSERT_TOAST:\n      const { toast } = action;\n      return reducer(state, {\n        type: state.toasts.find((t) => t.id === toast.id)\n          ? ActionType.UPDATE_TOAST\n          : ActionType.ADD_TOAST,\n        toast,\n      });\n\n    case ActionType.DISMISS_TOAST:\n      const { toastId } = action;\n\n      return {\n        ...state,\n        toasts: state.toasts.map((t) =>\n          t.id === toastId || toastId === undefined\n            ? {\n                ...t,\n                dismissed: true,\n                visible: false,\n              }\n            : t\n        ),\n      };\n    case ActionType.REMOVE_TOAST:\n      if (action.toastId === undefined) {\n        return {\n          ...state,\n          toasts: [],\n        };\n      }\n      return {\n        ...state,\n        toasts: state.toasts.filter((t) => t.id !== action.toastId),\n      };\n\n    case ActionType.START_PAUSE:\n      return {\n        ...state,\n        pausedAt: action.time,\n      };\n\n    case ActionType.END_PAUSE:\n      const diff = action.time - (state.pausedAt || 0);\n\n      return {\n        ...state,\n        pausedAt: undefined,\n        toasts: state.toasts.map((t) => ({\n          ...t,\n          pauseDuration: t.pauseDuration + diff,\n        })),\n      };\n  }\n};\n\nconst listeners: Array<\n  [toasterId: string, reducer: (state: ToasterState) => void]\n> = [];\n\nconst defaultToasterState: ToasterState = {\n  toasts: [],\n  pausedAt: undefined,\n  settings: {\n    toastLimit: TOAST_LIMIT,\n  },\n};\nlet memoryState: State = {};\n\nexport const dispatch = (action: Action, toasterId = DEFAULT_TOASTER_ID) => {\n  memoryState[toasterId] = reducer(\n    memoryState[toasterId] || defaultToasterState,\n    action\n  );\n  listeners.forEach(([id, listener]) => {\n    if (id === toasterId) {\n      listener(memoryState[toasterId]);\n    }\n  });\n};\n\nexport const dispatchAll = (action: Action) =>\n  Object.keys(memoryState).forEach((toasterId) => dispatch(action, toasterId));\n\nexport const getToasterIdFromToastId = (toastId: string) =>\n  Object.keys(memoryState).find((toasterId) =>\n    memoryState[toasterId].toasts.some((t) => t.id === toastId)\n  );\n\nexport const createDispatch =\n  (toasterId = DEFAULT_TOASTER_ID) =>\n  (action: Action) => {\n    dispatch(action, toasterId);\n  };\n\nexport const defaultTimeouts: {\n  [key in ToastType]: number;\n} = {\n  blank: 4000,\n  error: 4000,\n  success: 2000,\n  loading: Infinity,\n  custom: 4000,\n};\n\nexport const useStore = (\n  toastOptions: DefaultToastOptions = {},\n  toasterId: string = DEFAULT_TOASTER_ID\n): ToasterState => {\n  const [state, setState] = useState<ToasterState>(\n    memoryState[toasterId] || defaultToasterState\n  );\n  const initial = useRef(memoryState[toasterId]);\n\n  // TODO: Switch to useSyncExternalStore when targeting React 18+\n  useEffect(() => {\n    if (initial.current !== memoryState[toasterId]) {\n      setState(memoryState[toasterId]);\n    }\n    listeners.push([toasterId, setState]);\n    return () => {\n      const index = listeners.findIndex(([id]) => id === toasterId);\n      if (index > -1) {\n        listeners.splice(index, 1);\n      }\n    };\n  }, [toasterId]);\n\n  const mergedToasts = state.toasts.map((t) => ({\n    ...toastOptions,\n    ...toastOptions[t.type],\n    ...t,\n    removeDelay:\n      t.removeDelay ||\n      toastOptions[t.type]?.removeDelay ||\n      toastOptions?.removeDelay,\n    duration:\n      t.duration ||\n      toastOptions[t.type]?.duration ||\n      toastOptions?.duration ||\n      defaultTimeouts[t.type],\n    style: {\n      ...toastOptions.style,\n      ...toastOptions[t.type]?.style,\n      ...t.style,\n    },\n  }));\n\n  return {\n    ...state,\n    toasts: mergedToasts,\n  };\n};\n"
  },
  {
    "path": "src/core/toast.ts",
    "content": "import {\n  Renderable,\n  Toast,\n  ToastOptions,\n  ToastType,\n  DefaultToastOptions,\n  ValueOrFunction,\n  resolveValue,\n} from './types';\nimport { genId } from './utils';\nimport {\n  createDispatch,\n  Action,\n  ActionType,\n  dispatchAll,\n  getToasterIdFromToastId,\n} from './store';\n\ntype Message = ValueOrFunction<Renderable, Toast>;\n\ntype ToastHandler = (message: Message, options?: ToastOptions) => string;\n\nconst createToast = (\n  message: Message,\n  type: ToastType = 'blank',\n  opts?: ToastOptions\n): Toast => ({\n  createdAt: Date.now(),\n  visible: true,\n  dismissed: false,\n  type,\n  ariaProps: {\n    role: 'status',\n    'aria-live': 'polite',\n  },\n  message,\n  pauseDuration: 0,\n  ...opts,\n  id: opts?.id || genId(),\n});\n\nconst createHandler =\n  (type?: ToastType): ToastHandler =>\n  (message, options) => {\n    const toast = createToast(message, type, options);\n\n    const dispatch = createDispatch(\n      toast.toasterId || getToasterIdFromToastId(toast.id)\n    );\n\n    dispatch({ type: ActionType.UPSERT_TOAST, toast });\n    return toast.id;\n  };\n\nconst toast = (message: Message, opts?: ToastOptions) =>\n  createHandler('blank')(message, opts);\n\ntoast.error = createHandler('error');\ntoast.success = createHandler('success');\ntoast.loading = createHandler('loading');\ntoast.custom = createHandler('custom');\n\n/**\n * Dismisses the toast with the given id. If no id is given, dismisses all toasts.\n * The toast will transition out and then be removed from the DOM.\n * Applies to all toasters, except when a `toasterId` is given.\n */\ntoast.dismiss = (toastId?: string, toasterId?: string) => {\n  const action: Action = {\n    type: ActionType.DISMISS_TOAST,\n    toastId,\n  };\n\n  if (toasterId) {\n    createDispatch(toasterId)(action);\n  } else {\n    dispatchAll(action);\n  }\n};\n\n/**\n * Dismisses all toasts.\n */\ntoast.dismissAll = (toasterId?: string) => toast.dismiss(undefined, toasterId);\n\n/**\n * Removes the toast with the given id.\n * The toast will be removed from the DOM without any transition.\n */\ntoast.remove = (toastId?: string, toasterId?: string) => {\n  const action: Action = {\n    type: ActionType.REMOVE_TOAST,\n    toastId,\n  };\n  if (toasterId) {\n    createDispatch(toasterId)(action);\n  } else {\n    dispatchAll(action);\n  }\n};\n\n/**\n * Removes all toasts.\n */\ntoast.removeAll = (toasterId?: string) => toast.remove(undefined, toasterId);\n\n/**\n * Create a loading toast that will automatically updates with the promise.\n */\ntoast.promise = <T>(\n  promise: Promise<T> | (() => Promise<T>),\n  msgs: {\n    loading: Renderable;\n    success?: ValueOrFunction<Renderable, T>;\n    error?: ValueOrFunction<Renderable, any>;\n  },\n  opts?: DefaultToastOptions\n) => {\n  const id = toast.loading(msgs.loading, { ...opts, ...opts?.loading });\n\n  if (typeof promise === 'function') {\n    promise = promise();\n  }\n\n  promise\n    .then((p) => {\n      const successMessage = msgs.success\n        ? resolveValue(msgs.success, p)\n        : undefined;\n\n      if (successMessage) {\n        toast.success(successMessage, {\n          id,\n          ...opts,\n          ...opts?.success,\n        });\n      } else {\n        toast.dismiss(id);\n      }\n      return p;\n    })\n    .catch((e) => {\n      const errorMessage = msgs.error ? resolveValue(msgs.error, e) : undefined;\n\n      if (errorMessage) {\n        toast.error(errorMessage, {\n          id,\n          ...opts,\n          ...opts?.error,\n        });\n      } else {\n        toast.dismiss(id);\n      }\n    });\n\n  return promise;\n};\n\nexport { toast };\n"
  },
  {
    "path": "src/core/types.ts",
    "content": "import { CSSProperties } from 'react';\n\nexport type ToastType = 'success' | 'error' | 'loading' | 'blank' | 'custom';\nexport type ToastPosition =\n  | 'top-left'\n  | 'top-center'\n  | 'top-right'\n  | 'bottom-left'\n  | 'bottom-center'\n  | 'bottom-right';\n\nexport type Renderable = React.ReactElement | string | null;\n\nexport interface IconTheme {\n  primary: string;\n  secondary: string;\n}\n\nexport type ValueFunction<TValue, TArg> = (arg: TArg) => TValue;\nexport type ValueOrFunction<TValue, TArg> =\n  | TValue\n  | ValueFunction<TValue, TArg>;\n\nconst isFunction = <TValue, TArg>(\n  valOrFunction: ValueOrFunction<TValue, TArg>\n): valOrFunction is ValueFunction<TValue, TArg> =>\n  typeof valOrFunction === 'function';\n\nexport const resolveValue = <TValue, TArg>(\n  valOrFunction: ValueOrFunction<TValue, TArg>,\n  arg: TArg\n): TValue => (isFunction(valOrFunction) ? valOrFunction(arg) : valOrFunction);\n\nexport interface Toast {\n  type: ToastType;\n  id: string;\n  toasterId?: string;\n  message: ValueOrFunction<Renderable, Toast>;\n  icon?: Renderable;\n  duration?: number;\n  pauseDuration: number;\n  position?: ToastPosition;\n  removeDelay?: number;\n\n  ariaProps: {\n    role: 'status' | 'alert';\n    'aria-live': 'assertive' | 'off' | 'polite';\n  };\n\n  style?: CSSProperties;\n  className?: string;\n  iconTheme?: IconTheme;\n\n  createdAt: number;\n  visible: boolean;\n  dismissed: boolean;\n  height?: number;\n}\n\nexport type ToastOptions = Partial<\n  Pick<\n    Toast,\n    | 'id'\n    | 'icon'\n    | 'duration'\n    | 'ariaProps'\n    | 'className'\n    | 'style'\n    | 'position'\n    | 'iconTheme'\n    | 'toasterId'\n    | 'removeDelay'\n  >\n>;\n\nexport type DefaultToastOptions = ToastOptions & {\n  [key in ToastType]?: ToastOptions;\n};\n\nexport interface ToasterProps {\n  position?: ToastPosition;\n  toastOptions?: DefaultToastOptions;\n  reverseOrder?: boolean;\n  gutter?: number;\n  containerStyle?: React.CSSProperties;\n  containerClassName?: string;\n  toasterId?: string;\n  children?: (toast: Toast) => React.ReactElement;\n}\n\nexport interface ToastWrapperProps {\n  id: string;\n  className?: string;\n  style?: React.CSSProperties;\n  onHeightUpdate: (id: string, height: number) => void;\n  children?: React.ReactNode;\n}\n"
  },
  {
    "path": "src/core/use-toaster.ts",
    "content": "import { useEffect, useCallback, useRef } from 'react';\nimport { createDispatch, ActionType, useStore, dispatch } from './store';\nimport { toast } from './toast';\nimport { DefaultToastOptions, Toast, ToastPosition } from './types';\n\nexport const REMOVE_DELAY = 1000;\n\nexport const useToaster = (\n  toastOptions?: DefaultToastOptions,\n  toasterId: string = 'default'\n) => {\n  const { toasts, pausedAt } = useStore(toastOptions, toasterId);\n  const toastTimeouts = useRef(\n    new Map<Toast['id'], ReturnType<typeof setTimeout>>()\n  ).current;\n\n  const addToRemoveQueue = useCallback(\n    (toastId: string, removeDelay = REMOVE_DELAY) => {\n      if (toastTimeouts.has(toastId)) {\n        return;\n      }\n\n      const timeout = setTimeout(() => {\n        toastTimeouts.delete(toastId);\n        dispatch({\n          type: ActionType.REMOVE_TOAST,\n          toastId: toastId,\n        });\n      }, removeDelay);\n\n      toastTimeouts.set(toastId, timeout);\n    },\n    []\n  );\n\n  useEffect(() => {\n    if (pausedAt) {\n      return;\n    }\n\n    const now = Date.now();\n    const timeouts = toasts.map((t) => {\n      if (t.duration === Infinity) {\n        return;\n      }\n\n      const durationLeft =\n        (t.duration || 0) + t.pauseDuration - (now - t.createdAt);\n\n      if (durationLeft < 0) {\n        if (t.visible) {\n          toast.dismiss(t.id);\n        }\n        return;\n      }\n      return setTimeout(() => toast.dismiss(t.id, toasterId), durationLeft);\n    });\n\n    return () => {\n      timeouts.forEach((timeout) => timeout && clearTimeout(timeout));\n    };\n  }, [toasts, pausedAt, toasterId]);\n\n  const dispatch = useCallback(createDispatch(toasterId), [toasterId]);\n\n  const startPause = useCallback(() => {\n    dispatch({\n      type: ActionType.START_PAUSE,\n      time: Date.now(),\n    });\n  }, [dispatch]);\n\n  const updateHeight = useCallback(\n    (toastId: string, height: number) => {\n      dispatch({\n        type: ActionType.UPDATE_TOAST,\n        toast: { id: toastId, height },\n      });\n    },\n    [dispatch]\n  );\n\n  const endPause = useCallback(() => {\n    if (pausedAt) {\n      dispatch({ type: ActionType.END_PAUSE, time: Date.now() });\n    }\n  }, [pausedAt, dispatch]);\n\n  const calculateOffset = useCallback(\n    (\n      toast: Toast,\n      opts?: {\n        reverseOrder?: boolean;\n        gutter?: number;\n        defaultPosition?: ToastPosition;\n      }\n    ) => {\n      const { reverseOrder = false, gutter = 8, defaultPosition } = opts || {};\n\n      const relevantToasts = toasts.filter(\n        (t) =>\n          (t.position || defaultPosition) ===\n            (toast.position || defaultPosition) && t.height\n      );\n      const toastIndex = relevantToasts.findIndex((t) => t.id === toast.id);\n      const toastsBefore = relevantToasts.filter(\n        (toast, i) => i < toastIndex && toast.visible\n      ).length;\n\n      const offset = relevantToasts\n        .filter((t) => t.visible)\n        .slice(...(reverseOrder ? [toastsBefore + 1] : [0, toastsBefore]))\n        .reduce((acc, t) => acc + (t.height || 0) + gutter, 0);\n\n      return offset;\n    },\n    [toasts]\n  );\n\n  // Keep track of dismissed toasts and remove them after the delay\n  useEffect(() => {\n    toasts.forEach((toast) => {\n      if (toast.dismissed) {\n        addToRemoveQueue(toast.id, toast.removeDelay);\n      } else {\n        // If toast becomes visible again, remove it from the queue\n        const timeout = toastTimeouts.get(toast.id);\n        if (timeout) {\n          clearTimeout(timeout);\n          toastTimeouts.delete(toast.id);\n        }\n      }\n    });\n  }, [toasts, addToRemoveQueue]);\n\n  return {\n    toasts,\n    handlers: {\n      updateHeight,\n      startPause,\n      endPause,\n      calculateOffset,\n    },\n  };\n};\n"
  },
  {
    "path": "src/core/utils.ts",
    "content": "export const genId = (() => {\n  let count = 0;\n  return () => {\n    return (++count).toString();\n  };\n})();\n\nexport const prefersReducedMotion = (() => {\n  // Cache result\n  let shouldReduceMotion: boolean | undefined = undefined;\n\n  return () => {\n    if (shouldReduceMotion === undefined && typeof window !== 'undefined') {\n      const mediaQuery = matchMedia('(prefers-reduced-motion: reduce)');\n      shouldReduceMotion = !mediaQuery || mediaQuery.matches;\n    }\n    return shouldReduceMotion;\n  };\n})();\n"
  },
  {
    "path": "src/headless/index.ts",
    "content": "import { toast } from '../core/toast';\n\nexport type {\n  DefaultToastOptions,\n  IconTheme,\n  Renderable,\n  Toast,\n  ToasterProps,\n  ToastOptions,\n  ToastPosition,\n  ToastType,\n  ValueFunction,\n  ValueOrFunction,\n} from '../core/types';\n\nexport { resolveValue } from '../core/types';\nexport { useToaster } from '../core/use-toaster';\nexport { useStore as useToasterStore } from '../core/store';\n\nexport { toast };\nexport default toast;\n"
  },
  {
    "path": "src/index.ts",
    "content": "import { toast } from './core/toast';\n\nexport * from './headless';\n\nexport { ToastBar } from './components/toast-bar';\nexport { ToastIcon } from './components/toast-icon';\nexport { Toaster } from './components/toaster';\nexport { CheckmarkIcon } from './components/checkmark';\nexport { ErrorIcon } from './components/error';\nexport { LoaderIcon } from './components/loader';\n\nexport { toast };\nexport default toast;\n"
  },
  {
    "path": "test/setup.ts",
    "content": "import '@testing-library/jest-dom';\n\n// Mock matchMedia\nObject.defineProperty(window, 'matchMedia', {\n  writable: true,\n  value: jest.fn().mockImplementation((query) => ({\n    matches: false,\n    media: query,\n    onchange: null,\n    addListener: jest.fn(), // deprecated\n    removeListener: jest.fn(), // deprecated\n    addEventListener: jest.fn(),\n    removeEventListener: jest.fn(),\n    dispatchEvent: jest.fn(),\n  })),\n});\n\n// Mock getBoundingClientRect\nElement.prototype.getBoundingClientRect = jest.fn(() => {\n  return {\n    width: 300,\n    height: 120,\n    x: 0,\n    y: 0,\n    top: 0,\n    left: 0,\n    bottom: 0,\n    right: 0,\n    toJSON: () => '{}',\n  };\n});\n"
  },
  {
    "path": "test/toast.test.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport {\n  render,\n  screen,\n  act,\n  waitFor,\n  fireEvent,\n} from '@testing-library/react';\n\nimport toast, { resolveValue, Toaster, ToastIcon } from '../src';\nimport { defaultTimeouts } from '../src/core/store';\nimport { REMOVE_DELAY } from '../src/core/use-toaster';\n\nbeforeEach(() => {\n  // Tests should run in serial for improved isolation\n  // To prevent collision with global state, reset all toasts for each test\n  toast.remove();\n  jest.useFakeTimers();\n});\n\nafterEach((done) => {\n  act(() => {\n    jest.runAllTimers();\n    jest.useRealTimers();\n    done();\n  });\n});\n\nconst waitTime = (time: number) => {\n  act(() => {\n    jest.advanceTimersByTime(time);\n  });\n};\n\nconst TOAST_DURATION = 1000;\n\ntest('close notification', async () => {\n  render(\n    <>\n      <button\n        type=\"button\"\n        onClick={() => {\n          toast.success((t) => (\n            <div>\n              Example\n              <button\n                aria-hidden={!t.visible}\n                type=\"button\"\n                onClick={() => {\n                  toast.dismiss(t.id);\n                }}\n                title={'close'}\n              >\n                Close\n              </button>\n            </div>\n          ));\n        }}\n      >\n        Notify!\n      </button>\n      <Toaster />\n    </>\n  );\n\n  fireEvent.click(screen.getByRole('button', { name: /Notify/i }));\n\n  await waitFor(() => screen.getByText(/example/i));\n\n  expect(screen.queryByText(/example/i)).toBeInTheDocument();\n\n  fireEvent.click(await screen.findByRole('button', { name: /close/i }));\n\n  waitTime(REMOVE_DELAY);\n\n  expect(screen.queryByText(/example/i)).not.toBeInTheDocument();\n});\n\ntest('promise toast', async () => {\n  const WAIT_DELAY = 1000;\n\n  render(\n    <>\n      <button\n        type=\"button\"\n        onClick={() => {\n          const sleep = new Promise((resolve) => {\n            setTimeout(resolve, WAIT_DELAY);\n          });\n\n          toast.promise(sleep, {\n            loading: 'Loading...',\n            success: 'Success!',\n            error: 'Error!',\n          });\n        }}\n      >\n        Notify!\n      </button>\n      <Toaster />\n    </>\n  );\n\n  act(() => {\n    fireEvent.click(screen.getByRole('button', { name: /Notify/i }));\n  });\n\n  await screen.findByText(/loading/i);\n\n  expect(screen.queryByText(/loading/i)).toBeInTheDocument();\n\n  waitTime(WAIT_DELAY);\n\n  await waitFor(() => {\n    expect(screen.queryByText(/success/i)).toBeInTheDocument();\n  });\n});\n\ntest('promise toast error', async () => {\n  const WAIT_DELAY = 1000;\n\n  render(\n    <>\n      <button\n        type=\"button\"\n        onClick={() => {\n          const sleep = new Promise((_, rej) => {\n            setTimeout(rej, WAIT_DELAY);\n          });\n\n          toast.promise(sleep, {\n            loading: 'Loading...',\n            success: 'Success!',\n            error: 'Error!',\n          });\n        }}\n      >\n        Notify!\n      </button>\n      <Toaster />\n    </>\n  );\n\n  act(() => {\n    fireEvent.click(screen.getByRole('button', { name: /Notify/i }));\n  });\n\n  await screen.findByText(/loading/i);\n\n  expect(screen.queryByText(/loading/i)).toBeInTheDocument();\n\n  waitTime(WAIT_DELAY);\n\n  await waitFor(() => {\n    expect(screen.queryByText(/error/i)).toBeInTheDocument();\n  });\n});\n\ntest('error toast with custom duration', async () => {\n  render(\n    <>\n      <button\n        type=\"button\"\n        onClick={() => {\n          toast.error('An error happened', {\n            duration: TOAST_DURATION,\n          });\n        }}\n      >\n        Notify!\n      </button>\n      <Toaster position=\"bottom-right\" />\n    </>\n  );\n\n  act(() => {\n    fireEvent.click(screen.getByRole('button', { name: /Notify/i }));\n  });\n\n  await screen.findByText(/error/i);\n\n  expect(screen.queryByText(/error/i)).toBeInTheDocument();\n\n  waitTime(TOAST_DURATION);\n\n  waitTime(REMOVE_DELAY);\n\n  expect(screen.queryByText(/error/i)).not.toBeInTheDocument();\n});\n\ntest('different toasts types with dismiss', async () => {\n  render(\n    <>\n      <Toaster />\n    </>\n  );\n\n  act(() => {\n    toast.success('Success!');\n  });\n\n  act(() => {\n    toast.error('Error!');\n  });\n\n  act(() => {\n    toast('Emoji Icon', {\n      icon: '✅',\n    });\n  });\n\n  act(() => {\n    toast('Custom Icon', {\n      icon: <span>ICON</span>,\n    });\n  });\n  let loadingToastId: string;\n  act(() => {\n    loadingToastId = toast.loading('Loading!');\n  });\n\n  expect(screen.queryByText(/error/i)).toBeInTheDocument();\n  expect(screen.queryByText(/success/i)).toBeInTheDocument();\n  expect(screen.queryByText(/loading/i)).toBeInTheDocument();\n  expect(screen.queryByText('✅')).toBeInTheDocument();\n  expect(screen.queryByText('ICON')).toBeInTheDocument();\n\n  waitTime(defaultTimeouts.success);\n\n  waitTime(REMOVE_DELAY);\n\n  expect(screen.queryByText(/success/i)).not.toBeInTheDocument();\n  expect(screen.queryByText(/error/i)).toBeInTheDocument();\n\n  waitTime(defaultTimeouts.error);\n\n  waitTime(REMOVE_DELAY);\n\n  expect(screen.queryByText(/error/i)).not.toBeInTheDocument();\n\n  act(() => {\n    toast.dismiss(loadingToastId);\n  });\n\n  waitTime(REMOVE_DELAY);\n\n  expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();\n});\n\ntest('custom toaster renderer', async () => {\n  render(\n    <>\n      <Toaster>\n        {(t) => (\n          <div className=\"custom-toast\">\n            <ToastIcon toast={t} />\n            {resolveValue(t.message, t)}\n          </div>\n        )}\n      </Toaster>\n    </>\n  );\n\n  act(() => {\n    toast.success('Success!');\n  });\n\n  expect(screen.queryByText(/success/i)).toHaveClass('custom-toast');\n\n  act(() => {\n    toast(<b>Bold</b>);\n  });\n\n  expect(screen.queryByText(/bold/i)).toBeInTheDocument();\n\n  act(() => {\n    toast.custom('Custom');\n  });\n\n  expect(screen.queryByText(/custom/i)).not.toHaveClass('custom-toast');\n});\n\ntest('pause toast', async () => {\n  render(\n    <>\n      <Toaster>\n        {(t) => (\n          <div className=\"custom-toast\">\n            <ToastIcon toast={t} />\n            {resolveValue(t.message, t)}\n          </div>\n        )}\n      </Toaster>\n    </>\n  );\n\n  act(() => {\n    toast.success('Hover me!', {\n      duration: 1000,\n    });\n  });\n\n  waitTime(500);\n\n  const toastElement = screen.getByText(/hover me/i);\n\n  expect(toastElement).toBeInTheDocument();\n\n  fireEvent.mouseEnter(toastElement);\n\n  waitTime(10000);\n\n  expect(toastElement).toBeInTheDocument();\n\n  fireEvent.mouseLeave(toastElement);\n\n  waitTime(1000);\n  waitTime(1000);\n\n  expect(toastElement).not.toBeInTheDocument();\n});\n\ntest('\"toast\" can be called from useEffect hook', async () => {\n  const MyComponent = () => {\n    const [success, setSuccess] = useState(false);\n    useEffect(() => {\n      toast.success('Success toast');\n      setSuccess(true);\n    }, []);\n\n    return success ? <div>MyComponent finished</div> : null;\n  };\n\n  render(\n    <>\n      <MyComponent />\n      <Toaster />\n    </>\n  );\n\n  await screen.findByText(/MyComponent finished/i);\n  expect(screen.queryByText(/Success toast/i)).toBeInTheDocument();\n});\n\ndescribe('Multi-Toaster behavior', () => {\n  test('renders toasts in correct containers and dismisses them individually', () => {\n    render(\n      <>\n        <Toaster position=\"top-left\" containerClassName=\"default-toaster\" />\n        <Toaster\n          position=\"top-right\"\n          toasterId=\"second-toaster\"\n          containerClassName=\"second-toaster\"\n        />\n        <Toaster\n          position=\"bottom-center\"\n          toasterId=\"third-toaster\"\n          containerClassName=\"third-toaster\"\n        />\n      </>\n    );\n\n    // Show three toasts in three different toasters\n    act(() => {\n      toast.success('Default toaster message');\n      toast.error('Second toaster message', {\n        toasterId: 'second-toaster',\n        id: 'second-toast',\n      });\n      toast.loading('Third toaster message', { toasterId: 'third-toaster' });\n    });\n\n    const defaultContainer = document.querySelector('.default-toaster');\n    const secondContainer = document.querySelector('.second-toaster');\n    const thirdContainer = document.querySelector('.third-toaster');\n\n    // Ensure each toast is present and in the correct container\n    expect(defaultContainer).toContainElement(\n      screen.getByText('Default toaster message')\n    );\n    expect(secondContainer).toContainElement(\n      screen.getByText('Second toaster message')\n    );\n    expect(thirdContainer).toContainElement(\n      screen.getByText('Third toaster message')\n    );\n\n    // Dismiss only the toast in the second toaster\n    act(() => {\n      toast.dismiss('second-toast');\n    });\n\n    waitTime(REMOVE_DELAY);\n\n    expect(\n      screen.queryByText('Second toaster message')\n    ).not.toBeInTheDocument();\n    expect(screen.queryByText('Default toaster message')).toBeInTheDocument();\n    expect(screen.queryByText('Third toaster message')).toBeInTheDocument();\n\n    // Dismiss all toasts\n    act(() => {\n      toast.dismissAll();\n    });\n\n    waitTime(REMOVE_DELAY);\n\n    expect(\n      screen.queryByText('Default toaster message')\n    ).not.toBeInTheDocument();\n    expect(\n      screen.queryByText('Second toaster message')\n    ).not.toBeInTheDocument();\n    expect(screen.queryByText('Third toaster message')).not.toBeInTheDocument();\n  });\n\n  test('updates a toast in a specific toaster without affecting others', () => {\n    render(\n      <>\n        <Toaster containerClassName=\"default-toaster\" />\n        <Toaster\n          toasterId=\"updatable-toaster\"\n          containerClassName=\"updatable-toaster\"\n        />\n      </>\n    );\n\n    let toastId: string;\n\n    // Create a loading toast in the second toaster\n    act(() => {\n      toastId = toast.loading('Please wait...', {\n        toasterId: 'updatable-toaster',\n      });\n    });\n\n    const secondContainer = document.querySelector('.updatable-toaster');\n    expect(secondContainer).toContainElement(\n      screen.getByText('Please wait...')\n    );\n\n    // Now update that toast to success\n    act(() => {\n      // Note that we are not providing a toasterId here\n      toast.success('Data saved!', {\n        id: toastId,\n      });\n    });\n\n    // Confirm the updated text\n    expect(screen.queryByText('Please wait...')).not.toBeInTheDocument();\n    expect(secondContainer).toContainElement(screen.getByText('Data saved!'));\n  });\n\n  test('dismisses all toasts from a specific toaster and leaves others intact', () => {\n    render(\n      <>\n        <Toaster containerClassName=\"default-toaster\" />\n        <Toaster toasterId=\"other-toaster\" containerClassName=\"other-toaster\" />\n      </>\n    );\n\n    // Create one toast in each toaster\n    act(() => {\n      toast.success('Default toaster toast');\n      toast.success('Other toaster toast', { toasterId: 'other-toaster' });\n    });\n\n    // Ensure both appear\n    expect(screen.getByText('Default toaster toast')).toBeInTheDocument();\n    expect(screen.getByText('Other toaster toast')).toBeInTheDocument();\n\n    // Dismiss only the second toaster's toasts\n    act(() => {\n      toast.dismissAll('other-toaster');\n    });\n    waitTime(REMOVE_DELAY);\n\n    // The other toaster's toast should be gone, default remains\n    expect(screen.queryByText('Other toaster toast')).not.toBeInTheDocument();\n    expect(screen.queryByText('Default toaster toast')).toBeInTheDocument();\n  });\n\n  test('dismisses all toasts across all toasters with dismissAll', () => {\n    render(\n      <>\n        <Toaster containerClassName=\"default-toaster\" />\n        <Toaster toasterId=\"other-toaster\" containerClassName=\"other-toaster\" />\n      </>\n    );\n\n    // Create one toast in each toaster\n    act(() => {\n      toast.success('Default toaster toast');\n      toast.error('Other toaster toast', { toasterId: 'other-toaster' });\n    });\n\n    // Dismiss every toast in all toasters\n    act(() => {\n      toast.dismissAll();\n    });\n    waitTime(REMOVE_DELAY);\n\n    // Both should be removed\n    expect(screen.queryByText('Default toaster toast')).not.toBeInTheDocument();\n    expect(screen.queryByText('Other toaster toast')).not.toBeInTheDocument();\n  });\n\n  test('removes toasts immediately when calling toast.remove()', () => {\n    render(\n      <>\n        <Toaster toasterId=\"instant-remove-toaster\" />\n        <Toaster toasterId=\"another-toaster\" />\n      </>\n    );\n\n    act(() => {\n      toast.success('Removable toast #1', {\n        toasterId: 'instant-remove-toaster',\n      });\n      toast.error('Removable toast #2', { toasterId: 'another-toaster' });\n    });\n\n    expect(screen.queryByText('Removable toast #1')).toBeInTheDocument();\n    expect(screen.queryByText('Removable toast #2')).toBeInTheDocument();\n\n    act(() => {\n      toast.removeAll('instant-remove-toaster');\n    });\n\n    expect(screen.queryByText('Removable toast #1')).not.toBeInTheDocument();\n    expect(screen.queryByText('Removable toast #2')).toBeInTheDocument();\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs\n  \"include\": [\"src\", \"types\", \"test\"],\n  \"compilerOptions\": {\n    \"module\": \"esnext\",\n    \"types\": [\"jest\", \"@testing-library/jest-dom\"],\n    \"lib\": [\"dom\", \"esnext\"],\n    // output .d.ts declaration files for consumers\n    \"declaration\": true,\n    // output .js.map sourcemap files for consumers\n    \"sourceMap\": true,\n    // match output dir to input dir. e.g. dist/index instead of dist/src/index\n    \"rootDir\": \"./src\",\n    // stricter type-checking for stronger correctness. Recommended by TS\n    \"strict\": true,\n    // linter checks for common issues\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative\n    \"noUnusedParameters\": true,\n    // use Node's module resolution algorithm, instead of the legacy TS one\n    \"moduleResolution\": \"node\",\n    // transpile JSX to React.createElement\n    \"jsx\": \"react\",\n    // interop between ESM and CJS modules. Recommended by TS\n    \"esModuleInterop\": true,\n    // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS\n    \"skipLibCheck\": true,\n    // error out if import and file system have a casing mismatch. Recommended by TS\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n  }\n}\n"
  },
  {
    "path": "tsup.config.ts",
    "content": "import { defineConfig, Options } from 'tsup';\nimport { minifyTemplates, writeFiles } from 'esbuild-minify-templates';\n\nconst commonConfig: Options = {\n  minify: true,\n  dts: true,\n  format: ['esm', 'cjs'],\n  sourcemap: true,\n  clean: true,\n  esbuildPlugins: [\n    minifyTemplates({ taggedOnly: false }),\n    writeFiles(),\n  ],\n};\nexport default defineConfig([\n  {\n    ...commonConfig,\n    esbuildOptions: (options) => {\n      // Append \"use client\" to the top of the react entry point\n      options.banner = {\n        js: '\"use client\";',\n      };\n    },\n    entry: ['src/index.ts'],\n    outDir: 'dist',\n  },\n  {\n    ...commonConfig,\n    entry: ['src/headless/index.ts'],\n    outDir: 'headless',\n  },\n]);\n"
  }
]