[
  {
    "path": ".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\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"
  },
  {
    "path": "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": "jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"node_modules\",\n    \"paths\": {\n      \"@/*\": [\"../src/*\"]\n    }\n  }\n}\n"
  },
  {
    "path": "next.config.js",
    "content": "const { createLoader } = require('simple-functional-loader')\nconst withBundleAnalyzer = require('@next/bundle-analyzer')({\n  enabled: process.env.ANALYZE === 'true',\n})\nconst withSyntaxHighlighting = require('./remark/withSyntaxHighlighting')\nconst withProse = require('./remark/withProse')\n\nmodule.exports = withBundleAnalyzer({\n  pageExtensions: ['js', 'jsx', 'mdx'],\n  experimental: {\n    modern: true,\n  },\n  images: {\n    disableStaticImages: true,\n  },\n  webpack: (config, options) => {\n    config.module.rules.push({\n      test: /\\.(svg|png|jpe?g|gif|mp4)$/i,\n      use: [\n        {\n          loader: 'file-loader',\n          options: {\n            publicPath: '/_next',\n            name: 'static/media/[name].[hash].[ext]',\n          },\n        },\n      ],\n    })\n\n    const mdx = [\n      options.defaultLoaders.babel,\n      {\n        loader: '@mdx-js/loader',\n        options: {\n          remarkPlugins: [withProse, withSyntaxHighlighting],\n        },\n      },\n    ]\n\n    config.module.rules.push({\n      test: /\\.mdx$/,\n      oneOf: [\n        {\n          test: /snippets/,\n          use: mdx,\n        },\n        {\n          resourceQuery: /preview/,\n          use: [\n            ...mdx,\n            createLoader(function (src) {\n              if (src.includes('<!--more-->')) {\n                const [preview] = src.split('<!--more-->')\n                return this.callback(null, preview)\n              }\n\n              const [preview] = src.split('<!--/excerpt-->')\n              return this.callback(null, preview.replace('<!--excerpt-->', ''))\n            }),\n          ],\n        },\n        {\n          resourceQuery: /rss/,\n          use: mdx,\n        },\n        {\n          use: [\n            ...mdx,\n            createLoader(function (src) {\n              const content = [\n                'import Post from \"@/components/Post\"',\n                'export { getStaticProps } from \"@/getStaticProps\"',\n                src,\n                'export default Post',\n              ].join('\\n')\n\n              if (content.includes('<!--more-->')) {\n                return this.callback(null, content.split('<!--more-->').join('\\n'))\n              }\n\n              return this.callback(null, content.replace(/<!--excerpt-->.*<!--\\/excerpt-->/s, ''))\n            }),\n          ],\n        },\n      ],\n    })\n\n    if (!options.dev && options.isServer) {\n      const originalEntry = config.entry\n\n      config.entry = async () => {\n        const entries = { ...(await originalEntry()) }\n        entries['scripts/build-rss'] = './scripts/build-rss.js'\n        return entries\n      }\n    }\n\n    return config\n  },\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"tailwind-blog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build && next export && npm run build:rss\",\n    \"start\": \"next start\",\n    \"build:rss\": \"node ./.next/server/scripts/build-rss.js\"\n  },\n  \"dependencies\": {\n    \"@headlessui/react\": \"^1.4.0\",\n    \"@mdx-js/loader\": \"^1.6.22\",\n    \"@tailwindcss/aspect-ratio\": \"^0.2.1\",\n    \"@tailwindcss/typography\": \"^0.4.0\",\n    \"autoprefixer\": \"^10.2.5\",\n    \"next\": \"^11.1.2\",\n    \"postcss\": \"^8.2.8\",\n    \"prismjs\": \"^1.25.0\",\n    \"react\": \"^17.0.2\",\n    \"react-dom\": \"^17.0.2\",\n    \"tailwindcss\": \"^2.2.0-canary.16\",\n    \"tinytime\": \"^0.2.6\"\n  },\n  \"devDependencies\": {\n    \"@next/bundle-analyzer\": \"^10.0.8\",\n    \"feed\": \"^4.2.1\",\n    \"file-loader\": \"^6.0.0\",\n    \"simple-functional-loader\": \"^1.2.1\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "prettier.config.js",
    "content": "module.exports = {\n  semi: false,\n  singleQuote: true,\n  printWidth: 100,\n  tabWidth: 2,\n  useTabs: false,\n  trailingComma: 'es5',\n  bracketSpacing: true,\n}\n"
  },
  {
    "path": "public/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo src=\"/mstile-150x150.png\"/>\n            <TileColor>#00aba9</TileColor>\n        </tile>\n    </msapplication>\n</browserconfig>\n"
  },
  {
    "path": "public/site.webmanifest",
    "content": "{\n    \"name\": \"\",\n    \"short_name\": \"\",\n    \"icons\": [\n        {\n            \"src\": \"/android-chrome-192x192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"/android-chrome-512x512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        }\n    ],\n    \"theme_color\": \"#ffffff\",\n    \"background_color\": \"#ffffff\",\n    \"display\": \"standalone\"\n}\n"
  },
  {
    "path": "remark/vendor/prism-diff-highlight.js",
    "content": "// https://github.com/PrismJS/prism/blob/master/plugins/diff-highlight/prism-diff-highlight.js\nmodule.exports = (Prism) => {\n  var LANGUAGE_REGEX = /diff-([\\w-]+)/i\n  var HTML_TAG =\n    /<\\/?(?!\\d)[^\\s>\\/=$<%]+(?:\\s(?:\\s*[^\\s>\\/=]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))|(?=[\\s/>])))+)?\\s*\\/?>/gi\n  //this will match a line plus the line break while ignoring the line breaks HTML tags may contain.\n  var HTML_LINE = RegExp(\n    /(?:__|[^\\r\\n<])*(?:\\r\\n?|\\n|(?:__|[^\\r\\n<])(?![^\\r\\n]))/.source.replace(/__/g, function () {\n      return HTML_TAG.source\n    }),\n    'gi'\n  )\n\n  var PREFIXES = Prism.languages.diff.PREFIXES\n\n  Prism.hooks.add('before-sanity-check', function (env) {\n    var lang = env.language\n    if (LANGUAGE_REGEX.test(lang) && !env.grammar) {\n      env.grammar = Prism.languages[lang] = Prism.languages['diff']\n    }\n  })\n  Prism.hooks.add('before-tokenize', function (env) {\n    var lang = env.language\n    if (LANGUAGE_REGEX.test(lang) && !Prism.languages[lang]) {\n      Prism.languages[lang] = Prism.languages['diff']\n    }\n  })\n\n  Prism.hooks.add('wrap', function (env) {\n    var diffLanguage, diffGrammar\n\n    if (env.language !== 'diff') {\n      var langMatch = LANGUAGE_REGEX.exec(env.language)\n      if (!langMatch) {\n        return // not a language specific diff\n      }\n\n      diffLanguage = langMatch[1]\n      diffGrammar = Prism.languages[diffLanguage]\n    }\n\n    // one of the diff tokens without any nested tokens\n    if (env.type in PREFIXES) {\n      /** @type {string} */\n      var content = env.content.replace(HTML_TAG, '') // remove all HTML tags\n\n      /** @type {string} */\n      var decoded = content.replace(/&lt;/g, '<').replace(/&amp;/g, '&')\n\n      // remove any one-character prefix\n      var code = decoded.replace(/(^|[\\r\\n])./g, '$1')\n\n      // highlight, if possible\n      var highlighted\n      if (diffGrammar) {\n        highlighted = Prism.highlight(code, diffGrammar, diffLanguage)\n      } else {\n        highlighted = Prism.util.encode(code)\n      }\n\n      // get the HTML source of the prefix token\n      var prefixToken = new Prism.Token('prefix', PREFIXES[env.type], [/\\w+/.exec(env.type)[0]])\n      var prefix = Prism.Token.stringify(prefixToken, env.language)\n\n      // add prefix\n      var lines = [],\n        m\n      HTML_LINE.lastIndex = 0\n      while ((m = HTML_LINE.exec(highlighted))) {\n        lines.push(prefix + m[0])\n      }\n      if (/(?:^|[\\r\\n]).$/.test(decoded)) {\n        // because both \"+a\\n+\" and \"+a\\n\" will map to \"a\\n\" after the line prefixes are removed\n        lines.push(prefix)\n      }\n      env.content = lines.join('')\n\n      if (diffGrammar) {\n        env.classes.push('language-' + diffLanguage)\n      }\n    }\n  })\n}\n"
  },
  {
    "path": "remark/withProse.js",
    "content": "const proseComponents = ['Heading']\n\nconst isJsNode = (node) => {\n  return (\n    ['jsx', 'import', 'export'].includes(node.type) &&\n    !/^<[a-z]+(>|\\s)/.test(node.value) &&\n    !new RegExp(`^<(${proseComponents.join('|')})(>|\\\\s)`).test(node.value)\n  )\n}\n\nmodule.exports = function () {\n  return (tree) => {\n    let insideProse = false\n    tree.children = tree.children.flatMap((node, i) => {\n      if (insideProse && isJsNode(node)) {\n        insideProse = false\n        return [{ type: 'jsx', value: '</div>' }, node]\n      }\n      if (!insideProse && !isJsNode(node)) {\n        insideProse = true\n        return [\n          { type: 'jsx', value: '<div className=\"prose max-w-none\">' },\n          node,\n          ...(i === tree.children.length - 1 ? [{ type: 'jsx', value: '</div>' }] : []),\n        ]\n      }\n      if (i === tree.children.length - 1 && insideProse) {\n        return [node, { type: 'jsx', value: '</div>' }]\n      }\n      return [node]\n    })\n  }\n}\n"
  },
  {
    "path": "remark/withSyntaxHighlighting.js",
    "content": "const visit = require('unist-util-visit')\nconst Prism = require('prismjs')\nconst loadLanguages = require('prismjs/components/')\nloadLanguages()\nrequire('./vendor/prism-diff-highlight')(Prism)\n\nfunction highlightCode(code, prismLanguage) {\n  const isDiff = prismLanguage.startsWith('diff-')\n  const language = isDiff ? prismLanguage.substr(5) : prismLanguage\n  const grammar = Prism.languages[isDiff ? 'diff' : language]\n  if (!grammar) {\n    console.warn(`Unrecognised language: ${prismLanguage}`)\n    return Prism.util.encode(code)\n  }\n  let highlighted = Prism.highlight(code, grammar, prismLanguage)\n\n  return language === 'html'\n    ? highlighted.replace(\n        /\\*\\*(.*?)\\*\\*/g,\n        (_, text) => `<span class=\"code-highlight bg-code-highlight\">${text}</span>`\n      )\n    : highlighted\n}\n\nmodule.exports = function withSyntaxHighlighting() {\n  return (tree) => {\n    visit(tree, 'code', (node) => {\n      if (node.lang !== null) {\n        node.type = 'html'\n        node.value = [\n          `<pre class=\"language-${node.lang}\">`,\n          `<code class=\"language-${node.lang}\">`,\n          highlightCode(node.value, node.lang),\n          '</code>',\n          '</pre>',\n        ]\n          .filter(Boolean)\n          .join('')\n      }\n    })\n  }\n}\n"
  },
  {
    "path": "scripts/build-rss.js",
    "content": "import fs from 'fs'\nimport ReactDOMServer from 'react-dom/server'\nimport { MDXProvider } from '@mdx-js/react'\nimport { Feed } from 'feed'\n\nimport { getAllPosts } from '../src/getAllPostPreviews'\n\nconst siteUrl = 'https://blog.tailwindcss.com'\n\nconst feed = new Feed({\n  title: 'Tailwind CSS Blog',\n  description: 'All the latest Tailwind CSS news, straight from the team.',\n  id: siteUrl,\n  link: siteUrl,\n  language: 'en',\n  image: `${siteUrl}/favicon-32x32.png`,\n  favicon: `${siteUrl}/favicon.ico`,\n  copyright: `All rights reserved ${new Date().getFullYear()}, Tailwind Labs`,\n  feedLinks: {\n    rss: `${siteUrl}/feed.xml`,\n    json: `${siteUrl}/feed.json`,\n    atom: `${siteUrl}/atom.xml`,\n  },\n  author: {\n    name: 'Adam Wathan',\n    link: 'https://twitter.com/@adamwathan',\n  },\n})\n\ngetAllPosts().forEach(({ link, module: { meta, default: Content } }) => {\n  const mdx = (\n    <MDXProvider>\n      <Content />\n    </MDXProvider>\n  )\n  const html = ReactDOMServer.renderToStaticMarkup(mdx)\n  const postText = `<p><em>(The post <a href=\"${siteUrl + link}\">${\n    meta.title\n  }</a> appeared first on <a href=\"${siteUrl}\">Tailwind CSS Blog</a>.)</em></p>`\n  feed.addItem({\n    title: meta.title,\n    id: meta.title,\n    link,\n    description: meta.description,\n    content: html + postText,\n    author: meta.authors.map(({ name, twitter }) => ({\n      name,\n      link: `https://twitter.com/${twitter}`,\n    })),\n    date: new Date(meta.date),\n    image: siteUrl + meta.image,\n    ...(meta.discussion\n      ? {\n          comments: meta.discussion,\n          extensions: [\n            {\n              name: '_comments',\n              objects: {\n                about: 'Link to discussion forum',\n                comments: meta.discussion,\n              },\n            },\n          ],\n        }\n      : {}),\n  })\n})\n\nfs.writeFileSync('./out/feed.xml', feed.rss2())\nfs.writeFileSync('./out/atom.xml', feed.atom1())\nfs.writeFileSync('./out/feed.json', feed.json1())\n"
  },
  {
    "path": "src/authors.js",
    "content": "import adamwathanAvatar from './img/adamwathan.jpg'\nimport bradlcAvatar from './img/bradlc.jpg'\nimport steveschogerAvatar from './img/steveschoger.jpg'\nimport robinmalfaitAvatar from './img/robinmalfait.jpg'\nimport simonswissAvatar from './img/simonswiss.jpg'\n\nexport const adamwathan = {\n  name: 'Adam Wathan',\n  twitter: 'adamwathan',\n  avatar: adamwathanAvatar,\n}\n\nexport const bradlc = {\n  name: 'Brad Cornes',\n  twitter: 'bradlc',\n  avatar: bradlcAvatar,\n}\n\nexport const steveschoger = {\n  name: 'Steve Schoger',\n  twitter: 'steveschoger',\n  avatar: steveschogerAvatar,\n}\n\nexport const robinmalfait = {\n  name: 'Robin Malfait',\n  twitter: 'malfaitrobin',\n  avatar: robinmalfaitAvatar,\n}\n\nexport const simonswiss = {\n  name: 'Simon Vrachliotis',\n  twitter: 'simonswiss',\n  avatar: simonswissAvatar,\n}\n"
  },
  {
    "path": "src/components/Header.js",
    "content": "import Link from 'next/link'\n\nexport function TailwindMark({ className }) {\n  return (\n    <svg className={className} fill=\"none\" viewBox=\"0 0 55 33\">\n      <path fill=\"#fff\" d=\"M0 0h55v33H0z\" />\n      <path\n        fill=\"#06B6D4\"\n        fillRule=\"evenodd\"\n        d=\"M27.5 0c-7.333 0-11.917 3.667-13.75 11 2.75-3.667 5.958-5.042 9.625-4.125 2.092.523 3.587 2.04 5.242 3.72 2.697 2.737 5.817 5.905 12.633 5.905 7.333 0 11.917-3.667 13.75-11-2.75 3.667-5.958 5.042-9.625 4.125-2.092-.523-3.587-2.04-5.242-3.72C37.436 3.166 34.316 0 27.5 0zM13.75 16.5c-7.333 0-11.917 3.667-13.75 11 2.75-3.667 5.958-5.042 9.625-4.125 2.092.523 3.587 2.04 5.242 3.72C17.564 29.834 20.684 33 27.5 33c7.333 0 11.917-3.667 13.75-11-2.75 3.667-5.958 5.042-9.625 4.125-2.092-.523-3.587-2.04-5.242-3.72-2.697-2.738-5.817-5.905-12.633-5.905z\"\n        clipRule=\"evenodd\"\n      />\n    </svg>\n  )\n}\n\nexport function TailwindLogo({ className }) {\n  return (\n    <svg className={className} fill=\"none\" viewBox=\"0 0 285 33\">\n      <path\n        fill=\"#06B6D4\"\n        fillRule=\"evenodd\"\n        d=\"M27.5 0c-7.333 0-11.917 3.667-13.75 11 2.75-3.667 5.958-5.042 9.625-4.125 2.092.523 3.587 2.04 5.242 3.72 2.697 2.737 5.817 5.905 12.633 5.905 7.333 0 11.917-3.667 13.75-11-2.75 3.667-5.958 5.042-9.625 4.125-2.092-.523-3.587-2.04-5.242-3.72C37.436 3.166 34.316 0 27.5 0zM13.75 16.5c-7.333 0-11.917 3.667-13.75 11 2.75-3.667 5.958-5.042 9.625-4.125 2.092.523 3.587 2.04 5.242 3.72C17.564 29.834 20.684 33 27.5 33c7.333 0 11.917-3.667 13.75-11-2.75 3.667-5.958 5.042-9.625 4.125-2.092-.523-3.587-2.04-5.242-3.72-2.697-2.738-5.817-5.905-12.633-5.905z\"\n        clipRule=\"evenodd\"\n      />\n      <path\n        fill=\"#374151\"\n        d=\"M80.496 14.692V10.74h-4.712V4.42L71.68 5.636v5.104h-3.496v3.952h3.496v9.12c0 4.94 2.508 6.688 8.816 5.928v-3.686c-3.116.152-4.712.19-4.712-2.242v-9.12h4.712zM98.487 10.74v2.698c-1.444-1.976-3.686-3.192-6.65-3.192-5.168 0-9.462 4.332-9.462 9.994 0 5.624 4.294 9.994 9.462 9.994 2.964 0 5.206-1.216 6.65-3.23v2.736h4.104v-19h-4.104zm-6.004 15.58c-3.42 0-6.004-2.546-6.004-6.08 0-3.534 2.584-6.08 6.004-6.08 3.42 0 6.004 2.546 6.004 6.08 0 3.534-2.584 6.08-6.004 6.08zM109.426 7.89c1.444 0 2.622-1.216 2.622-2.622a2.627 2.627 0 00-2.622-2.622 2.627 2.627 0 00-2.622 2.622c0 1.406 1.178 2.622 2.622 2.622zm-2.052 21.85h4.104v-19h-4.104v19zM116.244 29.74h4.104V2h-4.104v27.74zM147.001 10.74l-3.724 13.11-3.952-13.11h-3.914l-3.99 13.11-3.686-13.11h-4.332l5.966 19h4.028l3.99-12.806 3.952 12.806h4.028l5.966-19h-4.332zM156.407 7.89c1.444 0 2.622-1.216 2.622-2.622a2.627 2.627 0 00-2.622-2.622 2.627 2.627 0 00-2.622 2.622c0 1.406 1.178 2.622 2.622 2.622zm-2.052 21.85h4.104v-19h-4.104v19zM173.218 10.246c-2.584 0-4.636.95-5.89 2.926V10.74h-4.104v19h4.104V19.556c0-3.876 2.128-5.472 4.826-5.472 2.584 0 4.256 1.52 4.256 4.408V29.74h4.104V18.074c0-4.94-3.04-7.828-7.296-7.828zM199.981 3.14v10.298c-1.444-1.976-3.686-3.192-6.65-3.192-5.168 0-9.462 4.332-9.462 9.994 0 5.624 4.294 9.994 9.462 9.994 2.964 0 5.206-1.216 6.65-3.23v2.736h4.104V3.14h-4.104zm-6.004 23.18c-3.42 0-6.004-2.546-6.004-6.08 0-3.534 2.584-6.08 6.004-6.08 3.42 0 6.004 2.546 6.004 6.08 0 3.534-2.584 6.08-6.004 6.08z\"\n      />\n      <path\n        fill=\"#06B6D4\"\n        d=\"M221.249 19.884c1.113-.895 1.792-2.172 1.792-3.8 0-3.177-2.579-5.349-5.783-5.349h-7.493V29.74h8.063c3.285 0 5.919-2.253 5.919-5.511 0-1.982-.977-3.476-2.498-4.345zm-3.991-5.647c1.195 0 2.036.896 2.036 2.09 0 1.195-.868 2.091-2.036 2.091h-3.747v-4.18h3.747zm.57 12h-4.317v-4.48h4.317c1.276 0 2.199.951 2.199 2.227 0 1.303-.923 2.254-2.199 2.254zM232.122 26.156V10.735h-3.747V29.74h10.996v-3.584h-7.249zM251.492 30.12c5.484 0 9.882-4.344 9.882-9.883 0-5.565-4.398-9.882-9.882-9.882-5.485 0-9.856 4.317-9.856 9.883 0 5.538 4.371 9.882 9.856 9.882zm0-3.665c-3.448 0-6.136-2.58-6.136-6.217 0-3.666 2.688-6.245 6.136-6.245s6.135 2.58 6.135 6.245c0 3.638-2.687 6.217-6.135 6.217zM284.832 19.532h-9.53v3.312h5.756c-.652 2.172-2.552 3.61-5.539 3.61-3.964 0-6.489-2.66-6.489-6.19 0-3.61 2.607-6.271 6.218-6.271 2.307 0 4.235 1.113 5.104 2.633l3.204-1.846c-1.602-2.606-4.67-4.425-8.281-4.425-5.675 0-9.964 4.398-9.964 9.91 0 5.43 4.235 9.855 10.181 9.855 5.457 0 9.34-3.638 9.34-9.122v-1.466z\"\n      />\n    </svg>\n  )\n}\n\nexport default function Header() {\n  return (\n    <header className=\"flex justify-between items-center py-10\">\n      <div>\n        <Link href=\"/\">\n          <a aria-label=\"Tailwind CSS Blog\">\n            <TailwindMark className=\"h-6 sm:hidden\" />\n            <TailwindLogo className=\"hidden sm:block h-6\" />\n          </a>\n        </Link>\n      </div>\n      <div className=\"text-base leading-5\">\n        <a\n          href=\"https://tailwindcss.com/docs\"\n          className=\"font-medium text-gray-500 hover:text-gray-700\"\n        >\n          Documentation &rarr;\n        </a>\n      </div>\n    </header>\n  )\n}\n"
  },
  {
    "path": "src/components/PageTitle.js",
    "content": "export default function PageTitle({ children }) {\n  return (\n    <h1 className=\"text-3xl font-extrabold text-gray-900 tracking-tight sm:text-4xl md:text-5xl md:leading-[3.5rem]\">\n      {children.replace(/ ([^ ]+)$/, '\\u00A0$1')}\n    </h1>\n  )\n}\n"
  },
  {
    "path": "src/components/Post.js",
    "content": "import Head from 'next/head'\nimport PageTitle from '@/components/PageTitle'\nimport tinytime from 'tinytime'\nimport Link from 'next/link'\nimport { useRouter } from 'next/router'\nimport { MDXProvider } from '@mdx-js/react'\nimport Header, { TailwindMark } from '@/components/Header'\nimport SectionContainer from '@/components/SectionContainer'\nimport smallCard from '@/img/twitter-card-small.jpg'\n\nconst postDateTemplate = tinytime('{dddd}, {MMMM} {DD}, {YYYY}')\n\nexport default function Post({ meta, children, posts }) {\n  const router = useRouter()\n\n  if (meta.private) {\n    return (\n      <>\n        <SectionContainer>\n          <main>\n            <article className=\"py-16\">\n              <Head>\n                <title>{meta.title} – Tailwind CSS</title>\n                <meta name=\"twitter:site\" content=\"@tailwindcss\" />\n                <meta name=\"twitter:creator\" content=\"@tailwindcss\" />\n                <meta name=\"twitter:title\" content={`${meta.title} – Tailwind CSS`} />\n                <meta name=\"twitter:description\" content={meta.description} />\n                {meta.image ? (\n                  <>\n                    <meta name=\"twitter:card\" content=\"summary_large_image\" />\n                    <meta\n                      name=\"twitter:image\"\n                      content={`https://blog.tailwindcss.com${meta.image}`}\n                    />\n                  </>\n                ) : (\n                  <>\n                    <meta name=\"twitter:card\" content=\"summary\" />\n                    <meta\n                      name=\"twitter:image\"\n                      content={`https://blog.tailwindcss.com${smallCard}`}\n                    />\n                  </>\n                )}\n                <meta\n                  property=\"og:url\"\n                  content={`https://blog.tailwindcss.com${router.pathname}`}\n                />\n                <meta property=\"og:type\" content=\"article\" />\n                <meta property=\"og:title\" content={`${meta.title} – Tailwind CSS`} />\n                <meta property=\"og:description\" content={meta.description} />\n                <meta property=\"og:image\" content={`https://blog.tailwindcss.com${meta.image}`} />\n                <meta name=\"description\" content={meta.description}></meta>\n              </Head>\n              <header className=\"\">\n                <div className=\"text-center\">\n                  <div className=\"flex justify-center\">\n                    <Link href=\"/\">\n                      <a className=\"inline-flex\">\n                        <span className=\"sr-only\">All posts</span>\n                        <TailwindMark className=\"h-12 w-12\" />\n                      </a>\n                    </Link>\n                  </div>\n                  <dl className=\"mt-4 space-y-10\">\n                    <div>\n                      <dt className=\"sr-only\">Published on</dt>\n                      <dd className=\"text-base font-medium text-gray-500\">\n                        <time dateTime={meta.date}>\n                          {postDateTemplate.render(new Date(meta.date))}\n                        </time>\n                      </dd>\n                    </div>\n                  </dl>\n                  <div className=\"mt-1\">\n                    <h1 className=\"text-2xl leading-8 font-extrabold text-gray-900 tracking-tight sm:text-3xl sm:leading-9\">\n                      {meta.title.replace(/ ([^ ]+)$/, '\\u00A0$1')}\n                    </h1>\n                  </div>\n                  <dl className=\"mt-4\">\n                    <dt className=\"sr-only\">Authors</dt>\n                    <dd>\n                      <ul className=\"flex items-center justify-center\">\n                        {meta.authors.map((author) => (\n                          <li key={author.twitter} className=\"flex items-center space-x-2\">\n                            <img src={author.avatar} alt=\"\" className=\"w-8 h-8 rounded-full\" />\n                            <dl className=\"text-sm font-medium whitespace-no-wrap\">\n                              <dt className=\"sr-only\">Name</dt>\n                              <dd className=\"text-gray-900\">{author.name}</dd>\n                            </dl>\n                          </li>\n                        ))}\n                      </ul>\n                    </dd>\n                  </dl>\n                </div>\n              </header>\n              <div className=\"mt-12\">\n                <div className=\"prose mx-auto\">\n                  <MDXProvider>{children}</MDXProvider>\n                </div>\n              </div>\n            </article>\n          </main>\n        </SectionContainer>\n      </>\n    )\n  }\n\n  const postIndex = posts.findIndex((post) => post.link === router.pathname)\n  const previous = posts[postIndex + 1]\n  const next = posts[postIndex - 1]\n\n  return (\n    <>\n      <SectionContainer>\n        <Header />\n      </SectionContainer>\n      <SectionContainer>\n        <main>\n          <article className=\"xl:divide-y xl:divide-gray-200\">\n            <Head>\n              <title>{meta.title} – Tailwind CSS</title>\n              <meta name=\"twitter:card\" content=\"summary_large_image\" />\n              <meta name=\"twitter:site\" content=\"@tailwindcss\" />\n              <meta name=\"twitter:creator\" content=\"@tailwindcss\" />\n              <meta name=\"twitter:title\" content={`${meta.title} – Tailwind CSS`} />\n              <meta name=\"twitter:description\" content={meta.description} />\n              <meta name=\"twitter:image\" content={`https://blog.tailwindcss.com${meta.image}`} />\n              <meta property=\"og:url\" content={`https://blog.tailwindcss.com${router.pathname}`} />\n              <meta property=\"og:type\" content=\"article\" />\n              <meta property=\"og:title\" content={`${meta.title} – Tailwind CSS`} />\n              <meta property=\"og:description\" content={meta.description} />\n              <meta property=\"og:image\" content={`https://blog.tailwindcss.com${meta.image}`} />\n              <meta name=\"description\" content={meta.description}></meta>\n            </Head>\n            <header className=\"pt-6 xl:pb-10\">\n              <div className=\"space-y-1 text-center\">\n                <dl className=\"space-y-10\">\n                  <div>\n                    <dt className=\"sr-only\">Published on</dt>\n                    <dd className=\"text-base leading-6 font-medium text-gray-500\">\n                      <time dateTime={meta.date}>\n                        {postDateTemplate.render(new Date(meta.date))}\n                      </time>\n                    </dd>\n                  </div>\n                </dl>\n                <div>\n                  <PageTitle>{meta.title}</PageTitle>\n                </div>\n              </div>\n            </header>\n            <div\n              className=\"divide-y xl:divide-y-0 divide-gray-200 xl:grid xl:grid-cols-4 xl:gap-x-6 pb-16 xl:pb-20\"\n              style={{ gridTemplateRows: 'auto 1fr' }}\n            >\n              <dl className=\"pt-6 pb-10 xl:pt-11 xl:border-b xl:border-gray-200\">\n                <dt className=\"sr-only\">Authors</dt>\n                <dd>\n                  <ul className=\"flex justify-center xl:block space-x-8 sm:space-x-12 xl:space-x-0 xl:space-y-8\">\n                    {meta.authors.map((author) => (\n                      <li key={author.twitter} className=\"flex items-center space-x-2\">\n                        <img src={author.avatar} alt=\"\" className=\"w-10 h-10 rounded-full\" />\n                        <dl className=\"text-sm font-medium whitespace-no-wrap\">\n                          <dt className=\"sr-only\">Name</dt>\n                          <dd className=\"text-gray-900\">{author.name}</dd>\n                          <dt className=\"sr-only\">Twitter</dt>\n                          <dd>\n                            <a\n                              href={`https://twitter.com/${author.twitter}`}\n                              className=\"text-teal-600 hover:text-teal-700\"\n                            >\n                              @{author.twitter}\n                            </a>\n                          </dd>\n                        </dl>\n                      </li>\n                    ))}\n                  </ul>\n                </dd>\n              </dl>\n              <div className=\"divide-y divide-gray-200 xl:pb-0 xl:col-span-3 xl:row-span-2\">\n                <div className=\"max-w-none pt-10 pb-8\">\n                  <MDXProvider>{children}</MDXProvider>\n                </div>\n                {meta.footer && (\n                  <div className=\"pt-6 pb-16\" dangerouslySetInnerHTML={{ __html: meta.footer }} />\n                )}\n                {!meta.footer && meta.discussion && (\n                  <div className=\"pt-6 pb-16\">\n                    <p>\n                      Want to talk about this post?{' '}\n                      <a\n                        href={meta.discussion}\n                        className=\"font-medium text-teal-600 hover:text-teal-700\"\n                      >\n                        Discuss this on GitHub &rarr;\n                      </a>\n                    </p>\n                  </div>\n                )}\n              </div>\n              <footer className=\"text-sm font-medium divide-y divide-gray-200 xl:col-start-1 xl:row-start-2\">\n                {(next || previous) && (\n                  <div className=\"space-y-8 py-8\">\n                    {next && (\n                      <div>\n                        <h2 className=\"text-xs leading-5 tracking-wide uppercase text-gray-500\">\n                          Next Article\n                        </h2>\n                        <div className=\"text-teal-600 hover:text-teal-700\">\n                          <Link href={next.link}>\n                            <a>{next.title}</a>\n                          </Link>\n                        </div>\n                      </div>\n                    )}\n                    {previous && (\n                      <div>\n                        <h2 className=\"text-xs leading-5 tracking-wide uppercase text-gray-500\">\n                          Previous Article\n                        </h2>\n                        <div className=\"text-teal-600 hover:text-teal-700\">\n                          <Link href={previous.link}>\n                            <a>{previous.title}</a>\n                          </Link>\n                        </div>\n                      </div>\n                    )}\n                  </div>\n                )}\n                <div className=\"pt-8\">\n                  <Link href=\"/\">\n                    <a className=\"text-teal-600 hover:text-teal-700\">&larr; Back to the blog</a>\n                  </Link>\n                </div>\n              </footer>\n            </div>\n          </article>\n        </main>\n      </SectionContainer>\n    </>\n  )\n}\n"
  },
  {
    "path": "src/components/SectionContainer.js",
    "content": "export default function SectionContainer({ children }) {\n  return <div className=\"max-w-3xl mx-auto px-4 sm:px-6 xl:max-w-5xl xl:px-0\">{children}</div>\n}\n"
  },
  {
    "path": "src/css/prism.css",
    "content": ".language-javascript {\n  color: white;\n}\n\n.token.tag,\n.token.class-name,\n.token.selector,\n.token.selector .class,\n.token.function {\n  @apply text-fuchsia-400;\n}\n\n.token.attr-name,\n.token.keyword,\n.token.rule,\n.token.operator,\n.token.pseudo-class,\n.token.important {\n  @apply text-teal-400;\n}\n\n.token.attr-value,\n.token.class,\n.token.string,\n.token.number,\n.token.unit,\n.token.color {\n  @apply text-lime-300;\n}\n\n.token.punctuation,\n.token.module,\n.token.property {\n  @apply text-sky-200;\n}\n\n.token.atapply .token:not(.rule):not(.important) {\n  color: inherit;\n}\n\n.language-shell .token:not(.comment) {\n  color: inherit;\n}\n\n.language-css .token.function {\n  color: inherit;\n}\n\n.token.comment {\n  @apply text-gray-400;\n}\n\n.token.deleted:not(.prefix) {\n  @apply relative block -mx-4 px-4;\n}\n\n.token.deleted:not(.prefix)::after {\n  content: '';\n  @apply pointer-events-none absolute inset-0 block bg-rose-400 bg-opacity-25;\n}\n\n.token.deleted.prefix {\n  @apply text-gray-400 select-none;\n}\n\n.token.inserted:not(.prefix) {\n  @apply block bg-emerald-700 bg-opacity-50 -mx-4 px-4;\n}\n\n.token.inserted.prefix {\n  @apply text-emerald-200 text-opacity-75 select-none;\n}\n"
  },
  {
    "path": "src/css/tailwind.css",
    "content": "/* purgecss start ignore */\n@tailwind base;\n/* purgecss end ignore */\n\n@tailwind components;\n@tailwind utilities;\n\n@import './prism.css';\n"
  },
  {
    "path": "src/getAllPostPreviews.js",
    "content": "function importAll(r) {\n  return r.keys().map((fileName) => ({\n    link: fileName.substr(1).replace(/\\/index\\.mdx$/, ''),\n    module: r(fileName),\n  }))\n}\n\nfunction dateSortDesc(a, b) {\n  if (a > b) return -1\n  if (a < b) return 1\n  return 0\n}\n\nexport default function getAllPostPreviews() {\n  return importAll(require.context('./pages/?preview', true, /\\.mdx$/))\n    .filter((p) => !p.link.includes('/snippets/'))\n    .filter((p) => p.module.meta.private !== true)\n    .sort((a, b) => dateSortDesc(a.module.meta.date, b.module.meta.date))\n}\n\nexport function getAllPosts() {\n  return importAll(require.context('./pages/?rss', true, /\\.mdx$/))\n    .filter((p) => !p.link.includes('/snippets/'))\n    .filter((p) => p.module.meta.private !== true)\n    .sort((a, b) => dateSortDesc(a.module.meta.date, b.module.meta.date))\n}\n"
  },
  {
    "path": "src/getStaticProps.js",
    "content": "import getAllPostPreviews from '@/getAllPostPreviews'\n\nexport async function getStaticProps() {\n  return {\n    props: {\n      posts: getAllPostPreviews().map((post) => ({\n        title: post.module.meta.title,\n        link: post.link,\n      })),\n    },\n  }\n}\n"
  },
  {
    "path": "src/pages/_app.js",
    "content": "import '@/css/tailwind.css'\nimport Head from 'next/head'\n\nexport default function App({ Component, pageProps }) {\n  return (\n    <div className=\"antialiased\">\n      <Head>\n        <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\" />\n        <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\" />\n        <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\" />\n        <link rel=\"manifest\" href=\"/site.webmanifest\" />\n        <link rel=\"mask-icon\" href=\"/safari-pinned-tab.svg\" color=\"#5bbad5\" />\n        <meta name=\"msapplication-TileColor\" content=\"#00aba9\" />\n        <meta name=\"theme-color\" content=\"#ffffff\" />\n        <link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS 2.0\" href=\"/feed.xml\" />\n        <link rel=\"alternate\" type=\"application/atom+xml\" title=\"Atom 1.0\" href=\"/atom.xml\" />\n        <link rel=\"alternate\" type=\"application/json\" title=\"JSON Feed\" href=\"/feed.json\" />\n      </Head>\n      <Component {...pageProps} />\n    </div>\n  )\n}\n"
  },
  {
    "path": "src/pages/_document.js",
    "content": "import NextDocument, { Html, Head, Main, NextScript } from 'next/document'\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nclass InlineStylesHead extends Head {\n  getCssLinks(files) {\n    return files.sharedFiles\n      .filter((file) => /\\.css$/.test(file))\n      .filter((file) => fs.existsSync(path.join(process.cwd(), '.next', file)))\n      .map((file) => (\n        <style\n          key={file}\n          nonce={this.props.nonce}\n          data-href={`${this.context.assetPrefix}/_next/${file}`}\n          dangerouslySetInnerHTML={{\n            __html: fs.readFileSync(\n              path.join(process.cwd(), '.next', file),\n              'utf-8'\n            ),\n          }}\n        />\n      ))\n  }\n}\n\nexport default class Document extends NextDocument {\n  static async getInitialProps(ctx) {\n    const initialProps = await NextDocument.getInitialProps(ctx)\n    return { ...initialProps }\n  }\n\n  render() {\n    return (\n      <Html lang=\"en\">\n        <InlineStylesHead>\n          <link\n            rel=\"preload\"\n            href=\"/fonts/Inter-roman.var-latin.woff2?3.13\"\n            as=\"font\"\n            type=\"font/woff2\"\n            crossOrigin=\"true\"\n          />\n        </InlineStylesHead>\n        <body>\n          <Main />\n          <NextScript />\n        </body>\n      </Html>\n    )\n  }\n}\n"
  },
  {
    "path": "src/pages/building-react-and-vue-support-for-tailwind-ui/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport stackedListExample from './stacked-list-example.png'\n\nexport const meta = {\n  private: true,\n  title: 'Building React + Vue support for Tailwind UI',\n  description: `Hey! We're getting really close to releasing React + Vue support for Tailwind UI, so I thought it would be interesting to share some of the behind-the-scenes efforts that have gone into even making it possible.`,\n  date: '2021-04-12T19:45:00.0Z',\n  authors: [adamwathan],\n}\n\nHey! We're getting _really_ close to releasing React + Vue support for Tailwind UI, so I thought it would be interesting to share some of the behind-the-scenes efforts that have gone into even making it possible.\n\nGrab some popcorn...\n\n## The Backstory\n\nFrom the day we started working on Tailwind UI somewhere in mid-2019 I knew that ultimately it would be 10x more valuable to people if they could grab fully interactive examples built using their favorite JS framework. Trying to make that happen for the first release was way too ambitious though, so we had to figure out how to get there one step at a time.\n\nWe decided to focus on vanilla HTML first because it's totally universal, and even if something like JSX would be more helpful for some people, there are lots of existing tools out there for converting HTML to JSX that people could lean on already.\n\nWe also made the hard trade-off not to provide any JS for interactions like toggling a responsive menu or opening and closing a modal dialog in the first version. I felt like anything we provided would just do more harm than good, because there's no one JS framework that makes up the majority of the Tailwind user base. If we catered to React developers, we'd be making it harder to use for the 70% of people _not_ using React. If we catered to Vue developers, we'd be making it harder for the 70% of people _not_ using Vue. If we tried to write it in custom vanilla JS, well we'd be making it harder for literally everyone _(seriously do you have any idea how much code it takes to build a robust enter/leave transition system from scratch in JS?)_\n\nSo instead I just documented the different states using comments in the HTML, and left it to the end user to wire it up with their favorite JS framework. I know a lot of people love that about [Bulma](https://bulma.io), and I think it was a great approach for us to start with as well.\n\nBut once we felt like Tailwind UI was pretty fleshed out with hundreds of great examples, we decided it was time to tackle the JS problem and see what we could do.\n\n## What should it even be?\n\nAs an abstract concept adding \"JavaScript support\" to Tailwind UI sounds straightforward, but when you dig in to the details it is _not_. There are _so_ many decisions to make about what to even build, and so many trade-offs you have to consider when trying to make something useful for as many people as possible.\n\nI tossed the whole concept around in the back of my head for a full year while working on Tailwind UI before I actually had a plan I was happy with. Ultimately, these are the core values I decided on when designing a solution:\n\n1. **The promise of Tailwind UI is that it's just a code snippet** — it's easy to customize and adapt by directly editing the code. Any JS examples we provide need to respect this foundational idea.\n2. **The JS needs to be updateable**. Unlike the markup which we expect people to just totally own and edit to their heart's content, the JS needs to come from `node_modules` _somehow_, because building these things right is hard, there are going to be bugs, and we want to be able to fix them for people without asking them to copy a new code snippet. On top of that, we don't want people to have to carefully transport 200 lines of JS they didn't write around their codebase, and constantly worry about accidentally breaking some small implementation detail by mistake.\n3. **It just has to be better than vanilla HTML**. At the end of the day, the most important thing is that we make the existing experience _better_ for people using the JS frameworks we decide to add support for first. Any time I found myself frustrated by two competing trade-offs that made it hard to make something _perfect_, asking myself \"is this still strictly better and in no ways worse for framework X users than vanilla HTML?\" provided a lot of clarity.\n\nThe other thing that was really important to me is that none of the underlying JS stuff was proprietary or Tailwind UI-specific. To me, Tailwind UI is not a UI kit like Ant Design or Material UI — those are great projects but it's not what I wanted to build.\n\nTo me, Tailwind UI is a collection of _blueprints_, showing you how to build awesome stuff using tools that are _already available_ to you. If you want to use things exactly as they come off the shelf you totally can and you'll get great results. But you should also be able to use Tailwind UI as a helpful starting point, tweak it to the nines, and end up with something that feels uniquely _yours_, even if we gave you a boost at the beginning.\n\nSo before we could add JavaScript support to Tailwind UI, we needed to build some tools.\n\n## Building Headless UI\n\nYears ago I remember seeing Kent C. Dodds' [downshift](https://www.downshift-js.com) library and thinking _\"man, this is a cool concept — all of the complex behavior is tucked away in the library, but all of the actual markup and styling is left to the user\"_.\n\nThis sort of approach is the perfect fit for Tailwind philosophically, because the entire goal of Tailwind is to help you build totally custom designs more quickly. Tailwind + a library of JS components that abstract away all of the keyboard navigation and accessibility logic without including any design opinions would be such a powerful combo — it would let teams building totally custom UIs move almost as fast as teams who were content to use hard-to-customize, opinionated frameworks.\n\nWe looked to see if there were any other tools out there solving these same problems, and while there were a few awesome projects in the space ([Reach UI](https://reach.tech) and [Reakit](https://reakit.io) especially at the time, and [react-aria](https://react-spectrum.adobe.com/react-aria/) since starting on our own library, phenomenal work by all those folks), ultimately we decided that something so important for our company would be best to build and control ourselves.\n\nThere were two big reasons we ended up starting our own project:\n\n1. **We wanted the APIs to work well with a class-based styling solution like Tailwind.** A lot of the other tools out there expected you to write custom CSS to target the different bits of each component, which is very different than the workflow you use to style things with Tailwind. We wanted to design something that was very class-friendly.\n2. **We wanted to support multiple frameworks using a consistent API.** There are React libraries, Vue libraries, Angular libraries, and others, but each one is different, designed by different people with different tastes. We wanted something that would be as consistent as possible from framework to framework, so that the framework-specific examples in Tailwind UI wouldn't be radically different from each other.\n\nI was really excited about what we were going to end up with at the end, but holy crap this was going to be a lot of work.\n\n### Getting the ball rolling\n\nWe decided to call this project \"Headless UI\" and in August of last year [Robin Malfait](https://twitter.com/malfaitrobin) joined the team to work on it full-time, pretty much exclusively.\n\nThe very first thing he worked on was a `Transition` component for React that would allow you to add enter/leave animations to elements, entirely using classes, and was very inspired by the [`<transition>`](https://v3.vuejs.org/guide/transitions-enterleave.html#enter-leave-transitions) component in Vue:\n\n```jsx\n<Transition\n  show={isOpen}\n  enter=\"transition-opacity duration-75\"\n  enterFrom=\"opacity-0\"\n  enterTo=\"opacity-100\"\n  leave=\"transition-opacity duration-150\"\n  leaveFrom=\"opacity-100\"\n  leaveTo=\"opacity-0\"\n>\n  I will fade in and out\n</Transition>\n```\n\nThis is a great example of what I meant earlier when I said we really wanted to design components that were \"class-friendly\". This component makes it really easy to style your enter/leave transitions with regular old Tailwind utility classes, so it feels just like styling anything else in your app. It's also _not_ coupled to Tailwind in any way though, and you can use whatever classes you want!\n\nWe published the [first public release](https://blog.tailwindcss.com/headless-ui-unstyled-accessible-ui-components) in October, and it included React and Vue libraries with the first three components:\n\n- Menu Button (or dropdown)\n- Listbox (or custom select)\n- Switch (or toggle)\n\nWe landed on a set of APIs that used \"compound components\" to abstract away all of the complexity while communicating with each other via [context](https://reactjs.org/docs/context.html) (or [provide/inject](https://v3.vuejs.org/api/composition-api.html#provide-inject) in Vue).\n\nHere's what a custom dropdown looks like in React:\n\n```jsx\nimport { Menu } from '@headlessui/react'\n\nfunction MyDropdown() {\n  return (\n    <Menu as=\"div\" className=\"relative\">\n      <Menu.Button className=\"px-4 py-2 rounded bg-blue-600 text-white ...\">Options</Menu.Button>\n      <Menu.Items className=\"absolute mt-1 right-0\">\n        <Menu.Item>\n          {({ active }) => (\n            <a className={`${active && 'bg-blue-500 text-white'} ...`} href=\"/account-settings\">\n              Account settings\n            </a>\n          )}\n        </Menu.Item>\n        <Menu.Item>\n          {({ active }) => (\n            <a className={`${active && 'bg-blue-500 text-white'} ...`} href=\"/documentation\">\n              Documentation\n            </a>\n          )}\n        </Menu.Item>\n        <Menu.Item disabled>\n          <span className=\"opacity-75 ...\">Invite a friend (coming soon!)</span>\n        </Menu.Item>\n      </Menu.Items>\n    </Menu>\n  )\n}\n```\n\nYou'll notice that to do things like style the \"active\" dropdown item, we use a [render prop](https://reactjs.org/docs/render-props.html) (or a [scoped slot](https://v3.vuejs.org/guide/component-slots.html#scoped-slots) in Vue):\n\n```jsx\n<Menu.Item>\n  {({ active }) => (\n    <a className={`${active && 'bg-blue-500 text-white'} ...`} href=\"/documentation\">\n      Documentation\n    </a>\n  )}\n</Menu.Item>\n```\n\nRender props aren't as common as they used to be because hooks have replaced the need for them in many situations. But for this sort of problem where you need access to internal state that's managed by the component you're consuming, they are still the right (only?) solution, and very elegant.\n\n### Designing the right components\n\nAfter releasing the first version of Headless UI in October, we buckled down for a couple of months to release [Tailwind CSS v2.0](https://blog.tailwindcss.com/tailwindcss-v2), and then spent the last month of the year focused on bug fixes and lots of project house keeping before taking a break for the holidays.\n\nWhen we came back, we buckled down hard to get to work on actually adding React + Vue support to Tailwind UI itself, and the first thing we needed to was audit all of the interactive behavior we needed for the examples in Tailwind UI and figure out what Headless UI abstractions we needed to design.\n\nThis was actually a pretty interesting and challenging job, because it's really not always obvious how a certain design-specific interaction should map to an established UI pattern that has known accessibility expectations.\n\nSome are obvious:\n\n- A modal dialog should be a [dialog](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role)\n- A toggle should be a [switch](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Switch_role)\n- A dropdown should be a [menu](https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-links.html) _(well, sometimes...)_\n\nBut some are a lot trickier. For example, what about mobile menus, the kind of thing you open with a hamburger button?\n\n_If it opens kinda like a popup, is that a menu like a dropdown?_\n\n_What if it slides in from the side of the screen?_\n\n_What if it just opens in place and pushes the rest of the page further down?_\n\nWe worked through questions like this regularly, and landing on good solutions took a lot of research and experimentation. We're lucky to have [David Luhr](https://twitter.com/david_luhr) on the team who has specialized in accessibility for a long time, and with his help we were able to feel really good about the solutions we landed on.\n\nHere's what we decided we needed in order to support the patterns that already existed in Tailwind UI:\n\n- **Menu Button**. Used for dropdown menus that only contain links or buttons, like a little actions menu at the end of a table row.\n- **Listbox**. For custom `select` implementations where you want to include extra stuff in the `option` elements. For example a country picker where you put a flag next to each country.\n- **Switch**. For custom toggle switches that behave like checkboxes.\n- **Disclosure**. For showing/hiding content in place. Think like collapsable FAQ questions. Also useful for bigger chunks of UI too though, like a mobile menu that opens in place and pushes the rest of the page down.\n- **Dialog**. For, well, modal dialogs! But also for mobile navigation that slides out from the side of the page, and other \"take-over\"-style UIs, even if they don't look like a traditional panel-centered-in-the-screen modal.\n- **Popover**. For panels that pop up on top of the page when you click a button. This is useful for menus where you need lots of custom content that would violate the strictness of regular `role=\"menu\"` menu buttons. We use these for some mobile menus, flyout menus in navigation bars, and other interesting places too. It's kind of like a menu/disclosure hybrid.\n- **Radio Group**. For custom radio selection UIs, like where you want a set of clickable cards instead of a boring little radio circle.\n\nWe ran into tons of challenges building this stuff, especially around complex stuff like focus management, and _especially_ around nested focus management.\n\nImaging you have a modal that opens, and inside that modal there's a dropdown. You open the modal, then open the dropdown, and hit escape. What happens? Well the dropdown should close right, but the modal should stay open.\n\nI guarantee 99% of modals on the internet would close too in this case, even though they aren't supposed to. But not ours — ours works!\n\nWe _(well mostly Robin)_ spent _months_ working on little details like this to make everything as bullet-proof as possible, and while I'm sure there have to be bugs hiding in there still somewhere, where we ended up feels _so_ rock solid compared to almost every UI you encounter day-to-day on the web.\n\nWe still have a lot of new patterns we want to add to Headless UI like tabs, accordions, maybe even _gulp_ a datepicker, and we're looking forward to exploring other frameworks in the future (Alpine.js is next on our list), but we're super proud to call what we're releasing this week Headless UI v1.0 and commit to a stable API going forward.\n\nWe think you're gonna love it. _&lt;/TimCook&gt;_\n\n## Just enough abstraction\n\nWith the Headless UI stuff figured out, the next big problem was figuring out _exactly_ what a React or Vue version of an existing Tailwind UI example should look like.\n\nThe examples in Tailwind UI are pure HTML snippets — you find something you like, copy the HTML into your project, then tweak it as much you like, chop it up into individual components, whatever you want. We don't make any assumptions about how you're going to use it, what elements you're going to keep or delete, or how you want to abstract away any duplication with your preferred tools.\n\nThis is an easy decision when working with pure HTML — what other choice do you really even have? But when offering framework-specific examples, it gets a lot trickier to know exactly what to provide.\n\nThe biggest question was how hard should we try to remove any duplication, and what are the right approaches to doing so?\n\nBoth React and Vue are _component_ frameworks, and the way you reuse code in your projects is by extracting bits of UI into components that you can use over and over again.\n\nThe challenge is that creating components like that is always _very_ project specific. Take this list component for example:\n\n<img src={stackedListExample} alt=\"Stacked list component example from Tailwind UI\" />\n\nFully componentized in a real app, the final code might look something like this:\n\n```jsx\n<TeamList>\n  {projectMembers.map(member => (\n    <TeamList.Item teamMember={member}>\n  ))}\n</TeamList>\n```\n\nIt looks super clean sure, but it's forcing a lot of opinions on you.\n\nFor example, it assumes the items are team members. What if you're building an invoicing app and you want to use this pattern for a list of clients instead? Hell, you might be using this for a sports betting app and these should be baseball teams, not even people!\n\nIt also makes assumptions about the shape of a `member` object. It would have to encode that it's pulling out a `name` and an `email` property, even though your data might be different.\n\nThe other issue is that in frameworks like Vue, you can only have one component per file. This means copying an example that was made up of 4-5 subcomponents would mean you have to copy 4-5 different snippets, create files for each one, and link them all together with the correct names/paths.\n\nTo me, something about doing all of this for people felt like going too far, at least for the problem we're trying to solve today. When everything is super broken up like that with predefined prop APIs and deliberately chosen component names, **it feels like you aren't supposed to change it anymore**. What I love about Tailwind UI is that clicking the \"code\" tab feels like opening up some complex piece of electronics and seeing all of the circuitry right there in front of you. It's a learning opportunity, and you can read the markup and class names and understand how it all works together.\n\nI wrestled with it for a long time, but ultimately decided that right now we were trying to solve two main problems:\n\n1. **Give people code using the syntax they actually need**, like giving React users JSX instead of HTML so they don't have to manually convert things like `for` to `htmlFor`.\n2. **Make the interactive elements work out of the box**, so dropdowns, mobile menus, toggles, and everything else was ready to go, instead of having to write all of that boilerplate JS yourself.\n\nI decided that the right solution was to focus on solving those problems, and be careful not to do anything that would turn Tailwind UI into a different product.\n\nSo this is what's different when you look at a React or Vue example compared to the vanilla HTML version:\n\n1. **Each framework example uses the right syntax** — React examples use JSX, and Vue examples are provided in the single-file component syntax.\n2. **Transitions are real now** — instead of comments telling you what classes to add at each phase of a transition, the transition is just there, using either a Headless UI transition component or Vue's native transition component.\n3. **Interactive elements are handled by Headless UI** — you'll see a few imports in any example that requires JS where we pull in the required Headless UI components and then those are used directly in the markup.\n4. **Any repeated chunks of markup have been converted into basic loops** — any data-driven loop stuff (like lists of people, or navigation items) are extracted into simple variables right there in the example to reduce duplication but still keep everything together in one place. In your own projects, you'd swap this out with data from an API or database or whatever, but we keep the examples simple and don't make any assumptions for you.\n5. **Icons are pulled in from the Heroicons library**. Instead of inlining the SVG directly whenever an icon is used, we pull them in from our React/Vue icon libraries instead to keep the markup simpler.\n\nHere's an example of what it actually looks like:\n\n```jsx\nimport { Menu, Transition } from '@headlessui/react'\nimport { DotsVerticalIcon } from '@heroicons/react/solid'\nimport { Fragment } from 'react'\n\nconst people = [\n  {\n    name: 'Calvin Hawkins',\n    email: 'calvin.hawkins@example.com',\n    image:\n      'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',\n  },\n  {\n    name: 'Kristen Ramos',\n    email: 'kristen.ramos@example.com',\n    image:\n      'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',\n  },\n  {\n    name: 'Ted Fox',\n    email: 'ted.fox@example.com',\n    image:\n      'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',\n  },\n]\n\nexport default function Example() {\n  return (\n    <ul className=\"divide-y divide-gray-200\">\n      {people.map((person) => (\n        <li key={person.email} className=\"py-4 flex\">\n          <img className=\"h-10 w-10 rounded-full\" src={person.image} alt=\"\" />\n          <div className=\"ml-3\">\n            <p className=\"text-sm font-medium text-gray-900\">{person.name}</p>\n            <p className=\"text-sm text-gray-500\">{person.email}</p>\n          </div>\n          <Menu as=\"div\" className=\"ml-3 relative inline-block text-left\">\n            {({ open }) => (\n              <>\n                <div>\n                  <Menu.Button className=\"bg-gray-100 rounded-full flex items-center text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500\">\n                    <span className=\"sr-only\">Open options</span>\n                    <DotsVerticalIcon className=\"h-5 w-5\" aria-hidden=\"true\" />\n                  </Menu.Button>\n                </div>\n\n                <Transition\n                  show={open}\n                  as={Fragment}\n                  enter=\"transition ease-out duration-100\"\n                  enterFrom=\"transform opacity-0 scale-95\"\n                  enterTo=\"transform opacity-100 scale-100\"\n                  leave=\"transition ease-in duration-75\"\n                  leaveFrom=\"transform opacity-100 scale-100\"\n                  leaveTo=\"transform opacity-0 scale-95\"\n                >\n                  <Menu.Items\n                    static\n                    className=\"origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none\"\n                  >\n                    <div className=\"py-1\">\n                      <Menu.Item>\n                        {({ active }) => (\n                          <a\n                            href=\"#\"\n                            className={classNames(\n                              active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n                              'block px-4 py-2 text-sm'\n                            )}\n                          >\n                            View details\n                          </a>\n                        )}\n                      </Menu.Item>\n                      <Menu.Item>\n                        {({ active }) => (\n                          <a\n                            href=\"#\"\n                            className={classNames(\n                              active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n                              'block px-4 py-2 text-sm'\n                            )}\n                          >\n                            Send message\n                          </a>\n                        )}\n                      </Menu.Item>\n                    </div>\n                  </Menu.Items>\n                </Transition>\n              </>\n            )}\n          </Menu>\n        </li>\n      ))}\n    </ul>\n  )\n}\n```\n\nIt's still a single example where you can see everything that's going on at once, and you can cut it up however makes the most sense for your project. You get to define your own prop APIs to meet your own needs, name things however makes the most sense for your domain, and fetch your data in whatever way works best with the other technologies you work with.\n\n## The machine that makes it work\n\nSo that's how it all works from a customer's perspective, but if you're curious how we actually built this stuff internally, it's pretty interesting and worth talking about.\n\nTailwind UI is like 450 examples or something now, and converting all of that stuff to React/Vue by hand would have been absolute torture, and impossible to maintain in the long-term. So we needed some way to automate it.\n\nIf you're anything like me, the entire idea of automatically generating this stuff in different formats might make you cringe. For me at least, my gut reaction is just _\"well there goes the human touch — it's just going to feel like machine-generated garbage now\"_, and of course that is not acceptable to me at all — I want to be proud of the stuff we release, not feel like we had to make really ugly compromises.\n\nSo however we did this, the output had to live up to our standards. This meant we were gonna have to build a system to do this ourselves, from scratch.\n\nFor the first 2 months of the year, [Brad](https://twitter.com/bradlc) spent all of his time building a custom authoring chain specifically for Tailwind UI components that could take our HTML and turn it into React code that looked like it was hand-written by a person.\n\nHere's how it works — instead of authoring our examples in vanilla HTML, we author them in a sort of custom flavor of HTML full of custom elements that we ultimately transform to vanilla HTML using [PostHTML](https://github.com/posthtml/posthtml).\n\nHere's what one of our dropdown examples looks like in our internal authoring format:\n\n```html\n<x-menu as=\"div\" id=\"options-menu\" class=\"relative inline-block text-left\">\n  <div>\n    <x-menu-button\n      class=\"inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500\"\n    >\n      Options\n      <x-heroicon type=\"solid\" name=\"chevron-down\" class=\"-mr-1 ml-2 h-5 w-5\" />\n    </x-menu-button>\n  </div>\n\n  <x-transition\n    as=\"x-fragment\"\n    enter=\"transition ease-out duration-100\"\n    enter-start=\"transform opacity-0 scale-95\"\n    enter-end=\"transform opacity-100 scale-100\"\n    leave=\"transition ease-in duration-75\"\n    leave-start=\"transform opacity-100 scale-100\"\n    leave-end=\"transform opacity-0 scale-95\"\n  >\n    <x-menu-items\n      class=\"origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none\"\n    >\n      <div class=\"py-1\">\n        <x-menu-item>\n          <a\n            href=\"#\"\n            class=\"block px-4 py-2 text-sm\"\n            x-active-class=\"bg-gray-100 text-gray-900\"\n            x-not-active-class=\"text-gray-700\"\n          >\n            Account settings\n          </a>\n        </x-menu-item>\n        <x-menu-item>\n          <a\n            href=\"#\"\n            class=\"block px-4 py-2 text-sm\"\n            x-active-class=\"bg-gray-100 text-gray-900\"\n            x-not-active-class=\"text-gray-700\"\n          >\n            Support\n          </a>\n        </x-menu-item>\n        <x-menu-item>\n          <a\n            href=\"#\"\n            class=\"block px-4 py-2 text-sm\"\n            x-active-class=\"bg-gray-100 text-gray-900\"\n            x-not-active-class=\"text-gray-700\"\n          >\n            License\n          </a>\n        </x-menu-item>\n      </div>\n    </x-menu-items>\n  </x-transition>\n</x-menu>\n```\n\nYou can probably already see why authoring things this way makes it so much easier to convert to something like React or Vue than just writing the HTML by hand.\n\nWe crawl this document as an AST, and actually transform it into _four_ formats:\n\n1. The vanilla HTML you get when you copy the snippet.\n2. The HTML that gets injected into the preview pane, where we use some very quick and dirty Alpine.js to demo the different interactions in the example.\n3. The React snippet for you to copy.\n4. The Vue snippet for you to copy.\n\nThe key to getting sensible output is really just having total control of the input format. It's still hard work, but when you can encode the _intent_ of each example into a custom input format, converting that to another format turns out _so_ much better than trying to write something that can convert arbitrary jQuery to React or something.\n\nThere's still some dark magic in there with regular expressions and all of the other usual suspects, but ultimately by keeping things as declarative as possible and hiding the real complexity inside of Headless UI, we're mostly just transforming markup which is a lot more restricted than regular code.\n\n## When's it coming out?\n\nReact and Vue support for Tailwind UI is going to be available to everyone on Wednesday April 14th — two days from now! It's a completely free update for all customers, you'll just see a new little dropdown appear in the UI for changing the snippet language and you'll be ready to go.\n\nWe'll also be releasing Headless UI v1.0 on the same day _(of course, since how else would this Tailwind UI stuff even work)_ along with a brand new documentation site, so even if you're not a Tailwind UI customer, there's gonna be lots of new free open-source goodies for you to play with.\n\nThanks as always to everyone supporting our work on this stuff — it's seriously a gift to get to work on tools like this for other developers every day and it brings us a ton of fulfillment to see people benefiting from what we build.\n\nHope you enjoy the stuff!\n\n– Adam\n"
  },
  {
    "path": "src/pages/building-the-tailwind-blog/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Building the Tailwind Blog with Next.js',\n  description: `One of the things we believe as a team is that everything we make should be sealed with a blog post. Forcing ourselves to write up a short announcement post for every project we work on acts as a built-in quality check, making sure that we never call a project \"done\" until we feel comfortable telling the world it's out there. The problem was that up until today, we didn't actually have anywhere to publish those posts!`,\n  date: '2020-06-30T18:05:31Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/1987',\n}\n\nOne of the things we believe as a team is that everything we make should be [sealed with a blog post](https://public.3.basecamp.com/p/toAcDMxu8Fvq2yMfd2azTuaV). Forcing ourselves to write up a short announcement post for every project we work on acts as a built-in quality check, making sure that we never call a project \"done\" until we feel comfortable telling the world it's out there.\n\nThe problem was that up until today, we didn't actually have anywhere to publish those posts!\n\n<!--more-->\n\n## Choosing a platform\n\nWe're a team of developers so naturally there was no way we could convince ourselves to use something off-the-shelf, and opted to build something simple and custom with [Next.js](http://nextjs.org).\n\nThere are a lot of things to like about Next.js, but the primary reason we decided to use it is that it has great support for [MDX](https://mdxjs.com/), which is the format we wanted to use to author our posts.\n\n```md\n# My first MDX post\n\nMDX is a really cool authoring format because it lets\nyou embed React components right in your markdown:\n\n<MyComponent myProp={5} />\n\nHow cool is that?\n```\n\nMDX is really interesting because unlike regular Markdown, you can embed live React components directly in your content. This is exciting because it unlocks a lot of opportunities in how you communicate ideas in your writing. Instead of relying only on images, videos, or code blocks, you can build interactive demos and stick them directly between two paragraphs of content, without throwing away the ergonomics of authoring in Markdown.\n\nWe're planning to do a redesign and rebuild of the Tailwind CSS documentation site later this year and being able to embed interactive components makes a huge difference in our ability to teach how the framework works, so using our little blog site as a test project made a lot of sense.\n\n## Organizing our content\n\nWe started by writing posts as simple MDX documents that lived directly in the `pages` directory. Eventually though we realized that just about every post would also have associated assets, for example an Open Graph image at the bare minimum.\n\nHaving to store those in another folder felt a bit sloppy, so we decided instead to give every post its own folder in the `pages` directory, and put the post content in an `index.mdx` file.\n\n```\npublic/\nsrc/\n├── components/\n├── css/\n├── img/\n└── pages/\n    ├── building-the-tailwindcss-blog/\n    │   ├── index.mdx\n    │   └── card.jpeg\n    ├── introducing-linting-for-tailwindcss-intellisense/\n    │   ├── index.mdx\n    │   ├── css.png\n    │   ├── html.png\n    │   └── card.jpeg\n    ├── _app.js\n    ├── _document.js\n    └── index.js\nnext.config.js\npackage.json\npostcss.config.js\nREADME.md\ntailwind.config.js\n```\n\nThis let us co-locate any assets for that post in the same folder, and leverage webpack's [file-loader](https://github.com/tailwindcss/blog/blob/59bd38f2f423f133c0b6157a925ec2875ce880af/next.config.js#L28-L39) to import those assets directly into the post.\n\n## Metadata\n\nWe store metadata about each post in a `meta` object that we export at the top of each MDX file:\n\n```js\nimport { bradlc } from '@/authors'\nimport openGraphImage from './card.jpeg'\n\nexport const meta = {\n  title: 'Introducing linting for Tailwind CSS IntelliSense',\n  description: `Today we’re releasing a new version of the Tailwind CSS IntelliSense extension for Visual Studio Code that adds Tailwind-specific linting to both your CSS and your markup.`,\n  date: '2020-06-23T18:52:03Z',\n  authors: [bradlc],\n  image: openGraphImage,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/1956',\n}\n\n// Post content goes here\n```\n\nThis is where we define the post title (used for the actual `h1` on the post page and the page title), the description (for Open Graph previews), the publish date, the authors, the Open Graph image, and a link to the GitHub Discussions thread for the post.\n\nWe store all of our authors data in a separate file that just contains each team member's name, Twitter handle, and avatar.\n\n```js\nimport adamwathanAvatar from './img/adamwathan.jpg'\nimport bradlcAvatar from './img/bradlc.jpg'\nimport steveschogerAvatar from './img/steveschoger.jpg'\n\nexport const adamwathan = {\n  name: 'Adam Wathan',\n  twitter: '@adamwathan',\n  avatar: adamwathanAvatar,\n}\n\nexport const bradlc = {\n  name: 'Brad Cornes',\n  twitter: '@bradlc',\n  avatar: bradlcAvatar,\n}\n\nexport const steveschoger = {\n  name: 'Steve Schoger',\n  twitter: '@steveschoger',\n  avatar: steveschogerAvatar,\n}\n```\n\nThe nice thing about actually importing the author object into a post instead of connecting it through some sort of identifier is that we can easily add an author inline if we wanted to:\n\n```js\nexport const meta = {\n  title: 'An example of a guest post by someone not on the team',\n  authors: [\n    {\n      name: 'Simon Vrachliotis',\n      twitter: '@simonswiss',\n      avatar: 'https://pbs.twimg.com/profile_images/1160929863/n510426211_274341_6220_400x400.jpg',\n    },\n  ],\n  // ...\n}\n```\n\nThis makes it easy for us to keep author information in sync by giving it a central source of truth, but doesn't give up any flexibility.\n\n## Displaying post previews\n\nWe wanted to display previews for each post on the homepage, and this turned out to be a surprisingly challenging problem.\n\nEssentially what we wanted to be able to do was use the `getStaticProps` feature of Next.js to get a list of all the posts at build-time, extract the information we need, and pass that in to the actual page component to render.\n\nThe challenge is that we wanted to do this without actually importing every single page, because that would mean that our bundle for the homepage would contain every single blog post for the entire site, leading to a much bigger bundle than necessary. Maybe not a big deal right now when we only have a couple of posts, but once you're up to dozens or hundreds of posts that's a lot of wasted bytes.\n\nWe tried a few different approaches but the one we settled on was using webpack's [resourceQuery](https://webpack.js.org/configuration/module/#ruleresourcequery) feature combined with a couple of custom loaders to make it possible to load each blog post in two formats:\n\n1. The entire post, used for post pages.\n2. The post preview, where we load the minimum data needed for the homepage.\n\nThe way we set it up, any time we add a `?preview` query to the end of an import for an individual post, we get back a much smaller version of that post that just includes the metadata and the preview excerpt, rather than the entire post content.\n\nHere's a snippet of what that custom loader looks like:\n\n```js\n{\n  resourceQuery: /preview/,\n  use: [\n    ...mdx,\n    createLoader(function (src) {\n      if (src.includes('<!--​more​-->')) {\n        const [preview] = src.split('<!--​more​-->')\n        return this.callback(null, preview)\n      }\n\n      const [preview] = src.split('<!--​/excerpt​-->')\n      return this.callback(null, preview.replace('<!--​excerpt​-->', ''))\n    }),\n  ],\n},\n```\n\nIt lets us define the excerpt for each post either by sticking `<!--​more-->` after the intro paragraph, or by wrapping the excerpt in a pair of `<!--​excerpt-->` and `<!--​/excerpt-->` tags, allowing us to write an excerpt that's completely independent from the post content.\n\n```\nexport const meta = {\n  // ...\n}\n\nThis is the beginning of the post, and what we'd like to\nshow on the homepage.\n\n<!--​more-->\n\nAnything after that is not included in the bundle unless\nyou are actually viewing that post.\n```\n\nSolving this problem in an elegant way was pretty challenging, but ultimately it was cool to come up with a solution that let us keep everything in one file instead of using a separate file for the preview and the actual post content.\n\n## Generating next/previous post links\n\nThe last challenge we had when building this simple site was being able to include links to the next and previous post whenever you're viewing an individual post.\n\nAt its core, what we needed to do was load up all of the posts (ideally at build-time), find the current post in that list, then grab the post that came before and the post that came after so we could pass those through to the page component as props.\n\nThis ended up being harder than we expected, because it turns out that MDX doesn't currently support `getStaticProps` the way you'd normally use it. You can't actually export it directly from your MDX files, instead you have to store your code in a separate file and re-export it from there.\n\nWe didn't want to load this extra code when just importing our post _previews_ on the homepage, and we also didn't want to have to repeat this code in every single post, so we decided to prepend this export to the beginning of each post using another custom loader:\n\n```js\n{\n  use: [\n    ...mdx,\n    createLoader(function (src) {\n      const content = [\n        'import Post from \"@/components/Post\"',\n        'export { getStaticProps } from \"@/getStaticProps\"',\n        src,\n        'export default (props) => <Post meta={meta} {...props} />',\n      ].join('\\n')\n\n      if (content.includes('<!--​more-->')) {\n        return this.callback(null, content.split('<!--​more-->').join('\\n'))\n      }\n\n      return this.callback(null, content.replace(/<!--​excerpt-->.*<!--\\/excerpt-->/s, ''))\n    }),\n  ],\n}\n```\n\nWe also needed to use this custom loader to actually pass those static props to our `Post` component, so we appended that extra export you see above as well.\n\nThis wasn't the only issue though. It turns out `getStaticProps` doesn't give you any information about the current page being rendered, so we had no way of knowing what post we were looking at when trying to determine the next and previous posts. I suspect this is solvable, but due to time constraints we opted to do more of that work on the client and less at build time, so we could actually see what the current route was when trying to figure out which links we needed.\n\nWe load up all of the posts in `getStaticProps`, and map them to very lightweight objects that just contain the URL for the post, and the post title:\n\n```js\nimport getAllPostPreviews from '@/getAllPostPreviews'\n\nexport async function getStaticProps() {\n  return {\n    props: {\n      posts: getAllPostPreviews().map((post) => ({\n        title: post.module.meta.title,\n        link: post.link.substr(1),\n      })),\n    },\n  }\n}\n```\n\nThen in our actual `Post` layout component, we use the current route to determine the next and previous posts:\n\n```js\nexport default function Post({ meta, children, posts }) {\n  const router = useRouter()\n  const postIndex = posts.findIndex((post) => post.link === router.pathname)\n  const previous = posts[postIndex + 1]\n  const next = posts[postIndex - 1]\n\n  // ...\n}\n```\n\nThis works well enough for now, but again long-term I'd like to figure out a simpler solution that lets us load only the next and previous posts in `getStaticProps` instead of the entire thing.\n\nThere's an interesting library by Hashicorp designed to make it possible to treat MDX files like a data source called [Next MDX Remote](https://github.com/hashicorp/next-mdx-remote) that we will probably explore in the future. It should let us switch to dynamic slug-based routing which would give us access to the current pathname in `getStaticProps` and give us a lot more power.\n\n## Wrapping up\n\nOverall, building this little site with Next.js was a fun learning experience. I'm always surprised at how complicated seemingly simple things end up being with a lot of these tools, but I'm very bullish on the future of Next.js and looking forward to building the next iteration of [tailwindcss.com](https://tailwindcss.com) with it in the months to come.\n\nIf you're interested in checking out the codebase for this blog or even submitting a pull request to simplify any of the things I mentioned above, [check out the repository on GitHub](https://github.com/tailwindcss/blog).\n"
  },
  {
    "path": "src/pages/designing-tailwind-ui-ecommerce/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\n\nexport const meta = {\n  private: true,\n  title: 'Designing Tailwind UI Ecommerce',\n  description: `Hey! We've been working on this new Tailwind UI Ecommerce kit for months now and are finally closing in on the finish line so I thought I'd write up a bit about the process and give you an update on where things are at.`,\n  date: '2021-08-07T12:45:00.0Z',\n  authors: [adamwathan],\n  image: card,\n}\n\nHey! We've been working on this new Tailwind UI Ecommerce kit for months now and are finally closing in on the finish line so I thought I'd write up a bit about the process and give you an update on where things are at.\n\nDesigning and building a big component kit like this is a _huge_ amount of work. If you weren't following along when Steve and I started working on Tailwind UI in the first place, it took about 10 months before we had something we felt good about releasing. We designed dozens of components only to scrap all of them and start from scratch at least three separate times. _Oof._\n\nWe've learned a lot since then and have really refined our process, and it's made putting together these ecommerce ideas go a lot smoother.\n\n---\n\n**The first step was to research and catalog**. We started by studying as many ecommerce sites as we could possibly find and cataloging every UI pattern we could pick out, like:\n\n- Product overviews\n- Product lists\n- Product feature details\n- Category previews\n- Checkout forms\n- Shopping carts\n- Category filters\n- Customer reviews\n- Order history\n- Order details\n- Category mega menus\n- Product quickviews\n- Promo sections\n\nWe scoured dozens of different types of stores as part of this research, including clothing stores like [Everlane](https://www.everlane.com/), shoe stores like [Allbirds](https://www.allbirds.com/), office accessory makers like [Grovemade](https://grovemade.com/), mattress companies like [Casper](https://casper.com/), audio plugin developers like [Toontrack](https://www.toontrack.com/), and tons more. We tried not to focus on any one specific _type_ of store, and I think that really helped in identifying which patterns are truly universal for these types of sites, and how different ideas fit into those patterns.\n\nSome of them were pretty hard to identify, especially because there's quite a bit of overlap between the sort of patterns you see in an ecommerce site and in a more brochure-style marketing website.\n\nFor example, this section isn't really that different from a hero section you might see on a marketing site:\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/adidas-hero.jpg)\n\nSo should the ecommerce kit have \"Hero Sections\" too, like the marketing kit? And if so what makes them different — why have two duplicate categories?\n\nIt took a lot of research and thinking before we eventually noticed that in ecommerce sites, the \"hero\" is almost _never_ a real hero. It's never something with like a summary of the whole brand or anything, like \"Handmade office accessories for the modern knowledge worker\", it's always some timely promotional thing like \"Get 50% off until the end of the summer\" or \"Our fall collection just dropped, explore it now\".\n\nWe also noticed that these \"hero\" sections were almost always designs that could very easily be moved to the middle of the page — they were never really true \"header\" content. This is when we came up with the concept of a \"promo section\", which is like a big banner that promotes a sale or a new product and _could_ be used as a hero, but could also be mixed in with other page content as well.\n\nThere were lots of little eureka moments like this that don't seem like much when you see the finished result but man, it is hard work to figure this stuff out. Organizing and categorizing all of these ideas was almost as much work as designing them.\n\n---\n\n**Next was designing full page examples**. If there's one thing we learned from the initial work on the Application UI and Marketing packages, it's that the components you extract are a lot better when designed in the context of a complete page.\n\nFor the first set of Tailwind UI stuff, we did a lot of designing in isolation, just thinking through a nice pagination design for example. But what we found is that when we started putting all that stuff together into real UIs, lots of little things felt off.\n\nSometimes it was the font size being a little too big compared to other elements in the design, maybe not using enough whitespace, or giving an element too much contrast, making it stand out against the rest of the page when it was supposed to feel like secondary content. When we reworked our approach to always _extract_ smaller examples from bigger examples, everything really started to click a lot better.\n\nSo for the ecommerce package, we designed _everything_ as page examples from the very beginning. In total, we've designed around 50 complete pages so far, including home pages, category listings, product pages, checkout forms, and tons more.\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/50-pages.jpg)\n\nWe'd reuse a few things across pages to move quickly (like footer or navbar ideas), but generally we tried to make every core element of every page unique, to generate as many ideas as possible.\n\n---\n\n**Then we built the pages.** A lot of folks assume we have this like really clean design hand-off process where we create these bullet-proof, perfect Figma designs, then implement them exactly as-is in their perfect form but that's totally not true at all.\n\nWhat happens instead is we take the full page designs that are in _pretty good_ shape, build them as best we can, then we review the finished designs together in the browser to make adjustments. Usually there are a _lot_ of little changes that happen, especially stuff related to little spacing details that are just easier to judge in the browser than they are in a design file. Sometimes though we make really drastic changes, like totally replacing part of the design with something else if we decide it just doesn't look as good as we hoped, or if it would make the code extremely complicated to match the design exactly but for little or no benefit over a slightly different design.\n\nHere's an example of a design that changed a _lot_ during the building process — Figma on the left, final HTML version on the right:\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/figma-vs-html.jpg)\n\nDefinitely think everything turns out a lot better when we are open to iterating on designs like this as we go instead of just throwing them over the wall.\n\nIf you're interested in the actual process we use for writing the templates, we author them in a sort of home-grown templating language driven by PostHTML, which we ultimately use to render the components to multiple targets, like HTML, React, and Vue.\n\nHere's what it looks like:\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/templating-sample.jpg)\n\nYou can read more about it [in this email I sent back in April](https://blog.tailwindcss.com/building-react-and-vue-support-for-tailwind-ui) if you're curious about more of the details!\n\n---\n\n**Next, we extracted the individual components.** Once the page designs are finalized and built, we extract the individual elements into their own templates. This is where \"Product Overviews\" are extracted from product pages, \"Product Lists\" are extracted from category pages, and \"Promo Sections\" are extracted from storefront home pages.\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/exploded-view.jpg)\n\nThe thing that's tough about this process is it's not really as simple as just \"a page is comprised of components\" — as you probably know even _components_ are comprised of components, so deciding exactly how to slice things for Tailwind UI is always tough.\n\nGenerally we err on the side of bigger components, because it's easy for someone to pull out a small piece of a bigger component if they like it and want to use it elsewhere, but it's not always easy to come up with ideas for the bigger components yourself if all you have is the tiny individual pieces.\n\nThe way I like to think about it is we're trying to give you pre-built LEGO creations, not just the bricks.\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/lego.jpeg)\n\nIt's easy to take the wheels off the truck if that's all you need — it's harder to turn the wheels, a seat, and some headlights into a nice truck from scratch though.\n\nFor example, this single \"Product Overview\" section could be cut up into a bunch of smaller components:\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/product-overview.jpg)\n\nThere's an image gallery, a star review widget, a color swatch picker, and a feature accordion there at a bare minimum. But I think providing it holistically like this is the better place to start — you can still grab all that stuff if you need it but as a bigger component it also serves as _inspiration_ and that's a really important ingredient in what makes Tailwind UI a cool product.\n\nWe might extract a lot of these things into smaller components as well down the road, but I definitely think this \"outside-in\" approach is the right way to go overall.\n\n---\n\n**Finally, take inventory, and repeat the whole thing.** A lesson I keep re-learning over and over again about product design is that no amount of planning will lead to the right product on the first try. You have to be keep iterating and improving — it's not a linear process.\n\nFor this ecommerce kit, we couldn't really see where the holes were in the product until we had designed and built a bunch of pages, extracted all of those components, and then assessed where we were at.\n\nWe noticed a bunch of things we were blind to at first, for example, we only had two mega menu ideas — probably because it was really easy to just not design the \"open\" state of any navbar when designing new pages. So we went back to the design step, and came up with a bunch of new concepts to flesh out the category.\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/mega-menu-concepts.jpg)\n\nWe also noticed that we had a disproportionate number of sort of card-based designs on off-white backgrounds, and not enough just flat simple designs which I think is a lot more common in ecommerce design. So we went back to the beginning and designed a bunch of new pages to fill this gap as well:\n\n![](https://tailwindui.nyc3.cdn.digitaloceanspaces.com/20210806-ecommerce-update/white-backgrounds.jpg)\n\nWe're less than a week away from when we hope to release this thing and every day we are _still_ designing brand new things to fill gaps we are identifying as we get closer to the finish line. The product is getting more awesome every day though, and I think these are the best designs we've come up with for Tailwind UI period _(thank god really, getting worse at this would be depressing.)_\n\n---\n\n**So when is it coming out?** I don't want to make any promises but we are trying _really_ hard to get this thing out the door mid-next week. I'll definitely send out another update when we know for sure, but it's looking good right now even though we've still got a lot to wrap up.\n\nAs mentioned in an earlier update, the ecommerce kit is a separate product so it will be a paid add-on for existing customers, but if you already own the Marketing and Application UI packages, it's going to be _really_ affordable, like cheaper than taking your kids out for lunch sort of territory. I **hate** when companies give the best deals to new customers _(hello telecom industry)_ instead of rewarding the people who supported them from the beginning, and we will never be that type of business.\n\nThanks as always for following along — look for another update early next week, and fingers crossed we'll have this ready for release shortly after!\n\n– Adam\n"
  },
  {
    "path": "src/pages/from-900-to-1-how-we-hired-robin-malfait/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport cardImage from './card.jpg'\nimport robinImage from './robin.jpg'\n\nexport const meta = {\n  title: 'From Nine Hundred to One: How We Hired Robin Malfait',\n  description: `Back in May we published our first job posting for a full-stack developer to join our team. This is the story of how we worked through almost 900 applications, and eventually hired Robin Malfait, a talented developer from Belgium who is starting with us today.`,\n  date: '2020-08-10T13:30:00.000Z',\n  authors: [adamwathan],\n  image: cardImage,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2153',\n}\n\nBack in May we published [our first job posting](https://jobs.tailwindui.com/full-stack-developer) to help us find a full-stack developer to join our team.\n\nAfter receiving almost 900 applications and interviewing dozens of talented people, we're excited to finally share that [Robin Malfait](https://twitter.com/malfaitrobin) accepted our offer for the position and is officially part of the Tailwind Labs team as of today!\n\n<!--more-->\n\n<img src={robinImage} alt=\"\" />\n\nRobin is a talented developer from Belgium, and has been an active member of the Tailwind community for a long time. If you're a Tailwind UI customer and have ever asked a question in the `#react` channel on our Discord server, there's a 90% chance he's the helpful person who answered your question. He even built a [bookmarklet](https://gist.github.com/RobinMalfait/a90e8651196c273dfa51eec0f43e1676) to help people convert Tailwind UI components to JSX!\n\nRobin is a seriously experienced React developer, and is joining us to help spearhead the open-source [renderless UI libraries](https://twitter.com/adamwathan/status/1265815748917813248) we are working on that will be the foundation for official React and Vue _(to start anyways)_ support in Tailwind UI.\n\nWe're super excited that he is finally starting with us today, and can't wait to watch his contributions enable people to build awesome UIs even faster and with more confidence. Welcome to the team dude!\n\nWhat follows is the story of how we went about hiring for this role, and how we narrowed down the candidates from almost 900 initial applications to finally making an offer to Robin.\n\n---\n\n## The Job Posting\n\nBefore this role, we had only hired [Brad](https://blog.tailwindcss.com/welcoming-brad-cornes-to-the-tailwind-team) who we already knew and trusted, so we didn't need a job posting or any sort of rigorous application process.\n\nI knew that if we wanted to get really great candidates, we had to write a compelling job posting. After about 3-4 days of working on it, this is where we ended up:\n\n[**Read the job posting &rarr;**](https://jobs.tailwindui.com/full-stack-developer)\n\nHere are the important things I focused on when writing it:\n\n- Be specific about the projects the applicant would be working on after they started\n- Be clear that we are a small team, so everyone has to do a bit of everything, including customer support\n- Give concrete examples of projects we just finished that the applicant would have worked on if they were already at the company\n- Go into detail about specific hard problems we expect to run into on the next major upcoming project, to help the applicant understand the sort of expertise that would be valuable to us\n- Share concrete salary and benefit information. I would never apply for a job without a clear understanding of the salary, so why should I expect talented people to apply for our posting without it?\n\nWe got _tons_ of positive feedback about this posting, and I'm really proud of how it turned out. I think it was very applicant-centric, and I think it made a big difference in the quality of submissions we got.\n\n## The Application Process\n\nOne thing we did a bit differently from other companies is that we didn't ask for a resume or give applicants a big list of questions to answer. All we asked for was an \"application\", in whatever form the person decided. It could be a cover letter, a small website, a video, a slide deck, whatever.\n\nI decided to ask for applications this way for a few reasons:\n\n- I just don't think resumes are that important\n- I wanted to filter for people with some inherent marketing sensibilities, we're a tiny company so we need [T-shaped](https://en.wikipedia.org/wiki/T-shaped_skills) people more than we need specialists\n- I wanted to filter for people who can ship things, and making the application completely free-form tells you a lot about someone's ability to take something from nothing to polished product on their own\n- I wanted to find someone who talked about the stuff we were looking for without being prompted for it — finding someone who was naturally well-aligned with what we are trying to do would be a big advantage for us\n- I expected a lot of applications, and I thought asking for applications this way would make it easy to filter people out who were using a shotgun approach to job-searching and not specifically interested in working with us\n\nEven with what I think was a fairly intimidating application process, we got well over 100 applications where there was clearly a lot of time spent crafting something very specific for our posting, including Robin's of course:\n\n[**Read Robin's application &rarr;**](https://robin-malfait-tailwind-job-application.now.sh/)\n\nSome people did some _really_ out there and creative things in their applications _(one person even made an interactive game!)_ but Robin's stood out to us for a few reasons:\n\n- The visual design was great. We're a very design-focused company, so having good taste in design is really important to us.\n- His story about learning to program and getting into the Laravel community told me we had a rich shared history, even if we had never met.\n- He took a chance and shared some strong opinions he had about component design that were _extremely_ relevant to some work we'll be doing very soon, and I agreed with what he was saying and even learned a few things.\n- He shared a super interesting [open-source library](https://github.com/RobinMalfait/lazy-collections) he authored, which despite being very unknown, still had very well thought-out and complete documentation that was presented in a very well-structured way. It was clear he thinks about visual design even when authoring a markdown file.\n- He shared lots of concrete ideas for projects he'd like to work on with us, and a lot of them were things I was already excited about doing.\n- He capitalized the \"H\" in \"GitHub\" _(holy shit I hate when people don't do that)_.\n\nRobin's was one of maybe 40-50 that _really_ stood out from a content perspective.\n\n## Filtering the Applications\n\nDealing with almost 900 job applications is a lot of work. Over half of them we were able to discard immediately because they just provided a link to their LinkedIn profile or a generic resume, but filtering through the rest was really tough.\n\nI've never hired anyone this way before, and at first I really felt like we needed to meet with and interview _everyone_ who submitted a quality application. As the applications poured in though, I realized this was just not practical, and that we had to put some sort of cap on it.\n\nI decided to sort the good applications as best I could, then just slice off the top 20 and start there. It meant there were lots of great people we wouldn't talk to and that maybe we even missed out on the absolute best applicant, but the reality is that we only have so much time we can dedicate to this, and I had to believe that out of the ~20 best applications, there would certainly be _multiple_ people we wouldn't regret hiring at all, even if there was a chance that the absolute best person was somewhere in the other 30.\n\n## The Interview Process\n\nWe started by scheduling video interviews with the top ~20 applicants, which took about 3 weeks to get through.\n\nThese were 30-45 minute calls where we had a pretty casual conversation about a few topics:\n\n- What the person had been working on recently, and where they think their strengths are\n- Why they applied for the job, and what about the role was interesting to them\n- What we as a company are going to be doing over the next year or so, and digging into a few projects in detail\n- Answering any questions the person had about the job or our company\n\nThis was a great way just to get to know the people who applied and get a gut sense for who stood out the most. We really enjoyed meeting with every single person we talked to, but made the hard decision to filter down again to about 10 people for the next phase.\n\n## Take-Home Project\n\nThe next step in the application process was a take-home project, where the applicant had to build out a design Steve had created using either Vue or React. We estimated it to be about a 4-8 hour project.\n\nWe provided a zip file containing all of the instructions, the design as a Figma file, and a walk-through video of a working implementation outlining any behavior that was hard to capture in Figma.\n\n[**See the take-home project on GitHub &rarr;**](https://github.com/adamwathan/tailwind-take-home-project)\n\nWe tried to give very clear instructions, and made sure to point out where we wanted people to focus their time, and what areas we didn't want them to overthink or spend too much time on.\n\nWe gave each candidate about two weeks to complete the project, just to make sure they had the opportunity to fit it into their schedule without it being disruptive.\n\nAll of the submissions we got back were great, but again we forced ourselves to limit the candidates for the next phase, this time down to 6 people.\n\nOne thing we really loved about Robin's submission was that he spent a lot of time guiding us through his solution with comments in the code. For regular production code I would say it was definitely overkill, but as part of a job application I thought it was extremely helpful to get a behind-the-scenes look into how he actually _thinks_ about the code he is writing. He also spent a lot of time describing alternate solutions to certain problems and why he didn't go with those approaches, which was very beneficial as well.\n\n## Pairing Session\n\nThe final step in the application process was a two-hour pair programming session with me.\n\nWhen pairing as part of an interview process like this, there's a really high risk of the inherent power dynamic coloring how the whole thing goes. I _really_ wanted to avoid that as much as possible, so I did two things:\n\n- I made sure whatever we were pairing on was something completely new, that I had no prior experience with\n- I let the candidate suggest a few things for us to pair on, and picked something from their list\n\nI absolutely didn't want to pair on something where I knew all the answers and I was just watching to see if the candidate could figure out something I already knew. That is absolutely not representative of real work and I don't think it would've been useful at all.\n\nInstead, by choosing a problem that neither of us had significant experience with, we got to put the power dynamic aside (as much as possible at least), and just focus on learning something new together, and seeing how we helped each other get unstuck.\n\nSome of the things I paired on included:\n\n- Building a date picker from scratch\n- Learning XState\n- Building a modal dialog with the Vue 3 composition API\n\nI really enjoyed this process and am very proud of how we put it together. It was definitely the most informative part of the interview process and really gave me a ton of confidence that we were offering the job to the right person.\n\nFor Robin's session, we decided to build an SVG charting library from scratch _(something neither of us had ever done before)_, in Svelte _(a framework neither of us had ever used before)_. This was Robin's idea, and that he had the courage to tackle two completely new problems at the same time _in an interview context_ really impressed me. We had a great time pairing together on this, and not once in the session did it ever feel like either of us was ahead of the other person or trying to catch them up on something. We had really great chemistry and it felt very energizing and productive, and reminded me of some of the best pairing sessions I've had in my career, which is pretty incredible given we'd never worked together before, and that he was being evaluated for a job.\n\n## Making the offer\n\nThis whole process took about 1.5 months, and at the end we had a very hard time choosing between the top few candidates. Realistically we could've hired any of them and not regretted it, but my experience interviewing and pairing with Robin stood out just a bit more and I was really excited to be able to offer him the role. We know he's going to be an amazing fit for the team, and I can't wait to dig in to some hard problems with him in the coming months.\n"
  },
  {
    "path": "src/pages/headless-ui-unstyled-accessible-ui-components/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\nimport headlessUiCard from './headless-ui-card.svg'\n\nexport const meta = {\n  title: `Headless UI: Unstyled, Accessible UI Components`,\n  description: `Headless UI is a set of completely unstyled, fully accessible UI components for React, Vue, and Alpine.js that make it easy to build fully accessible custom UI components, without sacrificing the ability to style them from scratch with simple utility classes.`,\n  date: '2020-10-06T18:30:00.000Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2508',\n}\n\nOne of the biggest pain points when building modern web applications is building custom components like select menus, dropdowns, toggles, modals, tabs, radio groups — components that are pretty similar from project to project, but never quite the _same_.\n\nYou could use an off-the-shelf package, but they usually come tightly coupled with their own provided styles. It ends up being very hard to get them to match the look and feel of your own project, and almost always involves writing a bunch of CSS overrides, which feels like a big step backwards when working Tailwind CSS.\n\nThe other option is building your own components from scratch. At first it seems easy, but then you remember you need to add support for keyboard navigation, managing ARIA attributes, focus trapping, and all of a sudden you're spending 3-4 weeks trying to build a truly bullet-proof dropdown menu.\n\nWe think there's a better option, so we're building it.\n\n<!--more-->\n\n**[Headless UI](https://headlessui.dev) is a set of completely unstyled, fully accessible UI components for React and Vue** _(and soon Alpine.js)_ that make it easy to build these sorts of custom components without worrying about any of the complex implementation details yourself, and without sacrificing the ability to style them from scratch with simple utility classes.\n\n<img src={headlessUiCard} alt=\"Headless UI Logo\" />\n\nHere's what it looks like to build a custom dropdown _(one of many components the library includes)_ using `@headlessui/react`, with complete keyboard navigation support and ARIA attribute management, styled with simple Tailwind CSS utilities:\n\n```jsx\nimport { Menu } from '@headlessui/react'\n\nfunction MyDropdown() {\n  return (\n    <Menu as=\"div\" className=\"relative\">\n      <Menu.Button className=\"px-4 py-2 rounded bg-blue-600 text-white ...\">Options</Menu.Button>\n      <Menu.Items className=\"absolute mt-1 right-0\">\n        <Menu.Item>\n          {({ active }) => (\n            <a className={`${active && 'bg-blue-500 text-white'} ...`} href=\"/account-settings\">\n              Account settings\n            </a>\n          )}\n        </Menu.Item>\n        <Menu.Item>\n          {({ active }) => (\n            <a className={`${active && 'bg-blue-500 text-white'} ...`} href=\"/documentation\">\n              Documentation\n            </a>\n          )}\n        </Menu.Item>\n        <Menu.Item disabled>\n          <span className=\"opacity-75 ...\">Invite a friend (coming soon!)</span>\n        </Menu.Item>\n      </Menu.Items>\n    </Menu>\n  )\n}\n```\n\nHere's what you're getting for free in that example, without having to write a single line of code related to it yourself:\n\n- The dropdown panel opens on click, spacebar, enter, or when using the arrow keys\n- The dropdown closes when you press escape, or click outside of it\n- You can navigate the items using the up and down arrow keys\n- You can jump the first item using the `Home` key, and the last item using the `End` key\n- Disabled items are automatically skipped when navigating with the keyboard\n- Hovering over an item with your mouse after navigating with the keyboard will switch to mouse position based focusing\n- Items are announced properly to screen readers while navigating them with the keyboard\n- The dropdown button is properly announced to screenreaders as controlling a menu\n- ...and probably tons more that I'm forgetting.\n\nAll without writing the letters `aria` anywhere in your own code, and without writing a single event listener. And you still have complete control over the design!\n\nThere are [over 3000 lines of tests for this component](https://github.com/tailwindlabs/headlessui/blob/c7b91dc7315b1f49c1a469f70eb1f6eba6a2e31c/packages/%40headlessui-react/src/components/menu/menu.test.tsx). Pretty nice that you didn't have to do that yourself, right?\n\nHere's a fully-styled live demo _(taken from [Tailwind UI](https://tailwindui.com))_ so you can see it in action:\n\n<iframe\n  src=\"https://codesandbox.io/embed/headlessuireact-menu-example-b6xje?fontsize=14&hidenavigation=1&module=%2Fsrc%2FApp.js&theme=dark\"\n  className=\"w-full border-0 rounded overflow-hidden\"\n  style={{ height: '480px' }}\n  title=\"@headlessui/react Menu Example\"\n  allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n  sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n></iframe>\n\nMake sure to try it with the keyboard or a screen reader to really appreciate it!\n\nWe just tagged v0.2.0, which currently includes the following components:\n\n- [Menu Button](https://codesandbox.io/s/headlessuivue-menu-example-70br3?file=/src/App.vue) (or dropdown)\n- [Listbox](https://codesandbox.io/s/headlessuivue-listbox-example-mi67g?file=/src/App.vue) (or custom select)\n- [Switch](https://codesandbox.io/s/headlessuivue-switch-example-8ycp6?file=/src/App.vue) (or toggle)\n- ...with many more on the way.\n\nTo learn more and dive in, [**head over to the Headless UI website**](https://headlessui.dev) and read the documentation.\n\n---\n\nIf you've followed my work online for the last few years, you might remember my fascination with [renderless UI components](https://adamwathan.me/renderless-components-in-vuejs/) — something I was really started getting into towards the [end of 2017](https://fullstackradio.com/79). I've wanted a library like this to exist for years, but until we started growing the team we just didn't have the resources to make it happen.\n\nEarlier this year [we hired Robin Malfait](https://blog.tailwindcss.com/from-900-to-1-how-we-hired-robin-malfait), and he's been working on Headless UI full-time ever since.\n\nThe biggest motivation for this project is that we'd really like to add production-ready JS examples to [Tailwind UI](https://tailwindui.com), which is currently an HTML-only, bring-your-own-JavaScript sort of project. This is great for lots of our customers who want full control over how everything works, but for many others it's a point of friction.\n\nWe didn't want to add 200 lines of gnarly JS to every component example, so we started working on Headless UI as a way to _extract_ all of that noise, without giving up any flexibility in the actual UI design.\n\n## Why reinvent the wheel?\n\nWe're not the first people to try and tackle this problem. [Downshift](https://github.com/downshift-js/downshift) was the first library I saw that got me excited about this idea back in 2017, [Reach UI](https://reach.tech/) and [Reakit](https://reakit.io/) started development in 2018, and [React Aria](https://react-spectrum.adobe.com/react-aria/getting-started.html) was released most recently, just earlier this year.\n\nWe decided to try our own take on the problem for a few reasons:\n\n- Existing solutions are focused almost entirely on React, and we'd like to bring these ideas to other ecosystems like Vue, Alpine, and hopefully more in the future.\n- These libraries are going to be foundational for adding JS support to Tailwind UI, and since that's what keeps the business running it felt important to have complete decision-making power over how the libraries worked and what they supported.\n- We have our own ideas on what the APIs should look like for these components, and want to be able to explore those ideas freely.\n- We want to make sure it is always super easy to style these components with Tailwind, rather than having to write custom CSS.\n\nWe think what we've come up with so far hits a great balance between flexibility and developer experience, and we're grateful there are other people working on similar problems that we can learn from and share our ideas with.\n\n## What's next\n\nWe've got quite a few more components to develop for Headless UI, including:\n\n- Modal\n- Radio group\n- Tabs\n- Accordion\n- Combobox\n- Datepicker\n\n...and likely many more. We're also about to start on Alpine.js support, and we're hoping to be able to tag a v1.0 for React, Vue, and Alpine near the end of the year.\n\nAfter that we'll start exploring other frameworks, with the hope that we can eventually offer the same tools for ecosystems like Svelte, Angular, and Ember, either first-class or with community partners.\n\nIf you'd like to keep up with what we're doing, be sure to [follow the project on GitHub](https://github.com/tailwindlabs/headlessui).\n"
  },
  {
    "path": "src/pages/headless-ui-v1/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/headless-ui-v1/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\n\nexport const meta = {\n  title: 'Headless UI v1.0',\n  description: `Last fall we announced Headless UI, a library of completely unstyled, fully accessible UI components, designed to pair perfectly with Tailwind CSS. Today we’re super excited to release Headless UI v1.0, which more than doubles the amount of included components for both React and Vue.`,\n  date: '2021-04-14T16:00:00.000Z',\n  authors: [adamwathan],\n  image: card,\n  footer: `\n    <p>\n      Want to try it out?\n      <a href=\"https://headlessui.dev\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Visit the Headless UI website →\n      </a>\n    </p>\n  `,\n}\n\nLast fall we announced [Headless UI](https://blog.tailwindcss.com/headless-ui-unstyled-accessible-ui-components), a library of completely unstyled, fully accessible UI components, designed to pair perfectly with Tailwind CSS.\n\nToday we’re super excited to release [Headless UI v1.0](https://headlessui.dev), which more than doubles the amount of included components for both React and Vue.\n\n<!--more-->\n\n<a href=\"https://headlessui.dev\">\n  <img src={card} alt=\"Headless UI\" />\n</a>\n\n## What’s new\n\nWe’ve added four new components to the React library, and five new components for Vue.\n\n### Dialog (modal)\n\nHeadless UI now includes a robust dialog implementation you can use to build traditional modal dialogs, mobile slide-out menus, or any other take-over-style UI that needs to capture the focus of the entire page.\n\n```jsx\nimport { useState } from 'react'\nimport { Dialog } from '@headlessui/react'\n\nfunction MyDialog() {\n  let [isOpen, setIsOpen] = useState(true)\n\n  return (\n    <Dialog open={isOpen} onClose={setIsOpen}>\n      <Dialog.Overlay />\n\n      <Dialog.Title>Deactivate account</Dialog.Title>\n      <Dialog.Description>\n        This will permanently deactivate your account\n      </Dialog.Description>\n\n      <p>\n        Are you sure you want to deactivate your account? All of your data will\n        be permanently removed. This action cannot be undone.\n      </p>\n\n      <button onClick={() => setIsOpen(false)}>Deactivate</button>\n      <button onClick={() => setIsOpen(false)}>Cancel</button>\n    </Dialog>\n  )\n}\n```\n\n### Disclosure\n\nWe’ve added a new `Disclosure` component that makes it easy to show/hide inline content accessibly. This is useful for things like collapsible FAQ questions, \"show more\" interfaces, or even hamburger menus that open up and push the rest of the page content away.\n\n```html\n<template>\n  <Disclosure>\n    <DisclosureButton> Is team pricing available? </DisclosureButton>\n    <DisclosurePanel>\n      Yes! You can purchase a license that you can share with your entire team.\n    </DisclosurePanel>\n  </Disclosure>\n</template>\n\n<script>\n  import {\n    Disclosure,\n    DisclosureButton,\n    DisclosurePanel,\n  } from '@headlessui/vue'\n\n  export default {\n    components: { Disclosure, DisclosureButton, DisclosurePanel },\n  }\n</script>\n```\n\n### Radio Group\n\nThere’s now a `RadioGroup` component that you can use to build totally custom radio button UIs, like when you want to use fancy cards or something instead of a simple little radio circle.\n\n```jsx\nimport { useState } from 'react'\nimport { RadioGroup } from '@headlessui/react'\n\nfunction MyRadioGroup() {\n  let [plan, setPlan] = useState('startup')\n\n  return (\n    <RadioGroup value={plan} onChange={setPlan}>\n      <RadioGroup.Label>Plan</RadioGroup.Label>\n      <RadioGroup.Option value=\"startup\">\n        {({ checked }) => (\n          <span className={checked ? 'bg-blue-200' : ''}>Startup</span>\n        )}\n      </RadioGroup.Option>\n      <RadioGroup.Option value=\"business\">\n        {({ checked }) => (\n          <span className={checked ? 'bg-blue-200' : ''}>Business</span>\n        )}\n      </RadioGroup.Option>\n      <RadioGroup.Option value=\"enterprise\">\n        {({ checked }) => (\n          <span className={checked ? 'bg-blue-200' : ''}>Enterprise</span>\n        )}\n      </RadioGroup.Option>\n    </RadioGroup>\n  )\n}\n```\n\n### Popover\n\nThe new `Popover` component lets you build custom dropdown UIs that don’t have any content restrictions like a regular `Menu` component would. Great for fly-out menus on marketing sites, dropdowns that have form fields in them, and tons more.\n\n```html\n<template>\n  <Popover class=\"relative\">\n    <PopoverButton>Solutions</PopoverButton>\n\n    <PopoverPanel class=\"absolute z-10\">\n      <div>\n        <a href=\"/analytics\">Analytics</a>\n        <a href=\"/engagement\">Engagement</a>\n        <a href=\"/security\">Security</a>\n        <a href=\"/integrations\">Integrations</a>\n      </div>\n\n      <img src=\"/solutions.jpg\" alt=\"\" />\n    </PopoverPanel>\n  </Popover>\n</template>\n\n<script>\n  import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\n\n  export default {\n    components: { Popover, PopoverButton, PopoverPanel },\n  }\n</script>\n```\n\n### TransitionRoot and TransitionChild (for Vue)\n\nHeadless UI already had a `Transition` component for React, but we’ve always recommended the native `<transition>` that already ships with Vue for Vue users. There are some limitations to the native transition though, and things can get complicated when trying to co-ordinate nested transitions that are supposed to run in parallel.\n\nHeadless UI v1.0 brings our React `Transition` component to Vue as well, which makes it a lot easier to transition things like modal dialogs.\n\n```html\n<template>\n  <!— This `show` prop controls all nested `Transition.Child` components. —>\n  <TransitionRoot :show=\"isOpen\">\n    <!— Background overlay —>\n    <TransitionChild\n      enter=\"transition-opacity\"\n      ease-linear\n      duration-300\"\n      enter-from=\"opacity-0\"\n      enter-to=\"opacity-100\"\n      leave=\"transition-opacity\"\n      ease-linear\n      duration-300\"\n      leave-from=\"opacity-100\"\n      leave-to=\"opacity-0\"\n    >\n      <!— … —>\n    </TransitionChild>\n\n    <!— Sliding sidebar —>\n    <TransitionChild\n      enter=\"transition\"\n      ease-in-out\n      duration-300\n      transform\"\n      enter-from=\"-translate-x-full\"\n      enter-to=\"translate-x-0\"\n      leave=\"transition\"\n      ease-in-out\n      duration-300\n      transform\"\n      leave-from=\"translate-x-0\"\n      leave-to=\"-translate-x-full\"\n    >\n      <!— … —>\n    </TransitionChild>\n  </TransitionRoot>\n</template>\n\n<script>\n  import { ref } from \"vue\";\n  import { Transition, TransitionChild } from \"@headlessui/vue\";\n\n  export default {\n    components: { TransitionRoot: Transition, TransitionChild },\n\n    setup() {\n      const isShowing = ref(true);\n\n      return {\n        isShowing,\n      };\n    },\n  };\n</script>\n```\n\n## Try it out\n\nHead over to our [brand new documentation website](https://headlessui.dev) to pull Headless UI into your projects and play with it! It’s MIT licensed and open-source, so if you’d like to poke around the code or you need to report an issue, [visit the GitHub repository](https://github.com/tailwindlabs/headlessui).\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/index.mdx",
    "content": "import { adamwathan, robinmalfait } from '@/authors'\nimport card from './card.jpg'\nimport banner from './banner.jpg'\nimport ecommerce from './ecommerce-screenie.jpg'\nimport { Tab } from '@headlessui/react'\nimport ReactExample1 from './snippets/react-1.mdx'\nimport ReactExample2 from './snippets/react-2.mdx'\nimport ReactExample3 from './snippets/react-3.mdx'\nimport ReactExample4 from './snippets/react-4.mdx'\nimport VueExample1 from './snippets/vue-1.mdx'\nimport VueExample2 from './snippets/vue-2.mdx'\nimport VueExample3 from './snippets/vue-3.mdx'\nimport VueExample4 from './snippets/vue-4.mdx'\n\nexport const meta = {\n  title: 'Headless UI v1.4: The One With Tabs',\n  description: `We just released Headless UI v1.4, which includes a brand new \\`Tab\\` component, and new APIs for manually closing \\`Popover\\` and \\`Disclosure\\` components more easily.`,\n  date: '2021-07-29T12:00:00.000Z',\n  authors: [adamwathan, robinmalfait],\n  image: card,\n  footer: `\n    <p>\n      Ready to try it out?\n      <a href=\"https://headlessui.dev\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Visit the Headless UI website →\n      </a>\n    </p>\n  `,\n}\n\nWe just released Headless UI v1.4, which includes a brand new `Tab` component, and new APIs for manually closing `Popover` and `Disclosure` components more easily.\n\n<!--more-->\n\n<a href=\"https://headlessui.dev\">\n  <img src={banner} alt=\"Headless UI v1.4\" />\n</a>\n\n## Tabs\n\nEarlier this year we started working on [Tailwind UI Ecommerce](https://tailwindui.com/ecommerce), and we realized pretty quickly we were going to need to support tabs in Headless UI to be able to build the new interfaces we were designing.\n\n<a href=\"https://tailwindui.com/ecommerce\">\n  <img\n    src={ecommerce}\n    alt=\"Product details interface design from Tailwind UI Ecommerce.\"\n  />\n</a>\n\nHere's what we ended up with:\n\n<Tab.Group as=\"div\" className=\"my-6 bg-gray-800 rounded-md\">\n  <Tab.List className=\"relative space-x-2 px-4 pt-3\">\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      React\n    </Tab>\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      Vue\n    </Tab>\n  </Tab.List>\n  <Tab.Panels>\n    <Tab.Panel className=\"focus:outline-none\">\n      <ReactExample1 />\n    </Tab.Panel>\n    <Tab.Panel className=\"focus:outline-none\">\n      <VueExample1 />\n    </Tab.Panel>\n  </Tab.Panels>\n</Tab.Group>\n\nAnd yep, those are tabs!\n\nLike all Headless UI components, this totally abstracts away stuff like keyboard navigation for you so you can create custom tabs in a completely declarative way, without having to think about any of the tricky accessibility details.\n\n[Check out the documentation](https://headlessui.dev/react/tabs) to learn more.\n\n### Closing Disclosures and Popovers\n\nUp until now, there was no way to close a `Disclosure` without clicking the actual button used to open it. For typical disclosure use cases this isn't a big deal, but it often makes sense to use disclosures for things like mobile navigation, where you want to close it when someone clicks a link _inside_ of it.\n\nNow you can use `Disclosure.Button` or (`DisclosureButton` in Vue) within your disclosure panel to close the panel, making it easy to wrap up things like links or other buttons so the panel doesn't stay open:\n\n<Tab.Group as=\"div\" className=\"my-6 bg-gray-800 rounded-md\">\n  <Tab.List className=\"relative space-x-2 px-4 pt-3\">\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      React\n    </Tab>\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      Vue\n    </Tab>\n  </Tab.List>\n  <Tab.Panels>\n    <Tab.Panel className=\"focus:outline-none\">\n      <ReactExample2 />\n    </Tab.Panel>\n    <Tab.Panel className=\"focus:outline-none\">\n      <VueExample2 />\n    </Tab.Panel>\n  </Tab.Panels>\n</Tab.Group>\n\nThe same thing works with `Popover` components, too:\n\n<Tab.Group as=\"div\" className=\"my-6 bg-gray-800 rounded-md\">\n  <Tab.List className=\"relative space-x-2 px-4 pt-3\">\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      React\n    </Tab>\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      Vue\n    </Tab>\n  </Tab.List>\n  <Tab.Panels>\n    <Tab.Panel className=\"focus:outline-none\">\n      <ReactExample3 />\n    </Tab.Panel>\n    <Tab.Panel className=\"focus:outline-none\">\n      <VueExample3 />\n    </Tab.Panel>\n  </Tab.Panels>\n</Tab.Group>\n\nIf you need finer control, we also pass a `close` function via the render prop/scoped slot, so you can imperatively close the panel when you need to:\n\n<Tab.Group as=\"div\" className=\"my-6 bg-gray-800 rounded-md\">\n  <Tab.List className=\"relative space-x-2 px-4 pt-3\">\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      React\n    </Tab>\n    <Tab\n      className={({ selected }) =>\n        `py-2 px-4 inline-block rounded-md ${\n          selected ? 'bg-gray-700' : 'bg-transparent hover:bg-gray-700'\n        } text-sm text-white font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-600`\n      }\n    >\n      Vue\n    </Tab>\n  </Tab.List>\n  <Tab.Panels>\n    <Tab.Panel className=\"focus:outline-none\">\n      <ReactExample4 />\n    </Tab.Panel>\n    <Tab.Panel className=\"focus:outline-none\">\n      <VueExample4 />\n    </Tab.Panel>\n  </Tab.Panels>\n</Tab.Group>\n\nFor more details, check out the updated [Popover](https://headlessui.dev/react/popover#closing-popovers-manually) and [Disclosure](https://headlessui.dev/react/disclosure#closing-disclosures-manually) documentation.\n\n## Try it out\n\nHeadless UI v1.4 is a minor update so there are no breaking changes. To upgrade, just install the latest version via npm:\n\n```shell\n# For React\nnpm install @headlessui/react\n\n# For Vue\nnpm install @headlessui/vue\n```\n\nCheck out [the official website](https://headlessui.dev) for the latest documentation, and check out [Tailwind UI](https://tailwindui.com) if you want to play tons of styled examples.\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/react-1.mdx",
    "content": "```jsx\nimport { Tab } from '@headlessui/react'\n\nfunction MyTabs() {\n  return (\n    <Tab.Group>\n      <Tab.List>\n        <Tab>Tab 1</Tab>\n        <Tab>Tab 2</Tab>\n        <Tab>Tab 3</Tab>\n      </Tab.List>\n      <Tab.Panels>\n        <Tab.Panel>Content 1</Tab.Panel>\n        <Tab.Panel>Content 2</Tab.Panel>\n        <Tab.Panel>Content 3</Tab.Panel>\n      </Tab.Panels>\n    </Tab.Group>\n  )\n}\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/react-2.mdx",
    "content": "```jsx\nimport { Disclosure } from '@headlessui/react'\nimport MyLink from './MyLink'\n\nfunction MyDisclosure() {\n  return (\n    <Disclosure>\n      <Disclosure.Button>Open mobile menu</Disclosure.Button>\n      <Disclosure.Panel>\n        <Disclosure.Button as={MyLink} href=\"/home\">\n          Home\n        </Disclosure.Button>\n        {/* ... */}\n      </Disclosure.Panel>\n    </Disclosure>\n  )\n}\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/react-3.mdx",
    "content": "```jsx\nimport { Popover } from '@headlessui/react'\nimport MyLink from './MyLink'\n\nfunction MyPopover() {\n  return (\n    <Popover>\n      <Popover.Button>Solutions</Popover.Button>\n      <Popover.Panel>\n        <Popover.Button as={MyLink} href=\"/insights\">\n          Insights\n        </Popover.Button>\n        {/* ... */}\n      </Popover.Panel>\n    </Popover>\n  )\n}\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/react-4.mdx",
    "content": "```jsx\nimport { Popover } from '@headlessui/react'\n\nfunction MyPopover() {\n  return (\n    <Popover>\n      <Popover.Button>Terms</Popover.Button>\n      <Popover.Panel>\n        {({ close }) => (\n          <button\n            onClick={async () => {\n              await fetch('/accept-terms', { method: 'POST' })\n              close()\n            }}\n          >\n            Read and accept\n          </button>\n        )}\n      </Popover.Panel>\n    </Popover>\n  )\n}\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/vue-1.mdx",
    "content": "```html\n<template>\n  <TabGroup>\n    <TabList>\n      <Tab>Tab 1</Tab>\n      <Tab>Tab 2</Tab>\n      <Tab>Tab 3</Tab>\n    </TabList>\n    <TabPanels>\n      <TabPanel>Content 1</TabPanel>\n      <TabPanel>Content 2</TabPanel>\n      <TabPanel>Content 3</TabPanel>\n    </TabPanels>\n  </TabGroup>\n</template>\n\n<script>\n  import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'\n\n  export default {\n    components: {\n      TabGroup,\n      TabList,\n      Tab,\n      TabPanels,\n      TabPanel,\n    },\n  }\n</script>\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/vue-2.mdx",
    "content": "```html\n<template>\n  <Disclosure>\n    <DisclosureButton>Open mobile menu</DisclosureButton>\n    <DisclosurePanel>\n      <DisclosureButton :as=\"MyLink\" href=\"/home\">Home</DisclosureButton>\n      <!-- ... -->\n    </DisclosurePanel>\n  </Disclosure>\n</template>\n\n<script>\n  import {\n    Disclosure,\n    DisclosureButton,\n    DisclosurePanel,\n  } from '@headlessui/vue'\n  import MyLink from './MyLink'\n\n  export default {\n    components: { Disclosure, DisclosureButton, DisclosurePanel, MyLink },\n  }\n</script>\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/vue-3.mdx",
    "content": "```html\n<template>\n  <Popover>\n    <PopoverButton>Solutions</PopoverButton>\n\n    <PopoverPanel>\n      <PopoverButton :as=\"MyLink\" href=\"/insights\">Insights</PopoverButton>\n      <!-- ... -->\n    </PopoverPanel>\n  </Popover>\n</template>\n\n<script>\n  import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\n  import MyLink from './MyLink'\n\n  export default {\n    components: { Popover, PopoverButton, PopoverPanel, MyLink },\n  }\n</script>\n```\n"
  },
  {
    "path": "src/pages/headless-ui-v1-4/snippets/vue-4.mdx",
    "content": "```html\n<template>\n  <Popover>\n    <PopoverButton>Solutions</PopoverButton>\n\n    <PopoverPanel v-slot=\"{ close }\">\n      <button @click=\"accept(close)\">Read and accept</button>\n    </PopoverPanel>\n  </Popover>\n</template>\n\n<script>\n  import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'\n\n  export default {\n    components: { Popover, PopoverButton, PopoverPanel },\n    setup() {\n      return {\n        accept: async (close) => {\n          await fetch('/accept-terms', { method: 'POST' })\n          close()\n        },\n      }\n    },\n  }\n</script>\n```\n"
  },
  {
    "path": "src/pages/heroicons-v1/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/heroicons-v1/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\n\nexport const meta = {\n  title: 'Heroicons v1.0',\n  description: `Today we're releasing Heroicons v1.0, which includes over 450+ free icons in two styles, official React and Vue libraries, and Figma assets.`,\n  date: '2021-03-29T19:00:00.000Z',\n  authors: [adamwathan],\n  image: card,\n  footer: `\n    <p>\n      Want to start playing with it?\n      <a href=\"https://heroicons.com\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Visit the Heroicons website →\n      </a>\n    </p>\n  `,\n}\n\nJust over a year ago we released the very first version of [Heroicons](https://heroicons.com), which is a set of beautiful UI icons we designed alongside Tailwind UI. Since then we've added tons of new icons, and designed and launched a dedicated web experience.\n\nToday we're excited to finally release Heroicons v1.0, which includes over 450+ free icons in two styles, official React and Vue libraries, and Figma assets.\n\n<!--more-->\n\n<a href=\"https://heroicons.com\">\n  <img src={card} alt=\"Heroicons\" />\n</a>\n\n## React + Vue Libraries\n\nIn addition to grabbing the icons you need directly from the website, you can now install our official React and Vue libraries for quick and easy access to each icon as a dedicated component.\n\nHere's what it looks like with React for example:\n\n```js\nimport { BeakerIcon } from '@heroicons/react/solid'\n\nfunction MyComponent() {\n  return (\n    <div>\n      <BeakerIcon className=\"h-5 w-5 text-blue-500\" />\n      <p>...</p>\n    </div>\n  )\n}\n```\n\n[Check out the documentation](https://github.com/tailwindlabs/heroicons) on GitHub to learn more.\n\n## Figma Assets\n\nWe've also published an official [Heroicons Figma file](https://www.figma.com/community/file/958423903283802665/heroicons) on our new Figma Community page!\n\nIt includes all the icons from Heroicons as individual Figma components so you can easily use them in your projects without having to manually import each SVG.\n"
  },
  {
    "path": "src/pages/index.js",
    "content": "import tinytime from 'tinytime'\nimport Link from 'next/link'\nimport Head from 'next/head'\nimport getAllPostPreviews from '@/getAllPostPreviews'\nimport twitterCard from '@/img/twitter-card.jpg'\nimport Header from '@/components/Header'\nimport SectionContainer from '@/components/SectionContainer'\n\nconst posts = getAllPostPreviews()\n\nconst postDateTemplate = tinytime('{MMMM} {DD}, {YYYY}')\n\nexport default function Home() {\n  return (\n    <>\n      <SectionContainer>\n        <Header />\n      </SectionContainer>\n      <SectionContainer>\n        <main>\n          <div className=\"divide-y divide-gray-200\">\n            <Head>\n              <meta name=\"twitter:card\" content=\"summary_large_image\" />\n              <meta name=\"twitter:site\" content=\"@tailwindcss\" />\n              <meta name=\"twitter:creator\" content=\"@tailwindcss\" />\n              <meta name=\"twitter:title\" content=\"Blog – Tailwind CSS\" />\n              <meta name=\"twitter:description\" content=\"News content from the Tailwind CSS team.\" />\n              <meta name=\"twitter:image\" content={`https://blog.tailwindcss.com${twitterCard}`} />\n              <meta property=\"og:url\" content=\"https://blog.tailwindcss.com\" />\n              <meta property=\"og:type\" content=\"article\" />\n              <meta property=\"og:title\" content=\"Blog – Tailwind CSS\" />\n              <meta property=\"og:description\" content=\"News content from the Tailwind CSS team.\" />\n              <meta property=\"og:image\" content={`https://blog.tailwindcss.com${twitterCard}`} />\n              <title>Blog – Tailwind CSS</title>\n              <meta name=\"description\" content=\"News content from the Tailwind CSS team.\" />\n            </Head>\n            <div className=\"pt-6 pb-8 space-y-2 md:space-y-5\">\n              <h1 className=\"text-3xl font-extrabold text-gray-900 tracking-tight sm:text-4xl md:text-[4rem] md:leading-[3.5rem]\">\n                Latest\n              </h1>\n              <p className=\"text-lg text-gray-500\">\n                All the latest Tailwind CSS news, straight from the team.\n              </p>\n            </div>\n            <ul className=\"divide-y divide-gray-200\">\n              {posts.map(({ link, module: { default: Component, meta } }) => {\n                return (\n                  <li key={link} className=\"py-12\">\n                    <article className=\"space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline\">\n                      <dl>\n                        <dt className=\"sr-only\">Published on</dt>\n                        <dd className=\"text-base font-medium text-gray-500\">\n                          <time dateTime={meta.date}>\n                            {postDateTemplate.render(new Date(meta.date))}\n                          </time>\n                        </dd>\n                      </dl>\n                      <div className=\"space-y-5 xl:col-span-3\">\n                        <div className=\"space-y-6\">\n                          <h2 className=\"text-2xl font-bold tracking-tight\">\n                            <Link href={link}>\n                              <a className=\"text-gray-900\">{meta.title}</a>\n                            </Link>\n                          </h2>\n                          <div className=\"prose max-w-none text-gray-500\">\n                            <Component />\n                          </div>\n                        </div>\n                        <div className=\"text-base font-medium\">\n                          <Link href={link}>\n                            <a\n                              className=\"text-teal-600 hover:text-teal-700\"\n                              aria-label={`Read \"${meta.title}\"`}\n                            >\n                              Read more &rarr;\n                            </a>\n                          </Link>\n                        </div>\n                      </div>\n                    </article>\n                  </li>\n                )\n              })}\n            </ul>\n          </div>\n        </main>\n      </SectionContainer>\n    </>\n  )\n}\n"
  },
  {
    "path": "src/pages/introducing-heroicons/index.mdx",
    "content": "import { steveschoger } from '@/authors'\nimport card from './card.jpg'\nimport banner from './banner.svg'\nimport iconStyles from './icon-styles.svg'\nimport heroPatternsPreview from './heropatterns-preview.jpg'\n\nexport const meta = {\n  title: 'Introducing Heroicons.com',\n  description: `Today we're launching the official Heroicons web experience, which makes it easier than every to search for icons and quickly copy them to your clipboard as Tailwind-ready HTML or JSX.`,\n  date: '2020-08-25T13:00:00.000Z',\n  authors: [steveschoger],\n  image: card,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2238',\n}\n\nA few months back we quietly released [Heroicons](https://www.heroicons.com/), a set of free SVG icons we initially designed to support the components in Tailwind UI. Today we’re launching the official [Heroicons web experience](https://www.heroicons.com/), which makes it easier than ever to search for icons and quickly copy them to your clipboard as Tailwind-ready HTML or JSX.\n\n<!--more-->\n\n<a href=\"https://heroicons.com\">\n  <img src={banner} alt=\"heroicons.com\" />\n</a>\n\nThere are currently over 220 icons available in both medium and small sizes, with size designed to serve a different use-case:\n\n- Medium icons are designed to be rendered at 24x24, and work well for things like primary navigation and marketing sections.\n- Small icons are designed to be rendered at 20x20, and work well for buttons, form elements and to support text.\n\nAll of the icons are Tailwind-ready, and are easy to style with Tailwind’s built-in size and color utilities.\n\n```html\n<svg\n  class=\"h-6 w-6 text-indigo-500\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  fill=\"none\"\n  viewBox=\"0 0 24 24\"\n  stroke=\"currentColor\"\n>\n  <path\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n    stroke-width=\"2\"\n    d=\"M17 8l4 4m0 0l-4 4m4-4H3\"\n  />\n</svg>\n```\n\nFor best results, use h-6 w-6 for medium icons, and h-5 w-5 for small icons.\n\n## Just the beginning\n\nWe’ve got lots of ideas for both new icons, as well as new icon styles _(duotone anyone?)_ that we’re excited to design and release in the coming months.\n\n<img src={iconStyles} alt=\"\" />\n\nDesigning this site also got me itching to refresh the Hero Patterns site, so you’ll probably see something like this show up at [heropatterns.com](http://www.heropatterns.com/) pretty soon:\n\n<img src={heroPatternsPreview} alt=\"\" />\n\nWe’ve got a bunch of [other “Hero” domains](https://twitter.com/steveschoger/status/1266042614710767616?s=20) waiting to be put to use too, and I’m pumped to reveal what we’re working on for those soon.\n\n## Got a suggestion?\n\nIf you have any ideas for new icons we’d love to hear them! Head over to the [Heroicons GitHub repository](https://github.com/tailwindlabs/heroicons) and open an issue to make a suggestion.\n"
  },
  {
    "path": "src/pages/introducing-linting-for-tailwindcss-intellisense/index.mdx",
    "content": "import { bradlc } from '@/authors'\nimport imgCss from './css.png'\nimport imgCss2x from './css@2x.png'\nimport imgHtml from './html.png'\nimport imgHtml2x from './html@2x.png'\nimport applyQuickFixVideo from './apply-quick-fix.mp4'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Introducing linting for Tailwind CSS IntelliSense',\n  description:\n    'Today we’re releasing a new version of the Tailwind CSS IntelliSense extension for Visual Studio Code that adds Tailwind-specific linting to both your CSS and your markup.',\n  date: '2020-06-23T18:52:03Z',\n  authors: [bradlc],\n  image,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/1956',\n}\n\nToday we’re releasing a new version of the [Tailwind CSS IntelliSense extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) that adds Tailwind-specific linting to both your CSS and your markup.\n\n<!--more-->\n\n## Detecting errors in your CSS\n\nTailwind already detects CSS errors, for example when you mistype a screen name in the `@screen` directive. The linting feature for Tailwind CSS IntelliSense surfaces these errors and displays them in context, directly inside your editor. The linter will validate your `@tailwind`, `@screen`, `@variants` and `@apply` directives, as well as any `theme` function calls:\n\n<img\n  width=\"1524\"\n  height=\"857\"\n  alt=\"Screen capture showing CSS linting in action\"\n  src={imgCss}\n  srcSet={`${imgCss} 762w, ${imgCss2x} 1524w`}\n  sizes=\"(min-width: 1280px) 51rem, (min-width: 768px) 45rem, (min-width: 640px) calc(100vw - 3rem), calc(100vw - 2rem)\"\n/>\n\n## Catching conflicts in your HTML\n\nThere is one more lint rule which analyses class lists in your template files and highlights any instances where utilities seem to be in conflict. For example you probably didn’t intend to have `mt-4` and `mt-6` in the same class list!\n\n<img\n  width=\"1524\"\n  height=\"857\"\n  alt=\"Screen capture showing markup linting in action\"\n  src={imgHtml}\n  srcSet={`${imgHtml} 762w, ${imgHtml2x} 1524w`}\n  sizes=\"(min-width: 1280px) 51rem, (min-width: 768px) 45rem, (min-width: 640px) calc(100vw - 3rem), calc(100vw - 2rem)\"\n/>\n\n## Quick fixes included\n\nTo make it as easy as possible to fix any issues, all of the lint rules have their own \"quick fixes\" which can be triggered directly within Visual Studio Code. If you accidentally typed `@screen small` instead of `@screen sm`, the editor can automatically replace `small` with `sm` for you!\n\nAs well as simple text replacements there’s also some more interesting quick fixes for the more complex lint rules. Take a look at how the extension can automatically refactor an invalid `@apply` directive:\n\n<video src={applyQuickFixVideo} width=\"1600\" height=\"900\" controls />\n\n## Configuration\n\nWe think you’ll love the new lint feature, but if you don’t, or you just want to tweak some behavior, we’ve got you covered. You can decide how each rule violation is treated: is it an `error`, or just a `warning`, or do you want to `ignore` the rule altogether? If you really want to you can disable linting entirely using the new `tailwindCSS.validate` setting.\n\nCheck out the [extension readme](https://github.com/tailwindcss/intellisense#tailwindcssvalidate) for more details about configuring the lint rules to suit your workflow.\n\n## Conclusion\n\nLinting is available now in `v0.4.0` of Tailwind CSS IntelliSense! If you already have the extension you may need to reload Visual Studio Code to get the update, and if you don’t you can install it via the [extension marketplace](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss).\n\nThis is the very first iteration of this feature, and we’d love to hear your feedback! Do you have an idea for a new lint rule? Let us know!\n"
  },
  {
    "path": "src/pages/introducing-tailwind-play/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: `Introducing Tailwind Play`,\n  description: `Tailwind Play is an advanced online playground for Tailwind CSS that lets you use all of Tailwind's build-time features directly in the browser.`,\n  date: '2020-10-07T13:00:00.000Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2511',\n}\n\nTo get the most out of Tailwind, you need a build step. It's the only way to be able to customize your [`tailwind.config.js`](https://tailwindcss.com/docs/configuration) file, extract components with [`@apply`](https://tailwindcss.com/docs/functions-and-directives#apply), or include [plugins](https://tailwindcss.com/docs/plugins).\n\nThis isn't a problem if you've already bought in to the framework, but if you're just trying to kick the tires for the first time it's a lot of friction. You either have to set up a local development environment with PostCSS support, or stick to the static CDN build, which means you lose out on lots of cool features.\n\nSo today we're excited to release the first version of [**Tailwind Play**](https://play.tailwindcss.com), an advanced online playground for Tailwind CSS that lets you use all of Tailwind's build-time features directly in the browser.\n\n<!--more-->\n\n<div className=\"my-8 aspect-w-16 aspect-h-9\">\n  <iframe\n    src=\"https://www.youtube.com/embed/eCWhTZ34Hck\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\nIt includes support for all of Tailwind's coolest features, plus tons of stuff that's even better in Tailwind Play than it is in your editor, like:\n\n- Customizing your Tailwind theme\n- Enabling special variants, like `group-hover` or `focus-within`\n- Using custom directives in your CSS like `@apply`, `@variants`, and `@responsive`\n- Adding plugins like `@tailwindcss/typography`\n- Intelligent code completion and linting\n- Responsive design mode\n- One-click sharing\n\nThe code completion even updates the rendered preview in _real-time_, which creates an incredible design workflow in the browser — just navigate through different padding utilities with the arrow keys for example to find the perfect value without ever saving the file or even hitting enter!\n\nOur responsive design mode that lets you fine-tune the viewport while you're working on your design, just like you can in Chrome DevTools. You can even drag the viewport beyond the available space, and the preview area will automatically zoom out, letting you design for larger screens even when you have limited space.\n\nOne-click sharing really is just that — you don't even need to create an account. Click \"Share\" and you've immediately got a link to a snapshot of what you're working on that you can share online.\n\n**Check it out at [play.tailwindcss.com](https://play.tailwindcss.com)** and let us know what you think!\n"
  },
  {
    "path": "src/pages/just-in-time-the-next-generation-of-tailwind-css/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: `Just-In-Time: The Next Generation of Tailwind CSS`,\n  description: `One of the hardest constraints we've had to deal with as we've improved Tailwind CSS over the years is the generated file size in development. Today I'm super excited to share a new project that makes this constraint a thing of the past: a just-in-time compiler for Tailwind CSS.`,\n  date: '2021-03-15T16:30:00.000Z',\n  authors: [adamwathan],\n  image,\n  footer: `\n    <p>\n      Ready to try it out?\n      <a href=\"https://github.com/tailwindlabs/tailwindcss-jit\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Get started →\n      </a>\n    </p>\n  `,\n}\n\n_**Update**: As of Tailwind CSS v2.1, the new Just-in-Time engine is included right in Tailwind CSS itself, so you don't need the `@tailwindcss/jit` package anymore. [Learn more in the documentation](https://tailwindcss.com/docs/just-in-time-mode)._\n\nOne of the hardest constraints we've had to deal with as we've improved Tailwind CSS over the years is the generated file size in development. With enough customizations to your config file, the generated CSS can reach 10mb or more, and there's only so much CSS that build tools and even the browser itself will comfortably tolerate.\n\nFor that reason, you've always had to be careful about expensive changes to your config file like adding too many extra breakpoints or enabling extra variants like `disabled` or `focus-visible`.\n\nToday I'm super excited to share a new project we've been working on that makes these considerations a thing of the past: [**a just-in-time compiler for Tailwind CSS**](https://github.com/tailwindlabs/tailwindcss-jit).\n\n<!--more-->\n\n<div className=\"aspect-w-16 aspect-h-9 my-12\">\n  <iframe\n    src=\"https://www.youtube.com/embed/3O_3X7InOw8\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\n[**@tailwindcss/jit**](https://github.com/tailwindlabs/tailwindcss-jit) is a new experimental library that compiles all of your CSS _on-demand_ as you author your template files, instead of generating your entire stylesheet up front.\n\nThis comes with a lot of advantages:\n\n- **Lightning fast build times**. Tailwind can take 3–8s to initially compile using our CLI, and upwards of 30–45s in webpack projects because webpack struggles with large CSS files. This library can compile even the biggest projects in about 800ms _(with incremental rebuilds as fast as 3ms)_, no matter what build tool you're using.\n- **Every variant is enabled out of the box**. Variants like `focus-visible`, `active`, `disabled`, and others are not normally enabled by default due to file-size considerations. Since this library generates styles on demand, you can use any variant you want, whenever you want. You can even stack them like `sm:hover:active:disabled:opacity-75`. Never configure your variants again.\n- **Generate arbitrary styles without writing custom CSS.** Ever needed some ultra-specific value that wasn't part of your design system, like `top: -113px` for a quirky background image? Since styles are generated on demand, you can just generate a utility for this as needed using square bracket notation like `top-[-113px]`. Works with variants too, like `md:top-[-113px]`.\n- **Your CSS is identical in development and production**. Since styles are generated as they are needed, you don't need to purge unused styles for production, which means you see the exact same CSS in all environments. Never worry about accidentally purging an important style in production again.\n- **Better browser performance in development**. Since development builds are as small as production builds, the browser doesn't have to parse and manage multiple megabytes of pre-generated CSS. In projects with heavily extended configurations this makes dev tools a lot more responsive.\n\nTry it today by installing `@tailwindcss/jit` and swapping it into your PostCSS configuration:\n\n```shell\nnpm install -D @tailwindcss/jit tailwindcss postcss autoprefixer\n```\n\n```js\n// postcss.config.js\nmodule.exports = {\n  plugins: {\n    '@tailwindcss/jit': {},\n    autoprefixer: {},\n  },\n}\n```\n\nWe're shipping it as a separate library for now, but once we've worked out all the kinks we're going to roll it right back into `tailwindcss` behind a configuration option, and we're aiming to make it the default in Tailwind CSS v3.0 later this year.\n\n[Learn more about the project on GitHub](https://github.com/tailwindlabs/tailwindcss-jit), then install it, play with it, bend it, break it, and let us know what you think!\n"
  },
  {
    "path": "src/pages/multi-line-truncation-with-tailwindcss-line-clamp/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Multi-line truncation with @tailwindcss/line-clamp',\n  description: `A few weeks back we released @tailwindcss/line-clamp, an official Tailwind CSS plugin for truncating text to a specific number of lines.`,\n  date: '2021-01-24T20:00:00Z',\n  authors: [adamwathan],\n  image,\n}\n\n<!--excerpt-->\n\nImagine you're implementing a beautiful design you or someone on your team carefully crafted in Figma. You've nailed all the different layouts at each breakpoint, perfected the whitespace and typography, and the photography you're using is really bringing the design to life.\n\nIt looks totally amazing — until you connect it your actual production content and realize that your beautiful grid of blog cards falls apart because, of course, _real_ article excerpts aren't all magically exactly three lines long, and now each card is a different height.\n\nSound familiar? If so, the line-clamp plugin is here to save your bacon.\n\n<!--/excerpt-->\n\n<p className=\"lead\">\n  A few weeks back we released{' '}\n  <a href=\"https://github.com/tailwindlabs/tailwindcss-line-clamp\">\n    <code>@tailwindcss/line-clamp</code>\n  </a>\n  , an official Tailwind CSS plugin for truncating text to a specific number of lines.\n</p>\n\nImagine you're implementing a beautiful design you or someone on your team carefully crafted in Figma. You've nailed all the different layouts at each breakpoint, perfected the whitespace and typography, and the photography you're using is really bringing the design to life.\n\nIt looks totally amazing — until you connect it your actual production content and realize that your beautiful grid of blog cards falls apart because, of course, _real_ article excerpts aren't all magically exactly three lines long, and now each card is a different height.\n\nSound familiar? If so, the line-clamp plugin is here to save your bacon.\n\n<div className=\"relative\" style={{ paddingBottom: '56.25%' }}>\n  <iframe\n    className=\"absolute inset-0 h-full w-full\"\n    src=\"https://www.youtube.com/embed/klh-jMTm5PU\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\nFirst, install the plugin and add it to your `tailwind.config.js` file:\n\n```shell\nnpm install @tailwindcss/line-clamp\n```\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  plugins: [\n    // ...\n    require('@tailwindcss/line-clamp'),\n  ],\n}\n```\n\nThen all you need to do is add a `line-clamp-{n}` utility to any block of text to automatically truncate to _n_ lines with a trailing ellipsis:\n\n```html\n<p class=\"line-clamp-3\">\n  Here's a block of text from a blog post that isn't conveniently three lines long like you designed\n  for originally. It's probably like 6 lines on mobile or even on desktop depending on how you have\n  things laid out. Truly a big pain in the derriere, and not the sort of thing you expected to be\n  wasting your time trying to deal with at 4:45pm on a Friday am I right? You've got tickets to\n  SmackDown and you heard there's gonna be a dark match with that local guy from two towns over that\n  your cousin went to high school with before the show starts, and you're gonna miss it if you're\n  not there early.\n</p>\n```\n\nFor more details, [check out the documentation](https://github.com/tailwindlabs/tailwindcss-line-clamp/) over on the GitHub repository.\n"
  },
  {
    "path": "src/pages/simon-vrachliotis-joins-tailwind-labs/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Simon Vrachliotis Joins Tailwind Labs',\n  description: `Today we are super excited to share that Simon Vrachliotis has joined the development team at Tailwind Labs.`,\n  date: '2020-07-19T19:00:00.000Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2043',\n}\n\nToday we are super excited to share that [Simon Vrachliotis](https://twitter.com/simonswiss) has joined the development team at Tailwind Labs! (We just finalized that new business name by the way, pretty cool right?)\n\nSimon has been a utility-first true believer since before Tailwind even existed, and authored an oft-referenced case study on his experience [rebuilding his company’s entire website with functional CSS in 10 days](https://hackernoon.com/full-re-write-with-tachyons-and-functional-css-a-case-study-part-1-635ccb5fb00b) way back in February 2017.\n\n<!--more-->\n\nHe also created the [first-ever Tailwind CSS video course](https://egghead.io/courses/build-user-interfaces-by-composing-css-utility-classes-with-tailwind), published on egghead.io only 4 months after we released v0.1.0.\n\nSteve and I met Simon for the first time when we visited Sydney for Laracon AU back in 2018, where Simon was giving a talk on, of course, utility-first CSS:\n\n<figure>\n  <div className=\"aspect-w-16 aspect-h-9\">\n    <iframe\n      src=\"https://player.vimeo.com/video/294976504\"\n      frameBorder=\"0\"\n      allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n      allowFullScreen\n    ></iframe>\n  </div>\n  <figcaption className=\"italic\">\n    We know this video isn't from Laracon, but this recording turned out better :)\n  </figcaption>\n</figure>\n\nHe knocked it out of the park, and it has been an absolute pleasure getting to know Simon better over the time since we first met.\n\nSimon is a talented developer, an amazing teacher, and has such a contagious enthusiasm for the sort of work we do that we knew we had to have him on the team if we ever had the chance.\n\nHe’s joining in a product and community focused role, and will be doing lots of amazing work helping us build things like Tailwind UI, as well as creating educational resources to help even more people have success with Tailwind CSS.\n\nWe couldn’t be more excited to be welcoming him to the team!\n"
  },
  {
    "path": "src/pages/tailwind-ui-ecommerce/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/tailwind-ui-ecommerce/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\nimport productPagePreview from './product-page-preview.jpg'\n\nexport const meta = {\n  title: 'Introducing Tailwind UI Ecommerce',\n  description: `Almost 6 months in the making, we finally released Tailwind UI Ecommerce — the first all-new component kit for Tailwind UI since the initial launch back in February 2020.`,\n  date: '2021-08-11T19:30:00.000Z',\n  authors: [adamwathan],\n  image: card,\n  footer: `\n    <p>\n      Want to check it out?\n      <a href=\"https://tailwindui.com\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Visit the Tailwind UI website →\n      </a>\n    </p>\n  `,\n}\n\n<!--excerpt-->\n\nAlmost 6 months in the making, we finally released [Tailwind UI Ecommerce](https://tailwindui.com/#product-ecommerce) — the first all-new component kit for Tailwind UI since the initial launch back in February 2020.\n\n<!--/excerpt-->\n\nAlmost 6 months in the making, we finally released the first all-new component kit for [Tailwind UI](https://tailwindui.com) since the initial launch back in February 2020!\n\n<a href=\"https://tailwindui.com\">\n  <img src={card} alt=\"Tailwind UI Ecommerce available now\" />\n</a>\n\n[Tailwind UI Ecommerce](https://tailwindui.com/#product-ecommerce) adds over 100 new components across 14 new component categories and 7 new page example categories, including stuff like:\n\n- Product Overviews\n- Product Lists\n- Category Previews\n- Shopping Carts\n- Category Filters\n- Product Quickviews\n- Store Navigation\n- Promo Sections\n- Checkout Forms\n- Customer Reviews\n- Order Summaries\n- Storefront Pages\n- Product Pages\n- Order History Pages\n\n...and more.\n\nFor a quick preview, check out this product page example we shared via our newsletter last week:\n\n<a href=\"https://tailwindui.com/page-examples/ecommerce-product-page-02\">\n  <img\n    src={productPagePreview}\n    alt=\"Preview one of the new product page examples\"\n  />\n</a>\n\nIt's been a really fun and challenging process putting this together, and I'm really proud of how it turned out. I wrote up a big post about [\"Designing Tailwind UI Ecommerce\"](https://blog.tailwindcss.com/designing-tailwind-ui-ecommerce) that's worth a read if you're interested in the process behind putting together a new Tailwind UI product like this.\n\nYou can check out a bunch more interactive previews as well as screenshots of every single new example over at [the Tailwind UI website](https://tailwindui.com/#product-ecommerce).\n\nIf you like what you see, consider [purchasing a license](https://tailwindui.com/pricing) — it's the best way to support our work on Tailwind CSS and Headless UI and makes it possible for us to keep making these tools better and better.\n"
  },
  {
    "path": "src/pages/tailwind-ui-now-with-react-and-vue-support/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/tailwind-ui-now-with-react-and-vue-support/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind UI: Now with React + Vue support',\n  description: `Last year we released Tailwind UI — a huge directory of professionally designed UI examples built with Tailwind CSS. Today we’re excited to add first class support for React and Vue 3 to all of the examples in Tailwind UI, which makes it even easier to adapt them for your projects.`,\n  date: '2021-04-14T16:01:00.000Z',\n  authors: [adamwathan],\n  image: card,\n  footer: `\n    <p>\n      Want to check it out?\n      <a href=\"https://tailwindui.com\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Visit the Tailwind UI website →\n      </a>\n    </p>\n  `,\n}\n\nLast year we released [Tailwind UI](https://tailwindui.com) — a huge directory of professionally designed UI examples built with Tailwind CSS. Up until now, all of the examples in Tailwind UI have been pure HTML which is sort of the lowest common denominator for all web developers, and makes it possible to adapt them to any templating language or JavaScript framework.\n\nToday we’re excited to add first class support for React and Vue 3 to all of the examples in Tailwind UI, which makes it even easier to adapt them for your projects.\n\n<!--more-->\n\n<a href=\"https://tailwindui.com\">\n  <img src={card} alt=\"Tailwind UI: Now for React and Vue\" />\n</a>\n\nIt’s been [a long journey](https://blog.tailwindcss.com/building-react-and-vue-support-for-tailwind-ui) but I am super proud of where we ended up on this one, and really think it’s going to make Tailwind UI a useful tool for a whole new group of Tailwind CSS users.\n\n## Functional and accessible\n\nAll of the React and Vue examples in Tailwind UI are powered [Headless UI](https://headlessui.dev) which is a library of components we developed to decouple all of the complicated JS behavior you need to build complex components like modals and dropdowns from the actual styles and markup.\n\nHeadless UI handles all of the ARIA attribute management, keyboard interactions, focus handling, and more for you, meaning all of the React and Vue examples provided in Tailwind UI are fully functional, with no need to write any of that complex JS stuff yourself. All of that gnarly complexity is safely tucked away in your `node_modules` folder where we can make improvements and fix bugs on your behalf, without you ever having to change your own code.\n\n## Fully customizable\n\nWith Headless UI, we’ve managed to abstract away all of the complicated JS functionality without taking away any control over the actual markup. That means that the entire design is still in entirely under your control.\n\n```jsx\nimport { useState } from 'react'\nimport { Switch } from '@headlessui/react'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default function Example() {\n  const [enabled, setEnabled] = useState(false)\n\n  return (\n    <Switch\n      checked={enabled}\n      onChange={setEnabled}\n      className={classNames(\n        enabled ? 'bg-indigo-600' : 'bg-gray-200',\n        'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'\n      )}\n    >\n      <span className=\"sr-only\">Use setting</span>\n      <span\n        aria-hidden=\"true\"\n        className={classNames(\n          enabled ? 'translate-x-5' : 'translate-x-0',\n          'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'\n        )}\n      />\n    </Switch>\n  )\n}\n```\n\nYou can copy a React or Vue example from Tailwind UI and change absolutely everything about it, from the border radius to the padding to the box shadows to the font-size, all by simply adding utility classes like you’re used to.\n\n## Get started\n\nIf you’re already a Tailwind UI customer, all of this stuff is available to you today as a totally free update. Just log in to your account, select between HTML, React, or Vue in the dropdown above any component, and grab the code in the format you want.\n\nIf you haven’t checked out Tailwind UI yet, browse the free preview components to get a feel for how it all works. It’s an awesome tool for moving fast on a new side-project idea, finding inspiration for a new feature you need to build at work, or learning how to implement a specific little UI trick with Tailwind, and a great way to support our work on open-source projects like Tailwind CSS, Headless UI, and Heroicons.\n"
  },
  {
    "path": "src/pages/tailwindcss-1-5/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v1.5.0',\n  description: `Tailwind CSS v1.5.0 is here, now with component variants, responsive container variants, focus-visible support, and more.`,\n  date: '2020-07-15T18:55:18.391Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2033',\n}\n\n<!--excerpt-->\n\nI was hoping to save v1.5.0 for something _really_ exciting but we needed a new feature to support the new [`@tailwindcss/typography`](https://github.com/tailwindcss/typography) plugin so h\\*ck it, we're dropping some new stuff on you early.\n\nNo breaking changes, this is a minor release and we're professionals you silly goose.\n\n<!--/excerpt-->\n\nI was hoping to save v1.5.0 for something _really_ exciting but we needed a new feature to support the new [`@tailwindcss/typography`](https://github.com/tailwindcss/typography) plugin so h\\*ck it, we're dropping some new stuff on you early.\n\nNo breaking changes, this is a minor release and we're professionals you silly goose.\n\n## New Features\n\n### Component `variants` support\n\nUntil Tailwind CSS v1.5.0, only \"utility\" classes were really intended to be used with `variants` (like \"responsive\", \"hover\", \"focus\", etc.)\n\nWhile these are still much more useful for utilities than any other type of class, we now support generating variants for component classes as well, like the `prose` classes in the new `@tailwindcss/typography` plugin:\n\n```html\n<article class=\"prose md:prose-lg\">\n  <!-- Content -->\n</article>\n```\n\nYou can take advantage of this feature in your own component classes by using the new `variants` option in the second argument of the `addComponents` plugin API:\n\n```js\nplugin(function ({ addComponents })) {\n  addComponents({\n    '.card': {\n      // ...\n    }\n  }, {\n    variants: ['responsive']\n  })\n})\n```\n\n...or using the array shorthand you might be familiar with from the `addUtilities` API:\n\n```js\nplugin(function ({ addComponents })) {\n  addComponents({\n    '.card': {\n      // ...\n    }\n  }, ['responsive'])\n})\n```\n\nTo take advantage of these feature in your custom CSS (rather than using the plugin API), you can use a new `@layer` directive to explicitly tell Tailwind that your styles belong to the \"components\" bucket:\n\n```css\n@layer components {\n  @responsive {\n    .card {\n      /* ... */\n    }\n  }\n}\n```\n\nThis helps Tailwind purge your unused CSS correctly, ensuring it doesn't remove any responsive component variants when using the default \"conservative\" purge mode.\n\n### Responsive `container` variants\n\nPiggy-backing off of the new component `variants` support, the `container` class now supports variants!\n\n```html\n<!-- Only lock the width at `md` sizes and above -->\n<div class=\"md:container\">\n  <!-- ... -->\n</div>\n```\n\nWe've enabled responsive variants by default, but if you are sick in the head you can also manually enable other variants like `focus`, `group-hover`, whatever:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  variants: {\n    container: ['responsive', 'focus', 'group-hover'],\n  },\n}\n```\n\n### New `focus-visible` variant\n\nWe've added support for the [`:focus-visible` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) using a new `focus-visible` variant.\n\nThis is super useful for adding focus styles that _only_ appear to keyboard users, and are ignored for mouse users:\n\n```html\n<button class=\"focus-visible:outline-none focus-visible:shadow-outline ...\">\n  Click me\n</button>\n```\n\nIt's not enabled for anything by default, but you can enable it in the `variants` section of your config file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  variants: {\n    backgroundColor: ['responsive', 'hover', 'focus', 'focus-visible'],\n  },\n}\n```\n\nBrowser support is still pretty weak on this but getting better. In the mean time, check out the [polyfill](https://github.com/WICG/focus-visible) and corresponding [PostCSS plugin](https://github.com/csstools/postcss-focus-visible) if you'd like to use this in all browsers right away.\n\n### New `checked` variant\n\nWe've added a new `checked` variant you can use to conditionally style things like checkboxes and radio buttons:\n\n```html\n<input type=\"checkbox\" class=\"bg-white checked:bg-blue-500\" />\n```\n\nIt's not enabled for anything by default, but you can enable it in the `variants` section of your config file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  variants: {\n    backgroundColor: ['responsive', 'hover', 'focus', 'checked'],\n  },\n}\n```\n"
  },
  {
    "path": "src/pages/tailwindcss-1-6/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v1.6.0',\n  description: `Tailwind CSS v1.6.0 is now available, with animations and more!`,\n  date: '2020-07-28T16:58:33.714Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2088',\n}\n\nIt's like Tailwind CSS v1.5 except now there's animation support, overscroll utilities, and more!\n\nThere aren't supposed to be any breaking changes here, but I thought that [last time](https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.5.0) too. If I _did_ break something, first person to report it gets a Tailwind shirt.\n\n<!--more-->\n\n## New Features\n\n### Animation support\n\nTailwind CSS v1.6 adds a brand new `animation` core plugin, with 4 general purpose animations included out of the box:\n\n- `animate-spin`\n- `animate-ping`\n- `animate-pulse`\n- `animate-bounce`\n\n```html\n<button type=\"button\" class=\"bg-indigo-600 ...\" disabled>\n  <svg class=\"animate-spin h-5 w-5 mr-3 ...\" viewBox=\"0 0 24 24\">\n    <!-- ... -->\n  </svg>\n  Processing\n</button>\n```\n\nThese are completely customizable as always, using the `animation` and `keyframes` sections of your `tailwind.config.js` theme:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      animation: {\n        wiggle: 'wiggle 1s ease-in-out infinite',\n      },\n      keyframes: {\n        wiggle: {\n          '0%, 100%': { transform: 'rotate(-3deg)' },\n          '50%': { transform: 'rotate(3deg)' },\n        },\n      },\n    },\n  },\n}\n```\n\nFor more information and a live demo, [read the new animation documentation](https://tailwindcss.com/docs/animation). For behind the scenes details about the design rationale, [check out the pull request](https://github.com/tailwindlabs/tailwindcss/pull/2068).\n\n### New `prefers-reduced-motion` variants\n\nTo go along with the new animation features, we've also added new `motion-safe` and `motion-reduce` variants that allow you to conditionally apply CSS based on the [`prefers-reduced-motion` media feature](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion).\n\nThese can be useful in conjunction with transition and animation utilities to disable problematic motion for users who are sensitive to it:\n\n```html\n<div class=\"... transition duration-150 ease-in-out motion-reduce:transition-none ...\"></div>\n```\n\n...or to explicitly opt-in to motion to make sure it's only being shown to users who haven't opted out:\n\n```html\n<div class=\"... motion-safe:transition duration-150 ease-in-out ...\"></div>\n```\n\nThese can be combined with responsive variants and pseudo-class variants as well:\n\n```html\n<!-- With responsive variants -->\n<div class=\"sm:motion-reduce:translate-y-0\"></div>\n\n<!-- With pseudo-class variants -->\n<div class=\"motion-reduce:hover:translate-y-0\"></div>\n\n<!-- With responsive and pseudo-class variants -->\n<div class=\"sm:motion-reduce:hover:translate-y-0\"></div>\n```\n\nThese are currently not enabled for any utilities by default, but you can enabled them as needed in the `variants` section of your `tailwind.config.js` file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  variants: {\n    translate: ['responsive', 'hover', 'focus', 'motion-safe', 'motion-reduce'],\n  },\n}\n```\n\nFor more details, check out [the updated variants documentation](https://tailwindcss.com/docs/pseudo-class-variants).\n\n### New `overscroll-behavior` utilities\n\nWe've also added new utilities for the [`overscroll-behavior`](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) property.\n\nYou can use these utilities to control how \"scroll chaining\" works in your sites, and avoid scrolling the whole page when you reach the top or bottom of an embedded scrollable area.\n\n```html\n<div class=\"overscroll-y-contain ...\">\n  <!-- ... -->\n</button>\n```\n\nNote that this is currently **not supported in Safari**, but in my opinion it's not a huge deal to treat this as a progressive enhancement anyways, since it falls back fairly gracefully.\n\nThis plugin can be configured in your `tailwind.config.js` file as `overscrollBehavior`:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n\n  // Disabling the plugin\n  corePlugins: {\n    overscrollBehavior: false,\n  },\n\n  // Customizing the enabled variants\n  variants: {\n    overscrollBehavior: ['responsive', 'hover'],\n  },\n}\n```\n\n### Generate your CSS without an input file\n\nIf you never write any custom CSS and you're sick of creating this file all the time...\n\n```css\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n```\n\n...then I've got news for you baby — if you're using our `tailwindcss` CLI tool you can start depositing those 58 characters into your savings account instead of wasting them on a pointless CSS file.\n\nThe input file argument is now optional in the CLI tool, so if you don't actually _need_ a custom CSS file, you can just write this:\n\n```bash\nnpx tailwindcss build -o compiled.css\n```\n\nYour kids are going to be so grateful for the extra time you get to spend together.\n"
  },
  {
    "path": "src/pages/tailwindcss-1-7/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v1.7.0',\n  description: `Tailwind CSS v1.7.0 is now available, with gradients and more!`,\n  date: '2020-08-18T19:15:00.000Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2183',\n}\n\nAnother new Tailwind release is here! This time with support for gradients, background-clip, experimental support for using `@apply` with variant utilities, and tons more. Let's dig in!\n\n<!--more-->\n\n## New features\n\n### Gradients\n\nThe big one for this release — Tailwind now ships with built-in support for background gradients!\n\nGradients are designed with a highly composable API that lets you specify up to three color stops in one of 8 directions by default:\n\n```html\n<div class=\"bg-gradient-to-r from-orange-400 via-red-500 to-pink-500\">\n  <!-- ... -->\n</div>\n```\n\n![](https://user-images.githubusercontent.com/4323180/90427639-ce57e280-e090-11ea-8611-53a64707fafc.png)\n\nThis is made possible by a new `backgroundImage` core plugin (which you can use for any background images you like!) and a new `gradientColorStops` core plugin.\n\nThe default configuration for these plugins looks like this:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    backgroundImage: {\n      'gradient-to-t': 'linear-gradient(to top, var(--gradient-color-stops))',\n      'gradient-to-tr': 'linear-gradient(to top right, var(--gradient-color-stops))',\n      'gradient-to-r': 'linear-gradient(to right, var(--gradient-color-stops))',\n      'gradient-to-br': 'linear-gradient(to bottom right, var(--gradient-color-stops))',\n      'gradient-to-b': 'linear-gradient(to bottom, var(--gradient-color-stops))',\n      'gradient-to-bl': 'linear-gradient(to bottom left, var(--gradient-color-stops))',\n      'gradient-to-l': 'linear-gradient(to left, var(--gradient-color-stops))',\n      'gradient-to-tl': 'linear-gradient(to top left, var(--gradient-color-stops))',\n    },\n    gradientColorStops: (theme) => theme('colors'),\n  },\n  variants: {\n    backgroundImage: ['responsive'],\n    gradientColorStops: ['responsive', 'hover', 'focus'],\n  },\n}\n```\n\nLearn more [the original pull request](https://github.com/tailwindlabs/tailwindcss/pull/2176).\n\n### New background-clip utilities\n\nWe've also added a new `backgroundClip` core plugin that you can use to control how background are rendered within an element.\n\nIt includes 4 new utilities:\n\n| Class             | CSS                            |\n| ----------------- | ------------------------------ |\n| `bg-clip-border`  | `background-clip: border-box`  |\n| `bg-clip-padding` | `background-clip: padding-box` |\n| `bg-clip-content` | `background-clip: content-box` |\n| `bg-clip-text`    | `background-clip: text`        |\n\nCombined with the new gradient features, you can use this to do cool gradient text stuff like this:\n\n```html\n<h1 class=\"text-6xl font-bold\">\n  <span class=\"bg-clip-text text-transparent bg-gradient-to-r from-teal-400 to-blue-500\">\n    Greetings from Tailwind v1.7.\n  </span>\n</h1>\n```\n\n![](https://user-images.githubusercontent.com/4323180/90427567-b54f3180-e090-11ea-988b-c95ed716866f.png)\n\nOnly responsive variants are enabled for the `backgroundClip` plugin by default:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  variants: {\n    backgroundClip: ['responsive'],\n  },\n}\n```\n\n### New gap utility aliases\n\nFor some dumb reason I named the `column-gap` and `row-gap` utilities `col-gap-{n}` and `row-gap-{n}` respectively, which isn't terrible but it's not consistent with how other things in Tailwind are named.\n\nI was finding myself getting them wrong all the time — is `row-gap` the gaps in a row, or the gap between rows?\n\nTailwind v1.7 introduces new `gap-x-{n}` and `gap-y-{n}` utilities that do the exact same thing but have names that don't suck. They make way more sense than the actual CSS names now that gap for flexbox is starting to roll out too, since flexbox has no \"columns\".\n\nThese utilities will replace the old ones in v2.0, but for now they both exist together.\n\n**We recommend migrating to the new names now, and disabling the old names using this feature flag:**\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  future: {\n    removeDeprecatedGapUtilities: true,\n  },\n  // ...\n}\n```\n\nTailwind will issue a warning in the console to remind you that you are including deprecated classes in your build until you enable this flag.\n\n### New `contents` display utility\n\nWe've added a new `contents` class for the recent `display: contents` CSS feature.\n\n```html\n<div class=\"flex\">\n  <div><!-- ... --></div>\n  <!-- This container will act as a phantom container, and its children will be treated as part of the parent flex container -->\n  <div class=\"contents\">\n    <div><!-- ... --></div>\n    <div><!-- ... --></div>\n  </div>\n  <div><!-- ... --></div>\n</div>\n```\n\nLearn more about it in [this great article by Rachel Andrew](https://rachelandrew.co.uk/archives/2016/01/29/vanishing-boxes-with-display-contents/).\n\n### Default letter-spacing per font-size\n\nYou can now configure a default letter-spacing value for each font-size in your `tailwind.config.js` theme, using a tuple syntax:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    fontSize: {\n      2xl: ['24px', {\n        letterSpacing: '-0.01em',\n      }],\n      // Or with a default line-height as well\n      3xl: ['32px', {\n        letterSpacing: '-0.02em',\n        lineHeight: '40px',\n      }],\n    }\n  }\n}\n```\n\nThis new syntax is supported in addition to the simpler `[{fontSize}, {lineHeight}]` syntax that was recently introduced.\n\n### Divide border styles\n\nWe've added utilities for setting the border style on the `divide` utilities:\n\n```html\n<div class=\"divide-y divide-dashed\">\n  <div><!-- ... --></div>\n  <div><!-- ... --></div>\n  <div><!-- ... --></div>\n  <div><!-- ... --></div>\n</div>\n```\n\nThese utilities include `responsive` variants by default:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  variants: {\n    divideStyle: ['responsive'],\n  },\n}\n```\n\n### Access entire config object from plugins\n\nThe `config` function passed to the plugin API now returns the entire config option when invoked with no arguments:\n\n```js\ntailwind.plugin(function ({ config, addUtilities, /* ... */ })) {\n  // Returns entire config object\n  config()\n})\n```\n\n### Define colors as closures\n\nYou can now define your colors as callbacks, which receive a bag of parameters you can use to generate your color value.\n\nThis is particularly useful when trying to make your custom colors work with the `backgroundOpacity`, `textOpacity`, etc. utilities\n\n```\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    colors: {\n      primary: ({ opacityVariable }) => `rgba(var(--color-primary), var(${variable}, 1))`,\n    },\n  },\n}\n```\n\nCurrently the only thing passed through is an `opacityVariable` property, which contains the name of the current opacity variable (`--background-opacity`, `--text-opacity`, etc.) depending on which plugin is using the color.\n\n## Deprecations\n\nTailwind v1.7 introduces a new feature flagging and deprecation system designed to make upgrades as painless as possible.\n\nAny time we deprecate functionality or introduce new (stable) breaking changes, they will be available in Tailwind v1.x under a `future` property in your `tailwind.config.js` file.\n\nWhenever there are deprecations or breaking changes available, Tailwind will warn you in the console on every build until you adopt the new changes and enable the flag in your config file:\n\n```\nrisk - There are upcoming breaking changes: removeDeprecatedGapUtilities\nrisk - We highly recommend opting-in to these changes now to simplify upgrading Tailwind in the future.\nrisk - https://tailwindcss.com/docs/upcoming-changes\n```\n\nYou can opt-in to a breaking change by setting that flag to `true` in your `tailwind.config.js` file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  future: {\n    removeDeprecatedGapUtilities: true,\n  },\n}\n```\n\nIf you'd prefer not to opt-in but would like to silence the warning, explicitly set the flag to `false`:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  future: {\n    removeDeprecatedGapUtilities: false,\n  },\n}\n```\n\n**We do not recommend this**, as it will make upgrading to Tailwind v2.0 more difficult.\n\n### Deprecated gap utilities\n\nAs mentioned previously, Tailwind v1.7.0 introduces new `gap-x-{n}` and `gap-y-{n}` utilities to replace the current `col-gap-{n}` and `row-gap-{n}` utilities.\n\nBy default both classes will exist, but the old utilities will be removed in Tailwind v2.0.\n\nTo migrate to the new class names, simply replace any existing usage of the old names with the new names:\n\n```diff\n- <div class=\"col-gap-4 row-gap-2 ...\">\n+ <div class=\"gap-x-4 gap-y-2 ...\">\n```\n\nTo opt-in to the new names now, enable the `removeDeprecatedGapUtilities` flag in your `tailwind.config.js` file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  future: {\n    removeDeprecatedGapUtilities: true,\n  },\n}\n```\n\n## Experimental features\n\nTailwind v1.7.0 introduces a new experimental feature system that allows you to opt-in to new functionality that is coming to Tailwind soon but isn't quite stable yet.\n\nIt's important to note that **experimental features may introduce breaking changes, do not follow semver, and can change at any time**.\n\nIf you like to live on the wild side though, you can enable all of them like so:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  experimental: 'all',\n}\n```\n\nWith that out of the way, here is some of the fun stuff we're working on that we're pumped you can finally play with...\n\n### Use `@apply` with variants and other complex classes\n\nThis is a huge one — you can finally use `@apply` with responsive variants, pseudo-class variants, and other complex classes!\n\n```css\n.btn {\n  @apply bg-indigo hover:bg-indigo-700 sm:text-lg;\n}\n```\n\nThere are a lot of details to understand with this one, so I recommend [reading the pull request](https://github.com/tailwindlabs/tailwindcss/pull/2159) to learn about how it all works.\n\nThis introduces breaking changes to how `@apply` worked before, so be sure to read all of the details before just flipping the switch.\n\nTo enable this feature, use the `applyComplexClasses` flag:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  experimental: {\n    applyComplexClasses: true,\n  },\n}\n```\n\n### New color palette\n\nWe've added a teaser of the new Tailwind 2.0 color palette that you can start playing with today using the `uniformColorPalette` flag:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  experimental: {\n    uniformColorPalette: true,\n  },\n}\n```\n\nThe idea behind the new palette is that every color at every shade has a similar perceived brightness. So you can swap `indigo-600` with `blue-600` and expect the same color contrast.\n\nWe do expect these colors to continue to change a lot as we iterate on them, so use these at your own risk.\n\n### Extended spacing scale\n\nWe've added a much bigger spacing scale that includes new micro values like `0.5`, `1.5`, `2.5`, and `3.5`, as well as new large values like `72`, `80`, and `96`, _and_ added percentage based fractional values to the whole spacing scale (`1/2`, `5/6`, `7/12`, etc.)\n\nYou can enable the extended spacing scale using the `extendedSpacingScale` flag:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  experimental: {\n    extendedSpacingScale: true,\n  },\n}\n```\n\nThis is pretty stable, I would be surprised if we change this.\n\n### Default line-heights per font-size by default\n\nWe've added recommended default line-heights to every built-in font-size, which can be enabled using the `defaultLineHeights` flag:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  experimental: {\n    defaultLineHeights: true,\n  },\n}\n```\n\nThis is a breaking change and will impact your designs, as previously all font sizes had a default line-height of `1.5`.\n\n### Extended font size scale\n\nWe've added three new font sizes (`7xl`, `8xl`, and `9xl`) to keep up with the latest huge-as-hell-hero-text trends. They include default line-heights as well.\n\nYou can enable them under the `extendedFontSizeScale` flag:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  experimental: {\n    extendedFontSizeScale: true,\n  },\n}\n```\n"
  },
  {
    "path": "src/pages/tailwindcss-1-8/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v1.8.0',\n  description: `Tailwind CSS v1.8.0 is now available, with font-variant-numeric support, experimental dark mode, and more!`,\n  date: '2020-09-04T20:15:00.000Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2315',\n}\n\nTailwind CSS v1.8 is now available with a handful of new utilities, a couple new features, and an exciting new experiment!\n\n<!--more-->\n\n### New features\n\n- New `font-variant-numeric` utilities ([#2305](https://github.com/tailwindlabs/tailwindcss/pull/2305))\n- New `place-items`, `place-content`, `place-self`, `justify-items`, and `justify-self` utilities ([#2306](https://github.com/tailwindlabs/tailwindcss/pull/2306))\n- New `preserveHtmlElements` option for `purge` ([#2283](https://github.com/tailwindlabs/tailwindcss/pull/2283))\n- New `layers` mode for `purge` ([#2288](https://github.com/tailwindlabs/tailwindcss/pull/2288))\n- Support configuring variants as functions ([#2309](https://github.com/tailwindlabs/tailwindcss/pull/2309))\n- Dark mode variant (experimental) ([#2279](https://github.com/tailwindlabs/tailwindcss/pull/2279))\n\n### Changes\n\n- CSS within `@layer` at-rules are now grouped with the corresponding `@tailwind` at-rule ([#2312](https://github.com/tailwindlabs/tailwindcss/pull/2312))\n\n### Deprecations\n\n- The `conservative` purge mode has been deprecated in favor of the new `layers` mode ([#2288](https://github.com/tailwindlabs/tailwindcss/pull/2288))\n\nCheck out the [full release notes on GitHub](https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.8.0) for more details.\n"
  },
  {
    "path": "src/pages/tailwindcss-1-9/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/tailwindcss-1-9/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v1.9.0',\n  description: `We just released Tailwind CSS v1.9 which adds support for configuration presets, useful new CSS grid utilities, extended border radius, rotate, and skew scales, helpful accessibility improvements, and more!`,\n  date: '2020-10-13T18:30:00.000Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2552',\n}\n\nWe just released Tailwind CSS v1.9 which adds support for configuration presets, useful new CSS grid utilities, extended border radius, rotate, and skew scales, helpful accessibility improvements, and more!\n\n<!--more-->\n\nLet's dig in to the highlights...\n\n- [Configuration presets](#configuration-presets)\n- [Utilities for `grid-auto-columns` and `grid-auto-rows`](#utilities-for-grid-auto-columns-and-grid-auto-rows)\n- [Focus indicator improvements and configurable outlines](#focus-indicator-improvements-and-configurable-outlines)\n- [Extended border radius, rotate, and skew scales](#extended-border-radius-rotate-and-skew-scales)\n- [Upgrading to v1.9](#upgrading)\n\nFor the complete summary of changes [check out the release notes on GitHub](https://github.com/tailwindlabs/tailwindcss/releases/tag/v1.9.0).\n\n---\n\n<h2 id=\"configuration-presets\">Configuration presets</h2>\n\nTailwind CSS v1.9 adds a new `presets` key to the `tailwind.config.js` file that makes it possible to configure a custom \"base configuration\" for your projects.\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  presets: [require('@my-company/tailwind-base')],\n  theme: {\n    extend: {\n      // Project specific overrides...\n    },\n  },\n}\n```\n\nWhatever you provide under `presets` _replaces_ the default Tailwind base configuration, so you can define your own totally custom starting point. This is really helpful if you're part of a team that works on multiple different Tailwind projects that all need to share the same brand colors, font customizations, or spacing scale.\n\nYou can even list multiple presets, which are merged together from top to bottom:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  presets: [\n    require('@my-company/tailwind-base'),\n    require('@my-company/tailwind-marketing'),\n  ],\n  theme: {\n    extend: {\n      // Project specific overrides...\n    },\n  },\n}\n```\n\nThe logic to merge your project-specific configuration with your custom base configuration is exactly the same as how things work with the default configuration, so all of the features you're used to like `extend` still work exactly the way you'd expect.\n\n---\n\n<h2 id=\"utilities-for-grid-auto-columns-and-grid-auto-rows\">\n  Utilities for grid-auto-columns and grid-auto-rows\n</h2>\n\nWe've added new `gridAutoColumns` and `gridAutoRows` core plugins that add new utilities for the `grid-auto-columns` and `grid-auto-rows` CSS properties respectively.\n\nThese utilities let you control the size of implicitly-created grid columns and rows. Use them to set a default column/row size whenever you don't specify a number of columns/rows for your grid.\n\n```html\n<div class=\"grid grid-flow-col auto-cols-max\">\n  <div>1</div>\n  <div>2</div>\n  <div>3</div>\n</div>\n```\n\nHere's a list of the new utilities that are included out of the box:\n\n| Class            | CSS                                  |\n| ---------------- | ------------------------------------ |\n| `auto-cols-auto` | `grid-auto-columns: auto;`           |\n| `auto-cols-min`  | `grid-auto-columns: min-content;`    |\n| `auto-cols-max`  | `grid-auto-columns: max-content;`    |\n| `auto-cols-fr`   | `grid-auto-columns: minmax(0, 1fr);` |\n| `auto-rows-auto` | `grid-auto-rows: auto;`              |\n| `auto-rows-min`  | `grid-auto-rows: min-content;`       |\n| `auto-rows-max`  | `grid-auto-rows: max-content;`       |\n| `auto-rows-fr`   | `grid-auto-rows: minmax(0, 1fr);`    |\n\nWe include `responsive` variants for these utilities by default, and they can be configured just like you'd expect under the `gridAutoColumns` and `gridAutoRows` sections of your `tailwind.config.js` file.\n\n---\n\n<h2 id=\"focus-indicator-improvements-and-configurable-outlines\">\n  Focus indicator improvements and configurable outlines\n</h2>\n\nWe've updated the `outline-none` class to render a _transparent_ outline by default instead of rendering _no_ outline. This is really helpful for people who use [Windows high contrast mode](https://blogs.windows.com/msedgedev/2020/09/17/styling-for-windows-high-contrast-with-new-standards-for-forced-colors/), where custom box-shadow-based focus styles are completely invisible.\n\nNow you can create custom focus styles using box shadows _safely_, without making your sites difficult to use for people with low vision.\n\n```html\n<button class=\"... focus:outline-none focus:shadow-outline\">\n  <!-- ... -->\n</button>\n```\n\nWe've also added two new outline styles: `outline-white` and `outline-black`.\n\nThese utilities render a 2px dotted outline in their respective color, with a 2px offset. They work great as general purpose unobtrusive focus indicators that make it easy for keyboard users to see which element on the screen is selected, without clashing too much with your design.\n\nWe've included both `white` and `black` variations so you can always be sure to have an option available that has sufficient contrast against whatever background color you're using.\n\n```html\n<!-- Use `outline-white` on dark backgrounds -->\n<div class=\"bg-gray-900\">\n  <button class=\"... focus:outline-white\">\n    <!-- ... -->\n  </button>\n</div>\n\n<!-- Use `outline-black` on light backgrounds -->\n<div class=\"bg-white\">\n  <button class=\"... focus:outline-black\">\n    <!-- ... -->\n  </button>\n</div>\n```\n\nOf course, you're also free to create whatever custom focus styles you like using background colors, box shadows, borders, whatever. These are great if you don't want to get too fancy though.\n\nWe've made the `outline` property configurable as well, so you can now define custom outlines in your `tailwind.config.js` file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      outline: {\n        blue: '2px solid #0000ff',\n      },\n    },\n  },\n}\n```\n\nYou can also provide an `outline-offset` value for any custom outline utilities using a tuple of the form `[outline, outlineOffset]`:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  theme: {\n    extend: {\n      outline: {\n        blue: ['2px solid #0000ff', '1px'],\n      },\n    },\n  },\n}\n```\n\n---\n\n<h2 id=\"extended-border-radius-rotate-and-skew-scales\">\n  Extended border radius, rotate, and skew scales\n</h2>\n\nWe've added three new border radius utilities by default:\n\n| Class         | Value              |\n| ------------- | ------------------ |\n| `rounded-xl`  | `0.75rem` _(12px)_ |\n| `rounded-2xl` | `1rem` _(16px)_    |\n| `rounded-3xl` | `1.5rem`_(24px)_   |\n\n...and an extended set of smaller values for both the `rotate` and `skew` utilities:\n\n| Class       | Value   |\n| ----------- | ------- |\n| `rotate-1`  | `1deg`  |\n| `rotate-2`  | `2deg`  |\n| `rotate-3`  | `3deg`  |\n| `rotate-6`  | `6deg`  |\n| `rotate-12` | `12deg` |\n| `skew-1`    | `1deg`  |\n| `skew-2`    | `2deg`  |\n\nNegative versions are included for all of these as well. Super handy for more subtle rotate and skew effects!\n\n---\n\n<h2 id=\"upgrading\">Upgrading</h2>\n\nTailwind CSS v1.9 is a non-breaking minor release, so to upgrade all you need to do is run:\n\n```bash\n# npm\nnpm install tailwindcss@^1.9\n\n# yarn\nyarn add tailwindcss@^1.9\n```\n\nWe have promoted two previously experimental features (`defaultLineHeights` and `standardFontWeights`) to `future`, so we also recommend [opting-in to those changes now](https://tailwindcss.com/docs/upcoming-changes#default-line-heights-for-font-size-utilities) to simplify the upgrade to Tailwind CSS v2.0 later this fall.\n"
  },
  {
    "path": "src/pages/tailwindcss-2-1/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/tailwindcss-2-1/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v2.1',\n  description: `We just released Tailwind CSS v2.1 which brings the new JIT engine to core, adds first-class CSS filter support, and more!`,\n  date: '2021-04-05T19:00:00.000Z',\n  authors: [adamwathan],\n  image,\n  footer: `\n    <p>\n      Ready to upgrade?\n      <a href=\"https://npmjs.com/tailwindcss\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Get it from npm →\n      </a>\n    </p>\n  `,\n}\n\nThe first new feature update since Tailwind CSS v2.0 is here and loaded with lots of cool stuff! We've merged the new JIT engine to core, added first-class support for composable CSS filters, added blending mode utilities, and a bunch more.\n\n<!--more-->\n\nHere are some of the highlights:\n\n- [JIT engine in core](#jit-engine-in-core)\n- [Composable CSS filters API](#new-filter-and-backdrop-filter-utilities)\n- [New blending mode utilities](#new-blending-mode-utilities)\n- [New isolation utilities](#new-isolation-utilities)\n\nFor the full details, [check out the release notes](https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.1.0) on GitHub.\n\n---\n\n<a name=\"jit-engine-in-core\"></a>\n\n## JIT engine in core\n\nThe [brand-new JIT engine we announced in March](https://blog.tailwindcss.com/just-in-time-the-next-generation-of-tailwind-css) has now been merged into core, and is available as an opt-in feature using a new `mode` option in your `tailwind.config.js` file:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  mode: 'jit',\n  purge: [\n    // ...\n  ],\n  // ...\n}\n```\n\n**This feature is still in preview** which means some details may change as we iron out the kinks, and it's not subject to semantic versioning.\n\nIf you were using `@tailwindcss/jit` before, you can now migrate to Tailwind CSS v2.1 instead, as that's where all new development on the engine will happen.\n\nRead the [Just-in-Time Mode documentation](https://tailwindcss.com/docs/just-in-time-mode) to learn more.\n\n<a name=\"new-filter-and-backdrop-filter-utilities\"></a>\n\n## Composable CSS filters API\n\nThis is a huge one — we've finally added first-class support for CSS filters!\n\nThey work a lot like our `transform` utilities, where you use `filter` to enable filters, and combine it with utilities like `grayscale`, `blur-lg`, or `saturate-200` to compose filters on the fly.\n\nHere's what `filter` looks like:\n\n```html\n<div class=\"filter blur-md grayscale invert ...\">\n  <!-- ... -->\n</div>\n```\n\n...and here's what `backdrop-filter` looks like:\n\n```html\n<div class=\"backdrop-filter backdrop-blur backdrop-brightness-50 ...\">\n  <!-- ... -->\n</div>\n```\n\nCheck out the [filter](https://tailwindcss.com/docs/filter) and [backdrop-filter](https://tailwindcss.com/docs/backdrop-filter) to learn more. We'll add a bunch of helpful visual examples there soon!\n\n<a name=\"new-blending-mode-utilities\"></a>\n\n## New blending mode utilities\n\nWe've added brand new utilities for `mix-blend-mode` and `background-blend-mode`:\n\n```html\n<div class=\"mix-blend-multiply ...\">\n  <!-- ... -->\n</div>\n```\n\nCheck out [the documentation](https://tailwindcss.com/docs/mix-blend-mode) to learn more.\n\n<a name=\"new-isolation-utilities\"></a>\n\n## New isolation utilities\n\nWe've added new `isolate` and `isolation-auto` utilities for working with the `isolation` property:\n\n```html\n<div class=\"isolate ...\">\n  <!-- ... -->\n</div>\n```\n\nThis can be really helpful for scoping blending mode features or z-index adjustments and is super powerful. Check out [the documentation](https://tailwindcss.com/docs/isolation) to learn more.\n\nI also highly recommend [this great article by Josh Comeau](https://www.joshwcomeau.com/css/stacking-contexts/#airtight-abstractions-with-isolation) to see it in action.\n\n---\n\n## Upgrading\n\nTailwind CSS v2.1 is an incremental upgrade with no breaking changes, so to upgrade you just need to run:\n\n```bash\nnpm install tailwindcss@latest\n```\n\nIf you were previously using `@tailwindcss/jit`, you can now switch back to `tailwindcss` and update your PostCSS configuration accordingly.\n"
  },
  {
    "path": "src/pages/tailwindcss-2-2/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/tailwindcss-2-2/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v2.2',\n  description: `An all-new high-performance CLI tool, ::before and ::after support, sibling selectors, selected text variants, and tons more.`,\n  date: '2021-06-17T14:00:00.000Z',\n  authors: [adamwathan],\n  image,\n  footer: `\n    <p>\n      Ready to upgrade?\n      <a href=\"https://npmjs.com/tailwindcss\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Get it from npm →\n      </a>\n    </p>\n  `,\n}\n\n<!--excerpt-->\n\nWell I can't say we were really planning on it but over the last few weeks we've been having a ton of fun dumping new and exciting features into Tailwind and now feels like the right time to cut a release, so here it is — Tailwind CSS v2.2!\n\nWe've built-in a new high-performance CLI tool, added `::before` and `::after` support, introduced new `peer-*` variants for sibling styling, added variants for styling highlighted text, and tons more.\n\n<!--/excerpt-->\n\nWell I can't say we were really planning on it but over the last few weeks we've been having a ton of fun dumping new and exciting features into Tailwind and now feels like the right time to cut a release, so here it is — Tailwind CSS v2.2!\n\n<div className=\"aspect-w-16 aspect-h-9 my-12\">\n  <iframe\n    src=\"https://www.youtube.com/embed/DxcJbrs6rKk\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\nThis has to be one of the most feature-rich Tailwind releases of all-time. Introducing [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode) back in v2.1 has opened the doors to a lot of cool features we couldn't have easily added otherwise, and this release is loaded with great examples of that.\n\nHere are the highlights:\n\n- [All-new high-performance Tailwind CLI](#all-new-high-performance-tailwind-cli)\n- [Before and after variants](#before-and-after-variants)\n- [First-letter/line variants](#first-letter-line-variants)\n- [Selected text variants](#selected-text-variants)\n- [List marker variants](#list-marker-variants)\n- [Sibling selector variants](#sibling-selector-variants)\n- [Exhaustive pseudo-class support](#exhaustive-pseudo-class-support)\n- [Shorthand color opacity syntax](#shorthand-color-opacity-syntax)\n- [Extended arbitrary value support](#extended-arbitrary-value-support)\n- [Improved nesting support](#improved-nesting-support)\n- [Caret color utilities](#caret-color-utilities)\n- [Background origin utilities](#background-origin-utilities)\n- [Simplified transform and filter composition](#simplified-transform-and-filter-composition)\n- [Per-side border color utilities](#per-side-border-color-utilities)\n- [Built-in safelist, transform, and extract support](#built-in-safelist-transform-and-extract-support)\n\nFor the full details, [check out the release notes](https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.2.0) on GitHub.\n\nIt's important to note that although this is a minor release and there are no breaking changes in the classic engine, **Just-in-Time mode is still in preview and v2.2 introduces a few very small changes that might impact you**, so make sure you read through the [changes and deprecations](https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.2.0#changes-and-deprecations) in the release notes when upgrading.\n\nWhen you're ready to upgrade, just install the latest version from npm and you're off to the races:\n\n```shell\nnpm install -D tailwindcss@latest\n```\n\n---\n\n<a name=\"all-new-high-performance-tailwind-cli\"></a>\n\n## All-new high-performance Tailwind CLI\n\nWe've rewritten the Tailwind CLI tool from the ground-up with a performance-first mindset, while also adding support for a bunch of new features.\n\n```shell\nnpx tailwindcss -o dist/tailwind.css --watch --jit --purge=\"./src/**/*.html\"\n```\n\nHere are some of the highlights:\n\n- **No installation or configuration necessary** — simply `npx tailwindcss -o output.css` to compile Tailwind from anywhere. You can even enable JIT mode with the `--jit` flag and pass in your content files using the `--purge` option, all without creating a config file.\n- **Watch mode** — so you can automatically rebuild your CSS whenever you make any changes.\n- **JIT performance optimizations** — since our CLI is Tailwind-specific we've been able to make tons of optimizations that make it the absolute fastest build tool for compiling your CSS in JIT mode.\n- **Minification support** — now you can minify your CSS with [cssnano](https://cssnano.co/) just by adding the `--minify` flag.\n- **PostCSS plugin support** — the new CLI will read and respect any extra plugins you configure using a `postcss.config.js` file.\n\nIt's fully backwards-compatible with the previous CLI, so if you've got any scripts set up already you should be able to upgrade to v2.2 without making any changes to your scripts.\n\nCheck out our [updated Tailwind CLI documentation](https://tailwindcss.com/docs/installation#using-tailwind-cli) to learn more.\n\n_Note that if you were using the `tailwindcss-cli` wrapper package, you can safely switch to `tailwindcss` as we've managed to resolve the peer-dependency issues that forced us to create the wrapper package in the first place._\n\n<a name=\"before-and-after-pseudo-element-variants\"></a>\n\n## Before and after pseudo-element variants\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nPeople have been asking for this for years and it's finally here! We've added first-party support for styling pseudo-elements like `before` and `after`:\n\n```html\n<div class=\"before:block before:bg-blue-500 after:flex after:bg-pink-300\"></div>\n```\n\nWe set `content: \"\"` automatically any time you use a `before` or `after` variant to make sure the elements are rendered, but you can override it using the new `content` utilities which have full arbitrary value support:\n\n```html\n<div class=\"before:content-['hello'] before:block ...\"></div>\n```\n\nYou can even grab the content from an attribute using the CSS `attr()` function:\n\n```html\n<div\n  before=\"hello world\"\n  class=\"before:content-[attr(before)] before:block ...\"\n></div>\n```\n\nThis can be super helpful when your content has spaces in it, since spaces can't be used in CSS class names.\n\n<a name=\"first-letter-line-variants\"></a>\n\n## First-letter/line variants\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nWe've added variants for the `first-letter` and `first-line` pseudo-elements, so you can do stuff like drop caps:\n\n```html\n<p class=\"first-letter:text-4xl first-letter:font-bold first-letter:float-left\">\n  The night was March 31, 1996, and it was finally time for Bret Hart to face\n  off against Shawn Michaels in the long anticipated Iron Man match — a 60\n  minute war of endurance where the man who scored the most number of falls\n  would walk away as the WWF World Heavyweight Champion.\n</p>\n```\n\n<a name=\"selected-text-variants\"></a>\n\n## Selected text variants\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nWe've added a new `selection` variant that makes it super easy to style highlighted to match your design:\n\n```html\n<p class=\"selection:bg-pink-200\">\n  After nearly a grueling hour of warfare with neither man scoring a fall, Hart\n  locked in the Sharpshooter, his signature submission hold. As Michaels\n  screamed in pain, the crowd were certain that Hart was about to walk away from\n  WrestleMania XII as the still-World Heavyweight Champion.\n</p>\n```\n\nWe've even built this feature in such a way that it can be applied to a parent element and cascade down, so you can set a highlight color for your whole site by applying a utility to the body:\n\n```html\n<body class=\"selection:bg-pink-200\">\n  <!-- ... -->\n  <p>\n    But Michaels didn't give up — he held on until the bell rang and the\n    designated 60 minutes was up. Hart walked away content, thinking that\n    without a clear winner, the title was his to hold. He was not prepared for\n    what would happen next, when Gorilla Monsoon declared the match would\n    continue under sudden death rules.\n  </p>\n</body>\n```\n\n<a name=\"list-marker-variants\"></a>\n\n## List marker variants\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nYou can use the new `marker` variant to style the bullets or numbers at the beginning of a list:\n\n```html\n<h1>WrestleMania XII Results</h1>\n\n<ol class=\"marker:text-gray-500 marker:font-medium\">\n  <li>\n    The British Bulldog, Owen Hart, and Vader defeated Ahmed Johnson, Jake\n    Roberts, and Yokozuna\n  </li>\n  <li>Roddy Piper defeated Goldust</li>\n  <li>Stone Cold Steve Austin defeated Savio Vega</li>\n  <li>The Ultimate Warrior defeated Hunter Hearst Helmsley</li>\n  <li>The Undertaker defeated Diesel</li>\n  <li>Shawn Michaels defeated Bret Hart</li>\n</ol>\n```\n\nLike the `selection` variant, we've implemented this in a way that it cascades from the parent, so you don't have to repeat it for each list item.\n\n<a name=\"sibling-selector-variants\"></a>\n\n## Sibling selector variants\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nTailwind CSS v2.2 adds new `peer-*` variants that behave much like the `group-*` variants, but for targeting sibling elements instead of parent elements.\n\nThis is useful for things like styling an element when a preceding checkbox is checked, doing things like floating labels, and lots more:\n\n```html\n<label>\n  <input type=\"checkbox\" class=\"peer sr-only\">\n  <span class=\"h-4 w-4 bg-gray-200 peer-checked:bg-blue-500\">\n  <!-- ... -->\n</label>\n```\n\nJust like `group` can be combined with any other variant, `peer` can as well, so you have variants like `peer-hover`, `peer-focus`, `peer-disabled`, and loads more at your fingertips.\n\nThe generated CSS uses the [general sibling combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator) and looks like this:\n\n```css\n.peer:checked ~ .peer-checked\\:bg-blue-500 {\n  background-color: #3b82f6;\n}\n```\n\nSo just like in vanilla CSS, it will only work for targeting _previous_ siblings, not siblings that appear later in the DOM.\n\n<a name=\"exhaustive-pseudo-class-support\"></a>\n\n## Exhaustive pseudo-class support\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nWe've added variants for basically every single missing pseudo-class we could think of in this release:\n\n- `only` _(only-child)_\n- `first-of-type`\n- `last-of-type`\n- `only-of-type`\n- `target`\n- `default`\n- `indeterminate`\n- `placeholder-shown`\n- `autofill`\n- `required`\n- `valid`\n- `invalid`\n- `in-range`\n- `out-of-range`\n\nPersonal favorite in the list is `placeholder-shown` — when combined with the new sibling selector variants it makes it possible to do cool stuff like floating labels:\n\n```html\n<div class=\"relative\">\n  <input id=\"name\" class=\"peer ...\">\n  <label for=\"name\" class=\"peer-placeholder-shown:top-4 peer-focus:top-0 ...\">\n</div>\n```\n\n<a name=\"shorthand-color-opacity-syntax\"></a>\n\n## Shorthand color opacity syntax\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nInstead of using utilities like `bg-opacity-50`, `text-opacity-25`, or `placeholder-opacity-40`, Tailwind CSS v2.2 gives you a new color opacity shorthand you can use to tweak the alpha channel of a color directly in the color utility itself:\n\n```diff-html\n- <div class=\"bg-red-500 bg-opacity-25\">\n+ <div class=\"bg-red-500/25\">\n```\n\nThis means you can now change the opacity of colors anywhere in Tailwind, even where we previously didn’t have specific opacity utilities, like in gradients for example:\n\n```html\n<div class=\"bg-gradient-to-r from-red-500/50\"></div>\n```\n\nThe opacity values are taken from your `opacity` scale, but you can also use arbitrary opacity values using square bracket notation:\n\n```html\n<div class=\"bg-red-500/[0.31]\"></div>\n```\n\nIf I'm being honest, I am more excited about never having to create another core plugin like `placeholderOpacity.js` for you people again than I am about actually using the feature. And I'm really excited about the feature, so that says something.\n\n<a name=\"extended-arbitrary-value-support\"></a>\n\n## Extended arbitrary value support\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nWe've gone over every core plugin in Tailwind to try and add the most flexible arbitrary value support we possibly could, and I think we've covered pretty much everything at this point.\n\nYou should be able to whatever arbitrary values you want, just about wherever you want:\n\n```html\n<div class=\"col-start-[73] placeholder-[#aabbcc] object-[50%] ...\"></div>\n```\n\nIf you find one we missed, open an issue and we'll sort it out.\n\nIn addition to making arbitrary value support more comprehensive, we've also added a new type-hint syntax to handle ambiguous situations. For example, if you are using a CSS variable as an arbitrary value, it's not always clear what the generated CSS should be:\n\n```html\n<!-- Is this a font size utility, or a text color utility? -->\n<div class=\"text-[var(--mystery-var)]\"></div>\n```\n\nNow you can provide a hint to the engine by prefixing the arbitrary value with the type name:\n\n```html\n<div class=\"text-[color:var(--mystery-var)]\"></div>\n```\n\nCurrently, the supported types are:\n\n- `length`\n- `color`\n- `angle`\n- `list`\n\nWe'll probably flesh this out even more over time as people discover new edge cases but this should get you very far.\n\n<a name=\"improved-nesting-support\"></a>\n\n## Improved nesting support\n\nSince Tailwind introduces a lot of non-standard CSS at-rules like `@tailwind` and `@apply`, you can often run into weird output when combining it with a PostCSS nesting plugin like `postcss-nested` or `postcss-nesting`.\n\nTo ease the pain here, we've included a new PostCSS plugin in the `tailwindcss` package that acts as a lightweight compatibility layer between existing nesting plugins and Tailwind itself.\n\nSo if you need nesting support in your project, use our plugin, and stick it before Tailwind in your PostCSS plugin list:\n\n```js\n// postcss.config.js\nmodule.exports = {\n  plugins: [\n    // ...\n    require('tailwindcss/nesting'),\n    require('tailwindcss'),\n    // ...\n  ],\n}\n```\n\nBy default, it uses `postcss-nested` under the hood _(since that's what we use to support nesting in Tailwind plugins)_, but if you'd like to use `postcss-nesting` instead, just call our plugin as a function and pass through the `postcss-nesting` plugin:\n\n```js\n// postcss.config.js\nmodule.exports = {\n  plugins: [\n    // ...\n    require('tailwindcss/nesting')(require('postcss-nesting')),\n    require('tailwindcss'),\n    // ...\n  ],\n}\n```\n\nUnder the hood, this uses a new `screen()` function we've introduced that you can use to get the expanded media expression from any of your configured breakpoints:\n\n```css\n/* Input */\n@media screen(sm) {\n  /* ... */\n}\n\n/* Output */\n@media (min-width: 640px) {\n  /* ... */\n}\n```\n\nYou probably won't need to use this yourself but it could be helpful if you're ever integrating Tailwind with another tool that understands `@media` but doesn't handle `@screen` properly.\n\n```diff-css\n- @screen sm { /* ... */ }\n+ @media screen(sm) { /* ... */ }\n```\n\n<a name=\"caret-color-utilities\"></a>\n\n## Caret color utilities\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nYou can now set the color of the cursor in form fields using the new `caret-{color}` utilities:\n\n```html\n<input class=\"caret-red-500\" />\n```\n\nThese are customizable using the `caretColor` key in the `theme` section of your `tailwind.config.js` file.\n\n<a name=\"background-origin-utilities\"></a>\n\n## Background origin utilities\n\nWe've added new utilities for the `background-origin` property, which let you control where an element's background is positioned relative to the element's border, padding box, or content:\n\n```html\n<div\n  class=\"bg-origin-border p-4 border-4 border-dashed ...\"\n  style=\"background-image: url(...)\"\n>\n  Background is rendered under the border\n</div>\n\n<div\n  class=\"bg-origin-padding p-4 border-4 border-dashed ...\"\n  style=\"background-image: url(...)\"\n>\n  Background is rendered within the border but on top of any padding\n</div>\n\n<div\n  class=\"bg-origin-content p-4 border-4 border-dashed ...\"\n  style=\"background-image: url(...)\"\n>\n  Background is rendered within any padding and under the content\n</div>\n```\n\nLearn more in the [background origin documentation](https://tailwindcss.com/docs/background-origin).\n\n<a name=\"simplified-transform-and-filter-composition\"></a>\n\n## Simplified transform and filter composition\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nThe `transform`, `filter`, and `backdrop-filter` classes are no longer necessary to \"enable\" their respective set of composable utilities.\n\n```diff-html\n- <div class=\"transform scale-50 filter grayscale backdrop-filter backdrop-blur-sm\">\n+ <div class=\"scale-50 grayscale backdrop-blur-sm\">\n```\n\nNow those features are automatically enabled any time you use any of the relevant sub-utilities.\n\nIt's important to understand though that because these utilities aren't needed anymore, you can no longer expect transforms and filters to be \"dormant\" by default. If you were relying on conditionally \"activating\" transforms or filters by toggling these classes, you will want to make sure you are toggling the sub-utilities themselves instead:\n\n```diff-html\n- <div class=\"scale-105 -translate-y-1 hover:transform\">\n+ <div class=\"hover:scale-105 hover:-translate-y-1\">\n```\n\nI don't expect this will be a real problem for most people, but it's technically a breaking change which is why we've limited this improvement to the JIT engine only.\n\n<a name=\"per-side-border-color-utilities\"></a>\n\n## Per-side border color utilities\n\n_This feature is only available in [Just-in-Time mode](https://tailwindcss.com/docs/just-in-time-mode)_.\n\nRequested at least once a month for the last four years, I'm excited to share that we've finally added per-side border color support now that we don't have to sweat the development stylesheet size.\n\n```html\n<div\n  class=\"border-2 border-t-blue-500 border-r-pink-500 border-b-green-500 border-l-yellow-500\"\n>\n  <!-- ... -->\n</div>\n```\n\nGo forth and build ugly websites! _(Kidding, kidding, I know they are useful settle the hell down.)_\n\n<a name=\"built-in-safelist-transform-and-extract-support\"></a>\n\n## Built-in safelist, transform, and extract support\n\nWe've added first-class support for a bunch of important PurgeCSS features and made them work in the JIT engine as well, which doesn't actually even use PurgeCSS.\n\nFirst is `safelist`, which is super useful if you need to protect specific classes from being removed from your production CSS, perhaps because they are used in content that comes from a database or similar:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  purge: {\n    content: ['./src/**/*.html'],\n    safelist: [\n      'bg-blue-500',\n      'text-center',\n      'hover:opacity-100',\n      // ...\n      'lg:text-right',\n    ],\n  },\n  // ...\n}\n```\n\n**Note that while the classic engine will accept regular expressions here, the JIT engine will not**. That's because when we're generating classes on demand, the class doesn't exist until it's used so we have nothing to match the expression against. So if you're using just-in-time mode, make sure you're providing complete class names to get the expected result.\n\nNext is `transform`, which lets you transform content for different file extensions before scanning it for potential class names:\n\n```js\n// tailwind.config.js\nlet remark = require('remark')\n\nmodule.exports = {\n  purge: {\n    content: ['./src/**/*.{html,md}'],\n    transform: {\n      md: (content) => {\n        return remark().process(content)\n      },\n    },\n  },\n  // ...\n}\n```\n\nThis is really useful if you have templates that are written in a language that compiles to HTML, like Markdown.\n\nFinally we have `extract`, which lets you customize the logic that Tailwind uses to detect class names in specific file types:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  purge: {\n    content: ['./src/**/*.{html,md}'],\n    extract: {\n      pug: (content) => {\n        return /[^<>\"'`\\s]*/.match(content)\n      },\n    },\n  },\n  // ...\n}\n```\n\nThis is an advanced feature and most users won’t need it. The default extraction logic in Tailwind works extremely well for almost all projects.\n\nFor more information on these features, check out our [optimizing for production documentation](https://tailwindcss.com/docs/optimizing-for-production).\n\n---\n\n## Upgrading\n\nTo upgrade to Tailwind CSS v2.2, install the latest release from npm:\n\n```bash\nnpm install -D tailwindcss@latest\n```\n\nIf you are using the Just-in-Time mode preview, you'll also want to read through the [changes and deprecations](https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.2.0#changes-and-deprecations) in the release notes.\n"
  },
  {
    "path": "src/pages/tailwindcss-from-zero-to-production/index.mdx",
    "content": "import { simonswiss } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: `\"Tailwind CSS: From Zero to Production\" on YouTube`,\n  description: `Today we're excited to release Tailwind CSS: From Zero to Production, a new screencast series that teaches you everything you need to know to go from zero to production with Tailwind CSS v2.0.`,\n  date: '2021-02-16T16:05:00.000Z',\n  authors: [simonswiss],\n  image,\n  footer: `\n    <p>\n      What are you waiting for?\n      <a href=\"https://www.youtube.com/watch?v=elgqxmdVms8&list=PL5f_mz_zU5eXWYDXHUDOLBE0scnuJofO0&index=1\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Start watching →\n      </a>\n    </p>\n  `,\n}\n\nToday we're excited to release [Tailwind CSS: From Zero to Production](https://www.youtube.com/watch?v=elgqxmdVms8&list=PL5f_mz_zU5eXWYDXHUDOLBE0scnuJofO0&index=1), a new screencast series that teaches you everything you need to know to get up and running with Tailwind CSS v2.0 from scratch.\n\n<!--more-->\n\n<div className=\"aspect-w-16 aspect-h-9\">\n  <iframe\n    src=\"https://www.youtube.com/embed/elgqxmdVms8\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\nIt's an eight-part series totaling 1.5 hours of content, and walks you through everything from the initial setup process, to building out a responsive design with utility classes, to customizing your design system, to optimizing for production with PurgeCSS.\n\n1. [Setting Up Tailwind CSS](https://www.youtube.com/watch?v=qYgogv4R8zg)\n2. [The Utility-First Workflow](https://www.youtube.com/watch?v=UvF56fPGVt4)\n3. [Responsive Design](https://www.youtube.com/watch?v=hX1zUdj4Dw4)\n4. [Hover, Focus and Other States](https://www.youtube.com/watch?v=5_BPDve5-3M)\n5. [Composing Utilities with @apply](https://www.youtube.com/watch?v=TrftauE2Vyk)\n6. [Extracting Reusable Components](https://www.youtube.com/watch?v=v-mkUxhaFVA)\n7. [Customizing Your Design System](https://www.youtube.com/watch?v=0l0Gx8gWPHk)\n8. [Optimizing for Production](https://www.youtube.com/watch?v=HZn2LtBT59w)\n\nWatch the series [on YouTube](https://www.youtube.com/watch?v=elgqxmdVms8&list=PL5f_mz_zU5eXWYDXHUDOLBE0scnuJofO0&index=1) and [grab the source code](https://github.com/tailwindlabs/tailwindcss-from-zero-to-production) for each lesson on GitHub.\n"
  },
  {
    "path": "src/pages/tailwindcss-typography/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Introducing Tailwind CSS Typography',\n  description: `Until now, trying to style an article, document, or blog post with Tailwind has been a tedious task that required a keen eye for typography and a lot of complex custom CSS. That's why today we're excited to release @tailwindcss/typography — a plugin that lets you easily style vanilla HTML content with beautiful typographic defaults.`,\n  date: '2020-07-13T16:35:02.037Z',\n  authors: [adamwathan],\n  image,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2021',\n}\n\n<!--excerpt-->\n\nUntil now, trying to style an article, document, or blog post with Tailwind has been a tedious\ntask that required a keen eye for typography and a lot of complex custom CSS.\n\nThat's why today we're excited to release [`@tailwindcss/typography`](https://github.com/tailwindcss/typography) — a plugin that lets you easily style vanilla HTML content with beautiful typographic defaults.\n\n<!--/excerpt-->\n\n<p className=\"lead\">\n  Until now, trying to style an article, document, or blog post with Tailwind has been a tedious\n  task that required a keen eye for typography and a lot of complex custom CSS.\n</p>\n\nBy default, Tailwind removes all of the default browser styling from paragraphs, headings, lists and more. This ends up being really useful for building application UIs because you spend less time undoing user-agent styles, but when you _really are_ just trying to style some content that came from a rich-text editor in a CMS or a markdown file, it can be surprising and unintuitive.\n\nWe get lots of complaints about it actually, with people regularly asking us things like:\n\n> Why is Tailwind removing the default styles on my `h1` elements? How do I disable this? What do you mean I lose all the other base styles too?\n\nWe hear you, but we're not convinced that simply disabling our base styles is what you really want. You don't want to have to remove annoying margins every time you use a `p` element in a piece of your dashboard UI. And I doubt you really want your blog posts to use the user-agent styles either — you want them to look _awesome_, not awful.\n\nThat's why today we're excited to release [`@tailwindcss/typography`](https://github.com/tailwindcss/typography) — a plugin that gives you what you _actually_ want, without any of the downside of doing something stupid like disabling our base styles.\n\nIt adds a new set of `prose` classes that you can slap on any block of vanilla HTML content and turn it into a beautiful, well-formatted document:\n\n```html\n<article class=\"prose lg:prose-xl\">\n  <h1>Garlic bread with cheese: What the science tells us</h1>\n  <p>\n    For years parents have espoused the health benefits of eating garlic bread with cheese to their\n    children, with the food earning such an iconic status in our culture that kids will often dress\n    up as warm, cheesy loaf for Halloween.\n  </p>\n  <p>\n    But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases\n    springing up around the country.\n  </p>\n  <!-- ... -->\n</article>\n```\n\nSo how does it actually look? Well you're looking at it right now — we use it to style the content on this very blog!\n\n[**Check out the documentation**](https://github.com/tailwindcss/typography) to learn more and try it out today.\n"
  },
  {
    "path": "src/pages/tailwindcss-v2/.prettierrc",
    "content": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true,\n  \"semi\": false\n}\n"
  },
  {
    "path": "src/pages/tailwindcss-v2/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport image from './card.jpg'\nimport colorsImage from './announcement-tailwind-colors.jpg'\nimport graysImage from './announcement-tailwind-grays.jpg'\n\nexport const meta = {\n  title: 'Tailwind CSS v2.0',\n  description: `Today we're finally releasing Tailwind CSS v2.0, including an all-new color palette, dark mode support, and tons more!`,\n  date: '2020-11-18T17:45:00.000Z',\n  authors: [adamwathan],\n  image,\n}\n\nAlmost exactly 18 months ago we released Tailwind CSS v1.0, which signalled a commitment to stability while continuing to push the boundaries with exciting new features in every minor release.\n\nOver the course of those 18 months we released nine minor versions that added features like placeholder styling, screenreader visibility, CSS grid, transitions, transforms, animations, layout utilities, integrated tree-shaking, gradients, and tons more.\n\nToday we're finally releasing Tailwind CSS v2.0.\n\n<!--more-->\n\n<div className=\"relative\" style={{ paddingBottom: '56.25%' }}>\n  <iframe\n    className=\"absolute inset-0 h-full w-full\"\n    src=\"https://www.youtube.com/embed/3u_vIdnJYLc\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\nTailwind CSS v2.0 is the first major update ever, including:\n\n- [**All-new color palette**](#all-new-color-palette), featuring 220 total colors and a new workflow for building your own color schemes\n- [**Dark mode support**](#dark-mode), making it easier than ever to dynamically change your design when dark mode is enabled\n- [**Extra wide 2XL breakpoint**](#extra-wide-2-xl-breakpoint), for designing at 1536px and above\n- [**New outline ring utilities**](#new-outline-ring-utilities), which are almost as good as if they would just make `outline-radius` a real thing\n- [**Utility-friendly form styles**](#utility-friendly-form-styles), a new form reset that makes it easy to customize form elements (even checkboxes) with just utility classes\n- [**Default line-heights per font-size**](#default-line-heights-per-font-size), because if we can't make using a 1.5 line-height with a 48px font illegal we should at least make it not the default\n- [**Extended spacing, typography, and opacity scales**](#extended-spacing-typography-and-opacity-scales), for fine-tuning things at the micro level, making an even bigger impact with huge headlines, and for when `opacity-25` wasn't enough and `opacity-50` was too much\n- [**Use @apply with anything**](#use-apply-with-anything), including responsive, hover, focus, and other variants\n- [**New text overflow utilities**](#new-text-overflow-utilities), for controlling things more precisely than you can with `truncate` alone\n- [**Extend variants**](#extend-variants), so you can enable extra variants like `focus-visible` without redeclaring the entire list or thinking about order\n- [**Group-hover and focus-within by default**](#group-hover-and-focus-within-by-default), because you were enabling them on every project anyways\n- [**Default transition duration and easing curve**](#default-transition-duration-and-easing-curve), so you only have to add 17 classes to make a button instead of 19\n- [**Incompatibility with IE11**](#incompatibility-with-ie11), so you can tell the person in charge _\"sorry boss it's out of my hands, blame Tailwind\"_\n\n...and [a bunch of other little things](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md) too.\n\nEven though Tailwind CSS v2.0 is a new major version, **we've worked really hard to minimize significant breaking changes**, especially ones that would force you to edit tons of your templates. We've renamed two classes, removed three that are no longer relevant in modern browsers, and replaced two with more powerful alternatives. Any other breaking changes that might impact you can be remedied with a couple small additions to your `tailwind.config.js` file. Upgrading shouldn't take more than about 30 minutes.\n\n[Check out the upgrade guide](https://tailwindcss.com/docs/upgrading-to-v2) for more details and step-by-step instructions on migrating your project to Tailwind CSS v2.0.\n\nIf you'd like to start a brand new project with v2.0, [head over to our updated installation documentation](https://tailwindcss.com/docs/installation) to get started fast.\n\nAlso how about that [brand new website](https://tailwindcss.com) eh? Hot damn.\n\n---\n\n<h2 id=\"all-new-color-palette\">All-new color palette</h2>\n\nWe've learned a lot about color since the first time we tried to design a general purpose color palette back in the Tailwind CSS v0.1.0 days, and v2.0 represents our best attempt so far.\n\nThe new color palette includes 22 colors _(compared to 10 previously)_ with 10 shades each _(instead of 9_) for a total of 220 values.\n\n<img src={colorsImage} alt=\"New Tailwind CSS color palette\" />\n\nWe've added an extra light `50` shade for every color, so they go from 50–900 now:\n\n```html\n<div class=\"bg-gray-50\">I can't believe it's not white.</div>\n```\n\nThe palette even includes 5 different shades of gray now, so you can choose \"blue gray\" if you want something really cool, or go all the way to \"warm gray\" for something with a lot more brown in it.\n\n<img src={graysImage} alt=\"New Tailwind CSS alternate gray\" />\n\nWe configure a well-balanced 8-color palette for you out of the box, but the complete color palette lives in a new `tailwindcss/colors` module that you can import at the top of your config file to curate your own custom palette however you like:\n\n```js\n// tailwind.config.js\nconst colors = require('tailwindcss/colors')\n\nmodule.exports = {\n  theme: {\n    colors: {\n      gray: colors.trueGray,\n      indigo: colors.indigo,\n      red: colors.rose,\n      yellow: colors.amber,\n    },\n  },\n}\n```\n\nLearn more in the new [customizing colors documentation](https://tailwindcss.com/docs/customizing-colors).\n\n---\n\n<h2 id=\"dark-mode\">Dark mode</h2>\n\nEver since iOS added native dark mode all you dark mode nerds haven't been able to leave me alone about adding it to Tailwind. Well you did it, it's here, you win.\n\nOpen up your `tailwind.config.js` file and flip `darkMode` to `media`:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  darkMode: 'media',\n  // ...\n}\n```\n\nBoom — now just add `dark:` to the beginning of a class like `bg-black` and it'll only take effect when dark mode is enabled:\n\n```html\n<div class=\"bg-white dark:bg-black\">\n  <h1 class=\"text-gray-900 dark:text-white\">Dark mode</h1>\n  <p class=\"text-gray-500 dark:text-gray-300\">\n    The feature you've all been waiting for.\n  </p>\n</div>\n```\n\nWorks with hover and stuff too:\n\n```html\n<button\n  class=\"bg-gray-900 hover:bg-gray-800 dark:bg-white dark:hover:bg-gray-50\"\n>\n  <!-- ... -->\n</button>\n```\n\nAnd responsive stuff:\n\n```html\n<div class=\"... lg:bg-white lg:dark:bg-black\">\n  <!-- ... -->\n</div>\n```\n\nAnd responsive hover stuff:\n\n```html\n<button class=\"... lg:dark:bg-white lg:dark:hover:bg-gray-50\">\n  <!-- ... -->\n</button>\n```\n\nCheck out the [dark mode docs](https://tailwindcss.com/docs/dark-mode) for all the gory details.\n\n---\n\n<h2 id=\"extra-wide-2-xl-breakpoint\">Extra wide 2XL breakpoint</h2>\n\nI'm pretty sure they make an iPhone that is 1280px wide now, so it's time to step it up.\n\nWe've added a new `2xl` breakpoint out-of-the-box that lets you target things at 1536px and above:\n\n```html\n<h1 class=\"... 2xl:text-9xl\">Godzilla</h1>\n```\n\nExciting I know but also let's be serious [you've been able to add this yourself](https://tailwindcss.com/docs/responsive-design#customizing-breakpoints) for like three years. Now it's blessed though, I get it.\n\n---\n\n<h2 id=\"new-outline-ring-utilities\">New outline ring utilities</h2>\n\nYou know how the `outline` property ignores border radius and pretty much just always looks bad? The `ring` utilities are our attempt to will a better solution into existence through blood, sweat, and tears.\n\nThey work a lot like the `border` utilities, except they add a solid box-shadow rather than a border so they don't impact the layout:\n\n```html\n<button\n  class=\"... focus:outline-none focus:ring-2 focus:ring-blue-300 focus:ring-opacity-50\"\n>\n  <!-- ... -->\n</button>\n```\n\nYou can even offset them to create a sort of halo effect with `ring-offset-{width}` utilities:\n\n```html\n<button\n  class=\"... focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-300 focus:ring-opacity-50\"\n>\n  <!-- ... -->\n</button>\n```\n\nUsing a bunch of CSS custom property voodoo we've even made them automatically combine with regular box-shadows, too:\n\n```html\n<button class=\"shadow-sm focus:ring-2 ...\">\n  <!-- Both the shadow and ring will render together -->\n</button>\n```\n\nThe [ring width documentation](https://tailwindcss.com/docs/ring-width) is the best starting point for learning these new APIs. They seriously turned out so cool, more useful than you probably think.\n\n---\n\n<h2 id=\"utility-friendly-form-styles\">Utility-friendly form styles</h2>\n\nOne thing I am constantly surprised by is how few people complain about how unbelievably useless form elements are out of the box with Tailwind. They literally look awful and you can't do anything about it without writing custom CSS full of weird background-image SVG tricks and worrying about obscure edge cases that require CSS properties you've never heard of before like `color-adjust`.\n\nI tried to solve this a while back with the [@tailwindcss/custom-forms](https://github.com/tailwindlabs/tailwindcss-custom-forms) plugin, but something about adding a bunch of classes like `form-input` and `form-checkbox` just didn't feel quite right so we didn't really promote it and didn't even link to it from the Tailwind documentation. This time though I think we figured it out.\n\nAlongside Tailwind CSS v2.0, we're releasing a brand new official plugin called `@tailwindcss/forms` that normalizes and resets all of the basic form controls across browsers to a state that is super easy to style with pure utility classes:\n\n```html\n<!-- This will be a nice rounded checkbox with an indigo focus ring and an indigo checked state -->\n<input\n  type=\"checkbox\"\n  class=\"h-4 w-4 rounded border-gray-300 focus:border-indigo-300 focus:ring-2 focus:ring-indigo-200 focus:ring-opacity-50 text-indigo-500\"\n/>\n```\n\nIt's not included out of the box but you can add it to your `tailwind.config.js` file with a single line:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  plugins: [require('@tailwindcss/forms')],\n}\n```\n\nCheck out [the @tailwindcss/forms documentation](https://github.com/tailwindlabs/tailwindcss-forms) for more information.\n\n---\n\n<h2 id=\"default-line-heights-per-font-size\">\n  Default line-heights per font-size\n</h2>\n\nEvery font-size utility in Tailwind now comes paired with a sensible default line-height:\n\n```js\n// Tailwind's default theme\nmodule.exports = {\n  theme: {\n    // ...\n    fontSize: {\n      xs: ['0.75rem', { lineHeight: '1rem' }],\n      sm: ['0.875rem', { lineHeight: '1.25rem' }],\n      base: ['1rem', { lineHeight: '1.5rem' }],\n      lg: ['1.125rem', { lineHeight: '1.75rem' }],\n      xl: ['1.25rem', { lineHeight: '1.75rem' }],\n      '2xl': ['1.5rem', { lineHeight: '2rem' }],\n      '3xl': ['1.875rem', { lineHeight: '2.25rem' }],\n      '4xl': ['2.25rem', { lineHeight: '2.5rem' }],\n      '5xl': ['3rem', { lineHeight: '1' }],\n      '6xl': ['3.75rem', { lineHeight: '1' }],\n      '7xl': ['4.5rem', { lineHeight: '1' }],\n      '8xl': ['6rem', { lineHeight: '1' }],\n      '9xl': ['8rem', { lineHeight: '1' }],\n    },\n  },\n}\n```\n\nSo now when you add a utility like `text-xl`, the corresponding default line-height (`1.75rem` in this case) is added automatically:\n\n```html\n<p class=\"text-xl\">This will have a line-height of 1.75rem automatically.</p>\n```\n\nIf you want to override this, you can still do it by layering on a `leading` utility:\n\n```html\n<p class=\"text-3xl leading-normal\">Come on don't do this to me.</p>\n```\n\nCheck out the [font size documentation](https://tailwindcss.com/docs/font-size#providing-a-default-line-height) for some additional details.\n\n---\n\n<h2 id=\"extended-spacing-typography-and-opacity-scales\">\n  Extended spacing, typography, and opacity scales\n</h2>\n\nWe've extended the default spacing scale to include a bunch of micro values like `0.5`, `1.5`, `2.5`, and `3.5`:\n\n```html\n<span class=\"ml-0.5\">Just a little nudge.</span>\n```\n\n...as well as a bunch of new values at the top end as well like `72`, `80`, and `96`:\n\n```html\n<div class=\"p-96\">This is too much padding.</div>\n```\n\nWe've also extended the `inset` (that's top/right/bottom/left for you dinosaurs) and `translate` plugins to include the full spacing scale, so now you can do things like this:\n\n```html\n<div class=\"top-8\">\n  <!-- .... -->\n</div>\n```\n\nWe've extended the default typography scale with new `7xl`, `8xl`, and `9xl` values:\n\n```html\n<h1 class=\"text-9xl font-bold\">What is this, an Apple website?</h1>\n```\n\nAnd we've also extended the default opacity scale with steps of 10, as well as 5 and 95 values:\n\n```html\n<figure class=\"opacity-5\">\n  <blockquote>You can't see me.</blockquote>\n  <figcaption>John Cena</figcaption>\n</figure>\n```\n\nPeruse the entire [default config file](https://github.com/tailwindlabs/tailwindcss/blob/master/stubs/defaultConfig.stub.js) to see exactly what's available.\n\n---\n\n<h2 id=\"use-apply-with-anything\">Use @apply with anything</h2>\n\nBy far the most common question I've gotten over the years is \"why doesn't `@apply hover:bg-black` work?\"\n\nAnd it's a fair question, it's stupid that it ~~doesn't~~ didn't work.\n\nIt took some serious engineering muscle but we figured it out — now you can `@apply` literally anything:\n\n```css\n.btn {\n  @apply bg-indigo-500 hover:bg-indigo-600 focus:ring-2 focus:ring-indigo-200 focus:ring-opacity-50;\n}\n```\n\nCheck out [the updated @apply documentation](https://tailwindcss.com/docs/functions-and-directives#apply) to learn more.\n\n---\n\n<h2 id=\"new-text-overflow-utilities\">New text overflow utilities</h2>\n\nUp until v2.0, if you wanted to control overflowing text all we really gave you was the fairly heavy-handed `truncate` utility.\n\nNow we've added dedicated `overflow-ellipsis` and `overflow-clip` utilities to control _just_ the `text-overflow` property, in case you wanted to add ellipsis to overflowing text without restricting that text to one line.\n\n```html\n<p class=\"overflow-ellipsis overflow-hidden\">\n  Look ma no whitespace-nowrap ipsum...\n</p>\n```\n\nCheck out the new [text overflow documentation](https://tailwindcss.com/docs/text-overflow) to see it in action.\n\n---\n\n<h2 id=\"extend-variants\">Extend variants</h2>\n\nYou know what sucks? Wanting to enable `focus-visible` for `backgroundColor` but having to list _every single default variant_ just to add one extra one:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  variants: {\n    backgroundColor: ['responsive', 'hover', 'focus', 'focus-visible'],\n  },\n}\n```\n\nYou know what's better? Just adding the one you want to enable:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  variants: {\n    extend: {\n      backgroundColor: ['focus-visible'],\n    },\n  },\n}\n```\n\n[Giddy up](https://tailwindcss.com/docs/configuring-variants#enabling-extra-variants).\n\n---\n\n<h2 id=\"group-hover-and-focus-within-by-default\">\n  Group-hover and focus-within by default\n</h2>\n\nOne of the things we learned working on [Tailwind UI](https://tailwindui.com/components) is that `group-hover` and `focus-within` aren't nice-to-haves, they're must-haves.\n\nAnywhere where we enabled hover or focus by default previously now has `group-hover` and `focus-within` enabled by default too:\n\n```html\n<div class=\"group ...\">\n  <span class=\"group-hover:text-blue-600 ...\">Da ba dee da ba daa</span>\n</div>\n```\n\nCheck out [the default variants reference](https://tailwindcss.com/docs/configuring-variants#default-variants-reference) for a full list of what's enabled where in v2.0.\n\n---\n\n<h2 id=\"default-transition-duration-and-easing-curve\">\n  Default transition duration and easing curve\n</h2>\n\nUntil now, any time you wanted to add a transition in Tailwind you typically needed to add three classes:\n\n```html\n<button class=\"... transition duration-150 ease-in-out\">Count them</button>\n```\n\nIn v2.0, we've made it possible to specify a default duration and timing function that is used automatically any time any `transitionProperty` utility is added:\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  // ...\n  theme: {\n    // ...\n    transitionDuration: {\n      DEFAULT: '150ms',\n      // ...\n    },\n    transitionTimingFunction: {\n      DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',\n      // ...\n    },\n  },\n}\n```\n\nSo now you only need to write a single class if you have a common duration and timing function that you use really frequently:\n\n```html\n<button class=\"... transition\">Count them again</button>\n```\n\nOf course you can override this by layering on separate duration or timing function utilities:\n\n```html\n<button class=\"... transition duration-300 ease-out\">We're back baby</button>\n```\n\nLearn more about transitions in the [transition property documentation](https://tailwindcss.com/docs/transition-property).\n\n---\n\n<h2 id=\"incompatibility-with-ie11\">Incompatibility with IE11</h2>\n\nWe've decided to unburden ourselves with caring about IE11 at all, which has allowed us to fully embrace CSS custom properties for [all sorts of crazy stuff](https://adamwathan.me/composing-the-uncomposable-with-css-variables/) and is what makes things like the new `ring` utilities even possible.\n\nDropping IE11 support means smaller builds even when using PurgeCSS, because we don't have to ship any CSS variable fallbacks which adds up more than you'd expect.\n\nCheers to Bootstrap for having the cojones to [do this first](https://github.com/twbs/bootstrap/pull/30377) — I don't think we would have been so bold if they hadn't decided to pave the way.\n\nThe good news is that if you need to support IE11, you can always use Tailwind CSS v1.9 which is still an amazingly productive framework.\n\n---\n\nSo there you have it folks, that's Tailwind CSS v2.0 in a _(pretty big)_ nutshell!\n\nWhat are you waiting for? [Go build something awesome](https://tailwindcss.com).\n"
  },
  {
    "path": "src/pages/utility-friendly-transitions-with-tailwindui-react/index.mdx",
    "content": "import { robinmalfait } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: 'Utility-Friendly Transitions with @tailwindui/react',\n  description: `Announcing the first release of @tailwindui/react, which introduces a <Transition> component for utility-first enter/leave transitions.`,\n  date: '2020-08-27T18:30:00.000Z',\n  authors: [robinmalfait],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2262',\n}\n\nBack in February we released [Tailwind UI](https://tailwindui.com), a directory of HTML component examples designed for you to copy and paste into your Tailwind projects as a starting point for your own designs.\n\nWe built Tailwind UI as an HTML-only, bring-your-own-JS product to make it as universal as possible, but many designs are inherently interactive and porting those interactive behaviors between JavaScript frameworks is unfortunately not always very easy.\n\nOne example of this is enter/leave transitions, like when you toggle a dropdown menu and see it fade in and out.\n\n<!--more-->\n\nVue.js has a really neat `<transition>` component for enter/leave transitions with a very utility-friendly API:\n\n```html\n<transition\n  enter-active-class=\"transition-opacity duration-75\"\n  enter-from-class=\"opacity-0\"\n  enter-to-class=\"opacity-100\"\n  leave-active-class=\"transition-opacity duration-150\"\n  leave-from-class=\"opacity-100\"\n  leave-to-class=\"opacity-0\"\n>\n  <div v-show=\"isShowing\">\n    <!-- Will fade in and out -->\n  </div>\n</transition>\n```\n\nBut replicating this in React turns out to be much more difficult, because until now there hasn't been a library designed to support utility-driven transition styling.\n\n**So earlier this week, we released the very first version of [@tailwindui/react](https://github.com/tailwindlabs/tailwindui-react), a library that provides low-level primitives for turning utility-first HTML into fully interactive UIs.**\n\nWe'll be adding many more components in the coming months _(like dropdowns, toggles, modals, and more, and for Vue too!)_ but thought we'd start with a `<Transition>` component to at least get the current Tailwind UI experience for React users up to par with what's possible in Vue and Alpine.js.\n\nHere's what it looks like to use:\n\n```jsx\nimport { Transition } from '@tailwindui/react'\nimport { useState } from 'react'\n\nfunction MyComponent() {\n  const [isOpen, setIsOpen] = useState(false)\n\n  return (\n    <div>\n      <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>\n      <Transition\n        show={isOpen}\n        enter=\"transition-opacity duration-75\"\n        enterFrom=\"opacity-0\"\n        enterTo=\"opacity-100\"\n        leave=\"transition-opacity duration-150\"\n        leaveFrom=\"opacity-100\"\n        leaveTo=\"opacity-0\"\n      >\n        {/* Will fade in and out */}\n      </Transition>\n    </div>\n  )\n}\n```\n\n[Read the documentation](https://github.com/tailwindlabs/tailwindui-react) to learn more about advanced functionality, like:\n\n- Rendering without an extra DOM element\n- Co-ordinating related transitions\n- Transitioning on initial mount.\n\nCheck it out in action in this CodeSandbox demo:\n\n<iframe\n  src=\"https://codesandbox.io/embed/dreamy-villani-1lz49?fontsize=14&hidenavigation=1&module=%2Fsrc%2FApp.js&view=preview\"\n  style={{ height: 500 }}\n  className=\"w-full rounded-md overflow-hidden\"\n  title=\"dreamy-villani-1lz49\"\n  allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n  sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n/>\n\nTry it out on your projects, and if you run into any problems, [report an issue on GitHub](https://github.com/tailwindlabs/tailwindui-react).\n"
  },
  {
    "path": "src/pages/welcoming-brad-cornes-to-the-tailwind-team/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\nimport intellisenseImage from '../introducing-linting-for-tailwindcss-intellisense/card.jpg'\n\nexport const meta = {\n  title: 'Welcoming Brad Cornes to the Team',\n  description: `Back in June, Brad Cornes joined our company as our very first team member. We didn't have a blog to announce it back then, but better late than never right?`,\n  date: '2020-07-18T15:43:02.988Z',\n  authors: [adamwathan],\n  image: card,\n  discussion: 'https://github.com/tailwindcss/tailwindcss/discussions/2040',\n}\n\nBack in June, [Brad Cornes](https://twitter.com/bradlc) joined our company as our very first team member. We didn't have a blog to announce it back then, but better late than never right?\n\nYou might know Brad as the creator of the amazing [Tailwind CSS IntelliSense plugin](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) for VS Code, which he first released way back in 2018 and has since been installed over 100,000 times!\n\n<!--more-->\n\n<img src={intellisenseImage} />\n\nBrad has been using Tailwind since it was first released, and I got to know Brad in the really early days of building the Tailwind community. I was immediately impressed by his willingness to tackle extremely hard problems, his ability to come up with out-of-the-box creative solutions, and his propensity for diving deep into bleeding-edge technologies and finding interesting use-cases for them.\n\nWhen we decided we wanted to grow the team, Brad was the first person that came to mind and I reached out to him about the idea way back in March. He's been with us for over a month now and it has been absolutely awesome working with him and benefitting from his extensive experience and expertise.\n\nBrad has been helping us out all over the place, working on things like the IntelliSense plugin, developing internal tooling for Tailwind UI, and even building this very blog. He has an amazing ability to take a hard, complex problem, lock himself in a room for 2 hours, and come out the other side with an elegant solution.\n\nHe's been an amazing addition to the team, and we are extremely excited about all the new things we're going to be building in the coming months that would not be possible without his help.\n\nFollow Brad [on Twitter](https://twitter.com/bradlc) to keep up with what he's working on and for sneak peaks of some exciting new projects!\n"
  },
  {
    "path": "src/pages/welcoming-david-luhr-to-tailwind-labs/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\n\nexport const meta = {\n  title: 'Welcoming David Luhr to Tailwind Labs',\n  description: `We started working with David Luhr last summer to help us develop a Figma version of Tailwind UI, as well as to leverage his accessibility expertise, ensuring we were following best practices and delivering markup that would work for everyone. Today we're excited to share that David has joined the team full-time!`,\n  date: '2021-02-01T13:35:00.0Z',\n  authors: [adamwathan],\n  image: card,\n  footer: `\n    <p>\n      Want to keep up with David's work?\n      <a href=\"https://twitter.com/david_luhr\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Follow David on Twitter →\n      </a>\n    </p>\n  `,\n}\n\nWe started working with [David Luhr](https://twitter.com/david_luhr) last summer on a project-by-project basis to help us develop a Figma version of [Tailwind UI](https://tailwindui.com) _(almost ready!)_, as well as to leverage his accessibility expertise when building Tailwind UI templates, ensuring we were following best practices and delivering markup that would work for everyone, no matter what tools they use to browse the web.\n\nToday we're excited to share that David has joined the team full-time!\n\n<!--more-->\n\n<figure>\n  <div className=\"aspect-w-16 aspect-h-9\">\n    <iframe\n      src=\"https://www.youtube.com/embed/djg8WoRGrq0\"\n      frameBorder=\"0\"\n      allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n      allowFullScreen\n    ></iframe>\n  </div>\n  <figcaption className=\"italic text-center\">\n    Watch David build some Tailwind UI components in Figma live on his YouTube channel!\n  </figcaption>\n</figure>\n\nDavid is an accessibility expert, a world-class front-end developer, a gifted educator, and a compassionate leader. He's performed black magic with Tailwind UI in Figma that Steve and I didn't even know was possible, and he's been doing an incredible job turning Steve's Tailwind UI designs into pixel-perfect HTML and CSS that works for everyone.\n\nSay what you will about HTML being easy to learn, it's a bear to master. David knows things about the spec that I've never encountered in 20 years of building things for the web, and has so much built up knowledge from his own real-world device testing that it would take years of dedicated focus to even come close to writing markup as bullet-proof as what David produces by default.\n\nAs a company that specializes in helping developers build better interfaces faster, it's our responsibility to make sure that the tools we build follow accessibility best practices by default, and we couldn't be more grateful to have David on the team to help us live up to that responsibility.\n\nOne of my favorite problems David has helped me solve is developing the new [ring utilities](https://tailwindcss.com/docs/ring-offset-width) in Tailwind CSS v2.0. When David first started auditing our work in Tailwind UI for accessibility improvements, he mentioned that some of our focus styles were not obvious enough. It turns out that just changing a button's _color_ for example isn't good enough — it's important that something _new_ is drawn to the screen (like a focus ring) that is really easy for anyone to see.\n\nTrying to come up with a way to solve this was hard. It needed to look good, needed to be straightforward to do with utility classes, and needed to be possible to actually implement in Tailwind internally. David suggested we study the interfaces of things like video games consoles or the Apple TV for inspiration since you can't interact with them with a mouse, and that's where we landed on trying to build some sort of customizable offset focus ring.\n\nComing up with an implementation for this was hard because it needed to be implemented with a box shadow, and we had to somehow make it composable with the existing box shadow API. There were many moments where I got frustrated and I might have even given up on it if I was working on it alone, but ultimately we figured it out and now it's one of my favorite features in the framework.\n\nDavid will be leading component and design asset development on Tailwind UI, and providing accessibility guidance on our other projects like Tailwind CSS and Headless UI. It's been an amazing experience working with him over the last 6 months and we are so excited to have him on the team full-time.\n"
  },
  {
    "path": "src/pages/welcoming-james-mcdonald-to-tailwind-labs/index.mdx",
    "content": "import { adamwathan } from '@/authors'\nimport card from './card.jpg'\nimport jamesWorkExamples from './james-work.jpg'\nimport tailwindUiExamples from './tailwindui-work.jpg'\n\nexport const meta = {\n  title: 'Welcoming James McDonald to Tailwind Labs',\n  description: `Many years ago I got a message from Steve that said something like: \"Have I ever shared this guy's Dribbble profile with you before? Been following him forever, some of my absolute favorite work I've ever found.\" That person was James McDonald, and today we're totally over the moon to share that James is joining our team full-time.`,\n  date: '2021-03-08T19:00:00.0Z',\n  authors: [adamwathan],\n  image: card,\n  footer: `\n    <p>\n      Want to keep up with James' work?\n      <a href=\"https://twitter.com/jamesm\" class=\"font-medium text-teal-600 hover:text-teal-700\">\n        Follow James on Twitter →\n      </a>\n    </p>\n  `,\n}\n\nMany years ago I got a message from [Steve](https://twitter.com/steveschoger) that said something like:\n\n> Have I ever shared this guy's Dribbble profile with you before? Been following him forever, some of my absolute favorite work I've ever found.\n\nThat person was [James McDonald](https://dribbble.com/james), and today we're totally over the moon to share that James is joining our team full-time.\n\n<!--more-->\n\n<figure>\n  <img src={jamesWorkExamples} alt=\"Examples of James' previous work\" />\n  <figcaption className=\"italic text-center\">\n    Some of James' awesome work from his <a href=\"https://dribbble.com/james\">Dribbble profile</a>.\n  </figcaption>\n</figure>\n\nJames is an incredibly talented UI designer with an amazing eye for tiny details _(they call him [the shadow king](https://dribbble.com/shots/3985645-Newsletter-Sign-Up))_ who is constantly pushing the industry forward and setting new trends while somehow never designing anything that feels trapped in a certain time period. He's also a fantastic [icon designer](https://dribbble.com/shots/14568974-)!\n\nJames has been a good friend of ours for a few years now, and we've worked with him on a few different projects over that time, including a bunch of awesome designs he put together for us for <a href=\"https://tailwindui.com/\">Tailwind UI</a> when we wanted to add some fresh perspective last year.\n\n<figure>\n  <img src={tailwindUiExamples} alt=\"Designs James created for Tailwind UI\" />\n  <figcaption className=\"italic text-center\">\n    Examples James designed for us for Tailwind UI.\n  </figcaption>\n</figure>\n\nWe've been such huge fans of James' work for so long that it's honestly a dream come true to have the chance to work with him on what we're doing with Tailwind CSS, Tailwind UI, Heroicons, and more.\n\nCan't wait to share some of the work we'll be creating together in the months to come!\n"
  },
  {
    "path": "src/pages/whats-new-in-tailwindcss-on-youtube/index.mdx",
    "content": "import { simonswiss } from '@/authors'\nimport image from './card.jpg'\n\nexport const meta = {\n  title: `\"What's new in Tailwind CSS?\" on YouTube`,\n  description: `A lot of cool stuff has been added to Tailwind since the last time we published any screencasts, so we thought it would be a great idea to record a little series that covers all of the new additions.`,\n  date: '2020-09-23T18:30:00.000Z',\n  authors: [simonswiss],\n  image,\n  discussion: 'https://github.com/tailwindlabs/tailwindcss/discussions/2441',\n}\n\n<!--excerpt-->\n\nA lot of cool stuff has been added to Tailwind since the last time we published any screencasts, so we thought it would be a great idea to record a little series that covers all of the new additions.\n\n[\"What's new in Tailwind CSS?\"](https://www.youtube.com/watch?v=b-hrxkgkG-s&list=PL5f_mz_zU5eV0_7udNKr3qffGCkJ4Avb_) is a series of 12 short videos that teach you everything you need to know about some of our favorite new Tailwind features.\n\n<!--/excerpt-->\n\nA lot of cool stuff has been added to Tailwind since the last time we published any screencasts, so we thought it would be a great idea to record a little series that covers all of the new additions.\n\n<div className=\"aspect-w-16 aspect-h-9\">\n  <iframe\n    src=\"https://www.youtube.com/embed/b-hrxkgkG-s?list=PL5f_mz_zU5eV0_7udNKr3qffGCkJ4Avb_\"\n    frameBorder=\"0\"\n    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n    allowFullScreen\n  ></iframe>\n</div>\n\n[\"What's new in Tailwind CSS?\"](https://www.youtube.com/watch?v=b-hrxkgkG-s&list=PL5f_mz_zU5eV0_7udNKr3qffGCkJ4Avb_) is a series of 12 short videos that teach you everything you need to know about some of our favorite new Tailwind features, including:\n\n- [CSS Grid](https://youtu.be/b-hrxkgkG-s)\n- [Transitions](https://youtu.be/AYoQfPX31Mg)\n- [Transforms](https://youtu.be/fZl6ufxmAqI)\n- [Space-between utilities](https://youtu.be/wTRTC4JNSGs)\n- [Divide utilities](https://youtu.be/XVmbVtO3tUU)\n- [Color opacity](https://youtu.be/Zo1JtXnLDdA)\n- [Built-in PurgeCSS support](https://youtu.be/ZNLeQDpE_8M)\n- [Animations](https://youtu.be/75_a2lTJSjA)\n- [Reduced motion variants](https://youtu.be/vnK7UrZ-IFs)\n- [Gradients](https://youtu.be/1Rs5Kml8qMM)\n- [Using `@apply` with complex classes](https://youtu.be/naJ_rIK6ppQ)\n\n...with more to come soon! You can check out the whole series on our brand new [YouTube channel](https://www.youtube.com/channel/UCOe-8z68tgw9ioqVvYM4ddQ/).\n\nAnd don't forget to like and subscribe.\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "const defaultTheme = require('tailwindcss/defaultTheme')\nconst colors = require('tailwindcss/colors')\nconst mdx = require('@mdx-js/mdx')\n\nmodule.exports = {\n  mode: 'jit',\n  purge: {\n    content: ['./src/**/*.{js,mdx}', './next.config.js'],\n    transform: {\n      mdx: mdx.sync,\n    },\n  },\n  theme: {\n    extend: {\n      fontFamily: {\n        sans: ['Inter var', ...defaultTheme.fontFamily.sans],\n      },\n      colors: {\n        teal: colors.cyan,\n\n        // for syntax highlighting\n        fuchsia: colors.fuchsia,\n        lime: colors.lime,\n        sky: colors.sky,\n        rose: colors.rose,\n        emerald: colors.emerald,\n      },\n      typography: (theme) => ({\n        DEFAULT: {\n          css: {\n            color: theme('colors.gray.700'),\n            h2: {\n              fontWeight: '700',\n              letterSpacing: theme('letterSpacing.tight'),\n              color: theme('colors.gray.900'),\n            },\n            h3: {\n              fontWeight: '600',\n              color: theme('colors.gray.900'),\n            },\n            'ol li:before': {\n              fontWeight: '600',\n              color: theme('colors.gray.500'),\n            },\n            'ul li:before': {\n              backgroundColor: theme('colors.gray.400'),\n            },\n            code: {\n              color: theme('colors.gray.900'),\n            },\n            a: {\n              color: theme('colors.gray.900'),\n              fontWeight: 400,\n            },\n            pre: {\n              color: theme('colors.white'),\n              backgroundColor: theme('colors.gray.800'),\n            },\n            blockquote: {\n              color: theme('colors.gray.900'),\n              borderLeftColor: theme('colors.gray.200'),\n            },\n          },\n        },\n      }),\n    },\n  },\n  plugins: [\n    require('@tailwindcss/aspect-ratio'),\n    require('@tailwindcss/typography'),\n    function ({ addBase }) {\n      addBase([\n        {\n          '@font-face': {\n            fontFamily: 'Inter var',\n            fontWeight: '100 900',\n            fontStyle: 'normal',\n            fontNamedInstance: 'Regular',\n            fontDisplay: 'swap',\n            src: 'url(\"/fonts/Inter-roman.var-latin.woff2?3.13\") format(\"woff2\")',\n          },\n        },\n        {\n          '@font-face': {\n            fontFamily: 'Inter var',\n            fontWeight: '100 900',\n            fontStyle: 'italic',\n            fontNamedInstance: 'Italic',\n            fontDisplay: 'swap',\n            src: 'url(\"/fonts/Inter-italic.var-latin.woff2?3.13\") format(\"woff2\")',\n          },\n        },\n      ])\n    },\n  ],\n}\n"
  }
]