Repository: tailwindlabs/blog.tailwindcss.com Branch: master Commit: 2b6144fa1553 Files: 74 Total size: 256.9 KB Directory structure: gitextract_xwaefze3/ ├── .gitignore ├── README.md ├── jsconfig.json ├── next.config.js ├── package.json ├── postcss.config.js ├── prettier.config.js ├── public/ │ ├── browserconfig.xml │ └── site.webmanifest ├── remark/ │ ├── vendor/ │ │ └── prism-diff-highlight.js │ ├── withProse.js │ └── withSyntaxHighlighting.js ├── scripts/ │ └── build-rss.js ├── src/ │ ├── authors.js │ ├── components/ │ │ ├── Header.js │ │ ├── PageTitle.js │ │ ├── Post.js │ │ └── SectionContainer.js │ ├── css/ │ │ ├── prism.css │ │ └── tailwind.css │ ├── getAllPostPreviews.js │ ├── getStaticProps.js │ └── pages/ │ ├── _app.js │ ├── _document.js │ ├── building-react-and-vue-support-for-tailwind-ui/ │ │ └── index.mdx │ ├── building-the-tailwind-blog/ │ │ └── index.mdx │ ├── designing-tailwind-ui-ecommerce/ │ │ └── index.mdx │ ├── from-900-to-1-how-we-hired-robin-malfait/ │ │ └── index.mdx │ ├── headless-ui-unstyled-accessible-ui-components/ │ │ └── index.mdx │ ├── headless-ui-v1/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── headless-ui-v1-4/ │ │ ├── .prettierrc │ │ ├── index.mdx │ │ └── snippets/ │ │ ├── react-1.mdx │ │ ├── react-2.mdx │ │ ├── react-3.mdx │ │ ├── react-4.mdx │ │ ├── vue-1.mdx │ │ ├── vue-2.mdx │ │ ├── vue-3.mdx │ │ └── vue-4.mdx │ ├── heroicons-v1/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── index.js │ ├── introducing-heroicons/ │ │ └── index.mdx │ ├── introducing-linting-for-tailwindcss-intellisense/ │ │ └── index.mdx │ ├── introducing-tailwind-play/ │ │ └── index.mdx │ ├── just-in-time-the-next-generation-of-tailwind-css/ │ │ └── index.mdx │ ├── multi-line-truncation-with-tailwindcss-line-clamp/ │ │ └── index.mdx │ ├── simon-vrachliotis-joins-tailwind-labs/ │ │ └── index.mdx │ ├── tailwind-ui-ecommerce/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── tailwind-ui-now-with-react-and-vue-support/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── tailwindcss-1-5/ │ │ └── index.mdx │ ├── tailwindcss-1-6/ │ │ └── index.mdx │ ├── tailwindcss-1-7/ │ │ └── index.mdx │ ├── tailwindcss-1-8/ │ │ └── index.mdx │ ├── tailwindcss-1-9/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── tailwindcss-2-1/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── tailwindcss-2-2/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── tailwindcss-from-zero-to-production/ │ │ └── index.mdx │ ├── tailwindcss-typography/ │ │ └── index.mdx │ ├── tailwindcss-v2/ │ │ ├── .prettierrc │ │ └── index.mdx │ ├── utility-friendly-transitions-with-tailwindui-react/ │ │ └── index.mdx │ ├── welcoming-brad-cornes-to-the-tailwind-team/ │ │ └── index.mdx │ ├── welcoming-david-luhr-to-tailwind-labs/ │ │ └── index.mdx │ ├── welcoming-james-mcdonald-to-tailwind-labs/ │ │ └── index.mdx │ └── whats-new-in-tailwindcss-on-youtube/ │ └── index.mdx └── tailwind.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env.local .env.development.local .env.test.local .env.production.local ================================================ FILE: README.md ================================================ 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). ## Getting Started First, run the development server: ```bash npm run dev # or yarn dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. ## Learn More To learn more about Next.js, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! ## Deploy on Vercel The 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. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. ================================================ FILE: jsconfig.json ================================================ { "compilerOptions": { "baseUrl": "node_modules", "paths": { "@/*": ["../src/*"] } } } ================================================ FILE: next.config.js ================================================ const { createLoader } = require('simple-functional-loader') const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }) const withSyntaxHighlighting = require('./remark/withSyntaxHighlighting') const withProse = require('./remark/withProse') module.exports = withBundleAnalyzer({ pageExtensions: ['js', 'jsx', 'mdx'], experimental: { modern: true, }, images: { disableStaticImages: true, }, webpack: (config, options) => { config.module.rules.push({ test: /\.(svg|png|jpe?g|gif|mp4)$/i, use: [ { loader: 'file-loader', options: { publicPath: '/_next', name: 'static/media/[name].[hash].[ext]', }, }, ], }) const mdx = [ options.defaultLoaders.babel, { loader: '@mdx-js/loader', options: { remarkPlugins: [withProse, withSyntaxHighlighting], }, }, ] config.module.rules.push({ test: /\.mdx$/, oneOf: [ { test: /snippets/, use: mdx, }, { resourceQuery: /preview/, use: [ ...mdx, createLoader(function (src) { if (src.includes('')) { const [preview] = src.split('') return this.callback(null, preview) } const [preview] = src.split('') return this.callback(null, preview.replace('', '')) }), ], }, { resourceQuery: /rss/, use: mdx, }, { use: [ ...mdx, createLoader(function (src) { const content = [ 'import Post from "@/components/Post"', 'export { getStaticProps } from "@/getStaticProps"', src, 'export default Post', ].join('\n') if (content.includes('')) { return this.callback(null, content.split('').join('\n')) } return this.callback(null, content.replace(/.*/s, '')) }), ], }, ], }) if (!options.dev && options.isServer) { const originalEntry = config.entry config.entry = async () => { const entries = { ...(await originalEntry()) } entries['scripts/build-rss'] = './scripts/build-rss.js' return entries } } return config }, }) ================================================ FILE: package.json ================================================ { "name": "tailwind-blog", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build && next export && npm run build:rss", "start": "next start", "build:rss": "node ./.next/server/scripts/build-rss.js" }, "dependencies": { "@headlessui/react": "^1.4.0", "@mdx-js/loader": "^1.6.22", "@tailwindcss/aspect-ratio": "^0.2.1", "@tailwindcss/typography": "^0.4.0", "autoprefixer": "^10.2.5", "next": "^11.1.2", "postcss": "^8.2.8", "prismjs": "^1.25.0", "react": "^17.0.2", "react-dom": "^17.0.2", "tailwindcss": "^2.2.0-canary.16", "tinytime": "^0.2.6" }, "devDependencies": { "@next/bundle-analyzer": "^10.0.8", "feed": "^4.2.1", "file-loader": "^6.0.0", "simple-functional-loader": "^1.2.1" } } ================================================ FILE: postcss.config.js ================================================ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, } ================================================ FILE: prettier.config.js ================================================ module.exports = { semi: false, singleQuote: true, printWidth: 100, tabWidth: 2, useTabs: false, trailingComma: 'es5', bracketSpacing: true, } ================================================ FILE: public/browserconfig.xml ================================================ #00aba9 ================================================ FILE: public/site.webmanifest ================================================ { "name": "", "short_name": "", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } ================================================ FILE: remark/vendor/prism-diff-highlight.js ================================================ // https://github.com/PrismJS/prism/blob/master/plugins/diff-highlight/prism-diff-highlight.js module.exports = (Prism) => { var LANGUAGE_REGEX = /diff-([\w-]+)/i var HTML_TAG = /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/gi //this will match a line plus the line break while ignoring the line breaks HTML tags may contain. var HTML_LINE = RegExp( /(?:__|[^\r\n<])*(?:\r\n?|\n|(?:__|[^\r\n<])(?![^\r\n]))/.source.replace(/__/g, function () { return HTML_TAG.source }), 'gi' ) var PREFIXES = Prism.languages.diff.PREFIXES Prism.hooks.add('before-sanity-check', function (env) { var lang = env.language if (LANGUAGE_REGEX.test(lang) && !env.grammar) { env.grammar = Prism.languages[lang] = Prism.languages['diff'] } }) Prism.hooks.add('before-tokenize', function (env) { var lang = env.language if (LANGUAGE_REGEX.test(lang) && !Prism.languages[lang]) { Prism.languages[lang] = Prism.languages['diff'] } }) Prism.hooks.add('wrap', function (env) { var diffLanguage, diffGrammar if (env.language !== 'diff') { var langMatch = LANGUAGE_REGEX.exec(env.language) if (!langMatch) { return // not a language specific diff } diffLanguage = langMatch[1] diffGrammar = Prism.languages[diffLanguage] } // one of the diff tokens without any nested tokens if (env.type in PREFIXES) { /** @type {string} */ var content = env.content.replace(HTML_TAG, '') // remove all HTML tags /** @type {string} */ var decoded = content.replace(/</g, '<').replace(/&/g, '&') // remove any one-character prefix var code = decoded.replace(/(^|[\r\n])./g, '$1') // highlight, if possible var highlighted if (diffGrammar) { highlighted = Prism.highlight(code, diffGrammar, diffLanguage) } else { highlighted = Prism.util.encode(code) } // get the HTML source of the prefix token var prefixToken = new Prism.Token('prefix', PREFIXES[env.type], [/\w+/.exec(env.type)[0]]) var prefix = Prism.Token.stringify(prefixToken, env.language) // add prefix var lines = [], m HTML_LINE.lastIndex = 0 while ((m = HTML_LINE.exec(highlighted))) { lines.push(prefix + m[0]) } if (/(?:^|[\r\n]).$/.test(decoded)) { // because both "+a\n+" and "+a\n" will map to "a\n" after the line prefixes are removed lines.push(prefix) } env.content = lines.join('') if (diffGrammar) { env.classes.push('language-' + diffLanguage) } } }) } ================================================ FILE: remark/withProse.js ================================================ const proseComponents = ['Heading'] const isJsNode = (node) => { return ( ['jsx', 'import', 'export'].includes(node.type) && !/^<[a-z]+(>|\s)/.test(node.value) && !new RegExp(`^<(${proseComponents.join('|')})(>|\\s)`).test(node.value) ) } module.exports = function () { return (tree) => { let insideProse = false tree.children = tree.children.flatMap((node, i) => { if (insideProse && isJsNode(node)) { insideProse = false return [{ type: 'jsx', value: '' }, node] } if (!insideProse && !isJsNode(node)) { insideProse = true return [ { type: 'jsx', value: '
' }, node, ...(i === tree.children.length - 1 ? [{ type: 'jsx', value: '
' }] : []), ] } if (i === tree.children.length - 1 && insideProse) { return [node, { type: 'jsx', value: '' }] } return [node] }) } } ================================================ FILE: remark/withSyntaxHighlighting.js ================================================ const visit = require('unist-util-visit') const Prism = require('prismjs') const loadLanguages = require('prismjs/components/') loadLanguages() require('./vendor/prism-diff-highlight')(Prism) function highlightCode(code, prismLanguage) { const isDiff = prismLanguage.startsWith('diff-') const language = isDiff ? prismLanguage.substr(5) : prismLanguage const grammar = Prism.languages[isDiff ? 'diff' : language] if (!grammar) { console.warn(`Unrecognised language: ${prismLanguage}`) return Prism.util.encode(code) } let highlighted = Prism.highlight(code, grammar, prismLanguage) return language === 'html' ? highlighted.replace( /\*\*(.*?)\*\*/g, (_, text) => `${text}` ) : highlighted } module.exports = function withSyntaxHighlighting() { return (tree) => { visit(tree, 'code', (node) => { if (node.lang !== null) { node.type = 'html' node.value = [ `
`,
          ``,
          highlightCode(node.value, node.lang),
          '',
          '
', ] .filter(Boolean) .join('') } }) } } ================================================ FILE: scripts/build-rss.js ================================================ import fs from 'fs' import ReactDOMServer from 'react-dom/server' import { MDXProvider } from '@mdx-js/react' import { Feed } from 'feed' import { getAllPosts } from '../src/getAllPostPreviews' const siteUrl = 'https://blog.tailwindcss.com' const feed = new Feed({ title: 'Tailwind CSS Blog', description: 'All the latest Tailwind CSS news, straight from the team.', id: siteUrl, link: siteUrl, language: 'en', image: `${siteUrl}/favicon-32x32.png`, favicon: `${siteUrl}/favicon.ico`, copyright: `All rights reserved ${new Date().getFullYear()}, Tailwind Labs`, feedLinks: { rss: `${siteUrl}/feed.xml`, json: `${siteUrl}/feed.json`, atom: `${siteUrl}/atom.xml`, }, author: { name: 'Adam Wathan', link: 'https://twitter.com/@adamwathan', }, }) getAllPosts().forEach(({ link, module: { meta, default: Content } }) => { const mdx = ( ) const html = ReactDOMServer.renderToStaticMarkup(mdx) const postText = `

(The post ${ meta.title } appeared first on Tailwind CSS Blog.)

` feed.addItem({ title: meta.title, id: meta.title, link, description: meta.description, content: html + postText, author: meta.authors.map(({ name, twitter }) => ({ name, link: `https://twitter.com/${twitter}`, })), date: new Date(meta.date), image: siteUrl + meta.image, ...(meta.discussion ? { comments: meta.discussion, extensions: [ { name: '_comments', objects: { about: 'Link to discussion forum', comments: meta.discussion, }, }, ], } : {}), }) }) fs.writeFileSync('./out/feed.xml', feed.rss2()) fs.writeFileSync('./out/atom.xml', feed.atom1()) fs.writeFileSync('./out/feed.json', feed.json1()) ================================================ FILE: src/authors.js ================================================ import adamwathanAvatar from './img/adamwathan.jpg' import bradlcAvatar from './img/bradlc.jpg' import steveschogerAvatar from './img/steveschoger.jpg' import robinmalfaitAvatar from './img/robinmalfait.jpg' import simonswissAvatar from './img/simonswiss.jpg' export const adamwathan = { name: 'Adam Wathan', twitter: 'adamwathan', avatar: adamwathanAvatar, } export const bradlc = { name: 'Brad Cornes', twitter: 'bradlc', avatar: bradlcAvatar, } export const steveschoger = { name: 'Steve Schoger', twitter: 'steveschoger', avatar: steveschogerAvatar, } export const robinmalfait = { name: 'Robin Malfait', twitter: 'malfaitrobin', avatar: robinmalfaitAvatar, } export const simonswiss = { name: 'Simon Vrachliotis', twitter: 'simonswiss', avatar: simonswissAvatar, } ================================================ FILE: src/components/Header.js ================================================ import Link from 'next/link' export function TailwindMark({ className }) { return ( ) } export function TailwindLogo({ className }) { return ( ) } export default function Header() { return (
Documentation →
) } ================================================ FILE: src/components/PageTitle.js ================================================ export default function PageTitle({ children }) { return (

{children.replace(/ ([^ ]+)$/, '\u00A0$1')}

) } ================================================ FILE: src/components/Post.js ================================================ import Head from 'next/head' import PageTitle from '@/components/PageTitle' import tinytime from 'tinytime' import Link from 'next/link' import { useRouter } from 'next/router' import { MDXProvider } from '@mdx-js/react' import Header, { TailwindMark } from '@/components/Header' import SectionContainer from '@/components/SectionContainer' import smallCard from '@/img/twitter-card-small.jpg' const postDateTemplate = tinytime('{dddd}, {MMMM} {DD}, {YYYY}') export default function Post({ meta, children, posts }) { const router = useRouter() if (meta.private) { return ( <>
{meta.title} – Tailwind CSS {meta.image ? ( <> ) : ( <> )}
Published on

{meta.title.replace(/ ([^ ]+)$/, '\u00A0$1')}

Authors
    {meta.authors.map((author) => (
  • Name
    {author.name}
  • ))}
{children}
) } const postIndex = posts.findIndex((post) => post.link === router.pathname) const previous = posts[postIndex + 1] const next = posts[postIndex - 1] return ( <>
) } ================================================ FILE: src/components/SectionContainer.js ================================================ export default function SectionContainer({ children }) { return
{children}
} ================================================ FILE: src/css/prism.css ================================================ .language-javascript { color: white; } .token.tag, .token.class-name, .token.selector, .token.selector .class, .token.function { @apply text-fuchsia-400; } .token.attr-name, .token.keyword, .token.rule, .token.operator, .token.pseudo-class, .token.important { @apply text-teal-400; } .token.attr-value, .token.class, .token.string, .token.number, .token.unit, .token.color { @apply text-lime-300; } .token.punctuation, .token.module, .token.property { @apply text-sky-200; } .token.atapply .token:not(.rule):not(.important) { color: inherit; } .language-shell .token:not(.comment) { color: inherit; } .language-css .token.function { color: inherit; } .token.comment { @apply text-gray-400; } .token.deleted:not(.prefix) { @apply relative block -mx-4 px-4; } .token.deleted:not(.prefix)::after { content: ''; @apply pointer-events-none absolute inset-0 block bg-rose-400 bg-opacity-25; } .token.deleted.prefix { @apply text-gray-400 select-none; } .token.inserted:not(.prefix) { @apply block bg-emerald-700 bg-opacity-50 -mx-4 px-4; } .token.inserted.prefix { @apply text-emerald-200 text-opacity-75 select-none; } ================================================ FILE: src/css/tailwind.css ================================================ /* purgecss start ignore */ @tailwind base; /* purgecss end ignore */ @tailwind components; @tailwind utilities; @import './prism.css'; ================================================ FILE: src/getAllPostPreviews.js ================================================ function importAll(r) { return r.keys().map((fileName) => ({ link: fileName.substr(1).replace(/\/index\.mdx$/, ''), module: r(fileName), })) } function dateSortDesc(a, b) { if (a > b) return -1 if (a < b) return 1 return 0 } export default function getAllPostPreviews() { return importAll(require.context('./pages/?preview', true, /\.mdx$/)) .filter((p) => !p.link.includes('/snippets/')) .filter((p) => p.module.meta.private !== true) .sort((a, b) => dateSortDesc(a.module.meta.date, b.module.meta.date)) } export function getAllPosts() { return importAll(require.context('./pages/?rss', true, /\.mdx$/)) .filter((p) => !p.link.includes('/snippets/')) .filter((p) => p.module.meta.private !== true) .sort((a, b) => dateSortDesc(a.module.meta.date, b.module.meta.date)) } ================================================ FILE: src/getStaticProps.js ================================================ import getAllPostPreviews from '@/getAllPostPreviews' export async function getStaticProps() { return { props: { posts: getAllPostPreviews().map((post) => ({ title: post.module.meta.title, link: post.link, })), }, } } ================================================ FILE: src/pages/_app.js ================================================ import '@/css/tailwind.css' import Head from 'next/head' export default function App({ Component, pageProps }) { return (
) } ================================================ FILE: src/pages/_document.js ================================================ import NextDocument, { Html, Head, Main, NextScript } from 'next/document' import * as fs from 'fs' import * as path from 'path' class InlineStylesHead extends Head { getCssLinks(files) { return files.sharedFiles .filter((file) => /\.css$/.test(file)) .filter((file) => fs.existsSync(path.join(process.cwd(), '.next', file))) .map((file) => (